在计算机科学中,一个原地算法(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] <= 104nums已按 升序 排列
在把题目认真过了几遍后,提炼出信息如下
- 有序数组
- 不能使用额外的空间
- 原地
- 不是很理解,不需要额外辅助的数据接口,我假定是指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;
这两天刷了下算法,有些地方能在心里把代码过一遍,总结下来能很好的锻炼代码逻辑,虽然现在还很生涩,最近看到的一句话,储备了相当的背景知识,后面才不会那么累,对后续代码编写有很好帮助