双指针思想
当我们在访问线性的数据结构时,往往可以引用两个指针来访问,再根据所给条件来对这两个指针进行对比,移动。在今天的题目中,是以数组为数据结构,我们都知道,数组可以利用下标来进行访问指定元素。所以,我们在数组的双指针就可以利用非指针变量(一般是整型变量)来当作下标,进而当作数组中的指针。
力扣:26
题目:
实例:
思路:我们首先需要遍历这个数组,创建一个新的数组来接收符合条件的元素;接着我们要想怎么给出条件,我们根据所给要求,由于是有序数组,可以判断新数组最后一个元素是否小于当前下标访问的元素,如果是,那么就将它存进新数组中。
int removeDuplicates(int* nums, int numsSize){ //创建一个数组接收重组内容 int new[10000]; //新数组位移下标 int cur=0; //先对首元素赋值 new[0]=nums[0]; //统计长度 int sum=1; //遍历找出符合条件 for(int i=1;i<numsSize;i++) { if(nums[i]>new[cur]) { new[++cur]=nums[i]; sum++; } } for(int i=1;i<sum;i++) { nums[i]=new[i]; } return sum; }
我们这么做,会发现我们利用空间比较多(空间复杂度O(n))。那能不能在节省一些空间呢?答案是肯定的。我们先对上面进行初步优化,我们会发现其实利用下标cur即可表现new数组的长度,只需最后+1就能表示新数组的长度,所以sum变量可以省去。上面放入新数组的条件大于改为不等于,因为实际上就是新数组最后一个元素与当前访问数组不同时,就改变新数组。相同时,那么nums数组继续向前遍历。
由于题目给出条件,不需要考虑数组中超出新长度后面的元素,其实我们就可以在原数组进行这些步骤,即将这两个数组合二为一。我们的其它思路保持不变,这时我们对nums[]创建两个指针,初始化都为1,一个为fast,一个为slow,fast表示访问的下标,对应上面的i,slow表示返回的长度,对应上面的cur+1(也是sum),也表示新数组下一个元素该填的位置;那么nums[slow-1]就表示新数组最后一个元素。这样思路就完成了。当然,还需要考虑一下特殊条件,倘若长度为0,那就返回0即可。长度为1时,我们只需要返回长度即可。那么,
代码如下:
int removeDuplicates(int* nums, int numsSize){ if(numsSize<=1) { return numsSize; } int fast=1,slow=1; //fast表示访问的下标,slow表示返回的长度,也表示新数组下一个元素该填的位置 while(fast<numsSize) { //nums[slow-1]表示新数组最后一个元素 //当它与nums[fast]不同,则nums[slow]需要替换 if(nums[slow-1]!=nums[fast]) { nums[slow]=nums[fast]; slow++; } fast++; } return slow; }
力扣:80
思路:这道题与26题相比,会发现改变条件是出现次数超过两次的元素只有两次;我们的思路先保持与上面一致,上面思路是新数组的最后一个元素与当前访问的新元素比较;在这里我们要改变条件是最后一个元素改为倒数第二个元素。
所以,我们是将nums[slow-2]与nums[fast]进行比较,对于slow-1位置上的元素,在这个例子中,可能性只有是1或者2,如果是1,表明新数组已有两个相同元素,slow位置存的是不相同的;如果是2,表明slow存的是第二个2;综上,slow-1位置不会影响检索条件。上面给出新数组是假设性的,实际题目要求空间复杂度为O(1)。接着对初始化条件修改为新数组有两个元素了,再加上特殊条件即可。
int removeDuplicates(int* nums, int numsSize){ //当数组长度小于等于2时直接返回 if(numsSize<=2) { return numsSize; } int slow=2,fast=2; while(fast<numsSize) { if(nums[slow-2]!=nums[fast]) { nums[slow]=nums[fast]; slow++; } fast++; } return slow; }