【LeetCode算法题】删除有效数组中的重复项

152 阅读2分钟

在计算机科学中,一个原地算法(in-place algorithm)基本上不需要额外辅助的数据结构,然而,允许少量额外的辅助变量来转换数据的算法。当算法运行时,输入的数据通常会被要输出的部分覆盖掉。不是原地算法有时候称为非原地(not-in-place)或不得其所(out-of-place)。–摘自维基百科。 在计算复杂性理论中,原地算法包含使用O(1)空间复杂度的所有算法,DSPACE(1)类型。

升序排列的数组nums原地删除重复出现的元素,是每个元素值出现一次,返回删除后数组的新长度,元素的相对顺序应该保持一致。 由于某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有k个元素,那么nums的前k个元素应该保存最终结果,将最终结果插入nums的前k个位置返回k 系统会用下面的代码来测试你的题解:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

提示:

  • 0 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按 升序 排列

在把题目认真过了几遍后,提炼出信息如下

  • 有序数组
  • 不能使用额外的空间
  • 原地
  • 不是很理解,不需要额外辅助的数据接口,我假定是指Map,Set内的,java8 Stream 自带去重估计也在排除范围内,
  • 一些语言不能修改数组长度,可以只保证前排数据有效

方案一: 直接使用了java8的去重,编译通过,但是在leetcode输出结果不符合,这个不是很懂,比如【1,1,2】 结果变成了【1,1】 但是在idea上编译结果是对的

方案二: 用了个很傻的方法,大致思路类似冒泡,双重循环,每次取出一个数都和数组后面的数字进行比较 ,比如1 1 2 2 3 4 5 6 将第一个1和后面的1 2 2 3 4 5 6 比较一轮,一旦返现相同,将后面的前移,最后一位空出来设置-1,无序的数据这个方法应该是可以

for(int i = 0 ; i < nums.length -1; i ++){
    for(int k = i +1; k < nums.length;){
         if(nums[i] == nums[k]){
             //  将后置前移 并将空出的位置设-1
           1  1  2  2  3  4  5 6
           1  2  2  3  4  5  6 -1
         }else {
            k ++;
         }
    }
}

后面多读了几遍发现是有序的,修改后如下

for(int i = 0 ; i < nums.length -1; ){
   if(nums[i] == nums[i+1]){
             //  将后置前移 并将空出的位置设-1
           1  1  2  2  3  4  5 6
           1  2  2  3  4  5  6 -1
         }else {
            i ++;
        }
   }
}

总结 没有认真领悟题意有序,移动位置后还需要矫下标,带来代码的复杂,真正的好的解法往往最简单

看了下官方题解,采用双指针

  • 第一位不需要变更,从第二位起,将快慢指针指向都指向第二位
  • 快指针与上1位进行比较不一致将快指针数据替换给慢指针
    比如1 1 2 2 3 4 5 6 ,快指针和慢指针开始指向第二位,第二个1,一致,所以不做变更
  • 快指针继续指向下一位的2,2和1不一致,将快指针指向的3替换给慢指指针指向的第1位的1
    这时结果1 2 2 2 3 4 5 6
  • 并将慢真针下移到第三位,第二个2的位置,快指针移动到第4位的2
  • 快指针继续和上一位2比较一致,快指针继续后移
  • 以此类推
int n = numbs.length;
if(n <= 1){
    return numbs.length;
}
int fast =1,slow = 1;
while(fast < n){
    if(nums[fast] == nums[fast-1]){
        nums[slow] = nums[fast];
       ++slow ;
    }
    ++fast;
}
return slow;

这两天刷了下算法,有些地方能在心里把代码过一遍,总结下来能很好的锻炼代码逻辑,虽然现在还很生涩,最近看到的一句话,储备了相当的背景知识,后面才不会那么累,对后续代码编写有很好帮助