leetcode【数据结构简介】《数组和字符串》卡片——小结
Authur Whywait 做一块努力吸收知识的海绵
想看博主的其他所有leetcode卡片学习笔记链接?传送门点这儿文章目录
2. 杨辉三角II 3. 翻转字符串里的单词 4. 反转字符串中的单词III 5. 删除排序数组中的重复项 6. 移动零
《数组和字符串》卡片- 小结
1. 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
暴力法
void rotate(int* nums, int numsSize, int k){ if(numsSize<1 || k<1 || k%numsSize==0) return; for(int i=0; i<k%numsSize; i++){ int temp = nums[numsSize-1]; for(int j=numsSize-1; j>0; j--) nums[j] = nums[j-1]; nums[0] = temp; } return; }
执行结果:
反转
/*反转函数*/ void reverse(int* nums, int numsSize, int a, int b){ int low=a, high=b; while(low<high){ int temp=nums[low]; nums[low] = nums[high]; nums[high] = temp; high--; low ++; } return; } /*rotate函数主体*/ void rotate(int* nums, int numsSize, int k){ if(numsSize<1 || k<1 || k%numsSize==0) return; int k0=k%numsSize; reverse(nums, numsSize, 0, numsSize-1); reverse(nums, numsSize, 0, k0-1); reverse(nums, numsSize, k0, numsSize-1); return; }
利用最大公约数
使用环状替代
使用辅助数组
O(n)算法,使用辅助数组,不符合题目空间复杂度O(1)的算法要求(虽然官方也给出了这种算法)
2. 杨辉三角II
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
输入: 3
输出: [1,3,3,1]
普通解法
int* getRow(int rowIndex, int* returnSize){ * returnSize = rowIndex + 1; int** tri = (int **) malloc (sizeof(int*) * (rowIndex+1)); for(int i=0; i<rowIndex+1; i++){ tri[i] = (int *) malloc (sizeof(int) * (i+1)); tri[i][0] = 1; tri[i][i] = 1; } for(int i=2; i<rowIndex+1; i++){ for(int j=1; j<i; j++){ tri[i][j] = tri[i-1][j-1] + tri[i-1][j]; } } return tri[rowIndex]; }
优化解法
降低解法的空间复杂度
只申请一行的空间,从后往前操作就不需要考虑被覆盖的问题。
int* getRow(int rowIndex, int* returnSize){ * returnSize = rowIndex + 1; int* array = (int *)malloc(sizeof(int) * (rowIndex+1)); for(int i=0; i<rowIndex+1; i++){ array[i]=1; for(int j=i-1; j>0; j--) array[j] = array[j] + array[j-1]; array[0] = 1; } return array; }
3. 翻转字符串里的单词
给定一个字符串,逐个翻转字符串中的每个单词。
输入: “the sky is blue”
输出: “blue is sky the”
输入: " hello world! "
输出: “world! hello”
解释:输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
输入: “a good example”
输出: “example good a”
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
法一:辅助(返回)字符串+反转
思路:
1.除了多余空格,其他字符依次转移到辅助字符串里。
2.反转整个字符串,再把单词依次反转。
法二:反转-额外空间复杂度O(1)
思路:
1.遍历字符串,删去多余空格,同时反转单词(使用双指针head,tail)。
2.最后反转整个字符串。
代码:
void reverse(char * s, int a, int b){ while(a<b){ char temp=s[a]; s[a++] = s[b]; s[b--] = temp; } return; }
char * reverseWords(char * s){ int len=strlen(s), head=0, tail=0; /*删除前面多余的空格*/ while(s[head]==' ') head++; for(int i=0; i<len-head; i++) s[i] = s[i+head]; s[len-head] = '\0'; len -= head;head = 0; int len1 = len;bool flag; if(!len1) return ""; /*删除中间多余的空格并反转单词(如果最后有空格的话仍有一个空格未处理)*/ for(int i=0; i<len+1; i++){ printf("%d",i); if(i>len1) break; if(i==len1 && flag){//如果字符串末尾没有空格的情况 tail=i-1; reverse(s, head, tail); } else if(s[i]!=' ' && s[i]!='\0'){ if(!flag) head=i; //若为不为空格且前面一个是空格:确定head flag=true; } else if(s[i]==' '){ if(flag){ tail=i-1; //若为空格且前面一个不为空格:确定tail reverse(s, head, tail); flag = false; } else if(!flag){ //若为空格且前面一个也为空格,删除此空格且i-- len1--; for(int j=i; j<len1; j++) s[j] = s[j+1]; s[len1] = '\0'; i--; }else; }else; } /*若有空格,处理字符串末尾空格*/ if(!flag){ len1--; s[len1] = '\0'; } /*反转整个字符串*/ reverse(s, 0, len1-1); return s; }
细心的读者就会发现了,为什么有一行输出
printf("%d",i);呢?因为是直接在leetcode上调试,所以要输出一些东西便于调试。结果在删除的时候遇到了问题,删除这个printf会导致程序的执行结果发生变化,真的是非常amazing了。
最后就把带着printf("%d",i);的代码提交了~
执行结果:
执行时间长确是一个硬伤。
4. 反转字符串中的单词III
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
输入: “Let’s take LeetCode contest”
输出: “s’teL ekat edoCteeL tsetnoc”
【思路】
和翻转字符串里的单词相比就很简单了,思路相同,故直接上代码:
void reverse(char * s, int a, int b){ while(a<b){ char temp=s[a]; s[a++] = s[b]; s[b--] = temp; } return; }
char * reverseWords(char * s){ int len=strlen(s), head=0, tail; if(!len) return ""; bool flag=true; for(int i=0; i<len; i++){ if(s[i]==' '){ if(flag){ tail = i-1; reverse(s, head, tail); } flag = false; } else if(s[i]!=' '){ if(!flag) head = i; flag = true; } } reverse(s, head, len-1); return s; }
5. 删除排序数组中的重复项
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
法一:暴力双指针法
思路:遇到重复,所有后面的元素往前移一个
int removeDuplicates(int* nums, int numsSize){ int len=numsSize; for(int i=1; i<len; i++){ if(nums[i]==nums[i-1]){ len--; for(int j=i; j<len; j++) nums[j] = nums[j+1]; i--; } } return len; }
法二:优化(快慢指针法)
法一中的遇到重复就把后面全体往前移一个位置,但是如果诸如
011111111111112之类的呢?就会多很多无谓的操作。
于是,快慢指针法就应运而生。
int removeDuplicates(int* nums, int numsSize){ if(!numsSize) return 0; int slow=1, quick=1; while(quick<numsSize){ if(nums[quick]!=nums[quick-1]) nums[slow++] = nums[quick++]; else quick++; } return slow; }
很明显,不管是在执行用时还是在内存消耗方面,快慢指针法都远远优于方法一。
6. 移动零
给定一个数组
nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
快慢指针法
直接用最优的快慢指针法
void moveZeroes(int* nums, int numsSize){ int num=0; int slow=0, quick=0; while(quick<numsSize){ if(nums[quick]) nums[slow++] = nums[quick++]; else{ quick++; num++; } } for(int i=numsSize-num; i<numsSize; i++) nums[i]=0; return; }
- 点赞
- 收藏
- 分享
- 文章举报
- leetcode【数据结构简介】《链表》卡片——小结
- leetcode【数据结构简介】《二叉搜索树》卡片 - 小结
- leetcode【数据结构简介】《队列&栈》卡片 - 小结
- leetcode【数据结构简介】《队列&栈》卡片 - 栈和深度优先搜索
- leetcode【数据结构简介】《队列&栈》卡片——队列和广度优先搜索
- leetcode【数据结构简介】《队列&栈》卡片——队列:先入先出的数据结构
- leetcode【数据结构简介】《N叉树》卡片 -递归
- leetcode【数据结构简介】《N叉树》卡片 - 遍历
- leetcode【数据结构简介】《二叉搜索树》卡片 - 附录:高度平衡的二叉搜索树
- leetcode【数据结构简介】《链表》卡片——双链表
- leetcode【数据结构简介】《链表》卡片——经典问题
- leetcode【数据结构简介】《链表》卡片——双指针技巧
- leetcode【数据结构简介】《二叉搜索树》卡片——二叉搜索树中的基本操作
- leetcode【数据结构简介】《链表》卡片——单链表
- leetcode【数据结构简介】《二叉搜索树》卡片——二叉搜索树简介
- leetcode【数据结构简介】《二叉树》卡片——总结
- leetcode【数据结构简介】《二叉树》卡片——运用递归解决问题
- leetcode【数据结构简介】《二叉树》卡片——树的遍历
- 数据结构(一)——数据结构简介
- 关于java开发、网络爬虫、自然语言处理、数据挖掘简介与关系小结