题目
题目26
- 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
- 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例:
给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
题目27
- 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
- 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
- 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
刚开始看完题目,不知道应该怎样去解决,为什么不创建新数组就可以“原地”删除元素,在参看力扣优秀解法之后,其实只要返回的新长度的范围内符合即可。
双指针法
题26解法
首先题目是已经排序后的数组,在不考虑数组中超出新长度后面的元素的前提下,可以分别定义快慢指针(i是慢指针,j是快指针),如果重复(nums[i]==nums[j]
的情况),直接跳过,将重复的一一向前覆盖(nums[++i] = nums[j];
),最终返回新长度即可。
/**
* 删除数组中重复的项,并返回数组的长度
* @param nums 传入已排序的数组
* @return 返回无重复项数组的长度
*/
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
//定义慢指针
int i = 0;
//定义快指针
for (int j = 1; j < nums.length; j++) {
//相等情况直接跳过
//不相等时,将nums[j]的值赋值给nums[i+1],然后递增i
if (nums[j] != nums[i]) {
nums[++i] = nums[j];
}
}
//返回无重复项数组的新长度
return i + 1;
}
题27解法一
类似地,也可以运用双指针法求解。还是定义快慢两个指针,只要快指针索引值和目标值不相等,即nums[j]!=val
,就可以将该索引值赋值给前面的慢指针索引值,即nums[i]=nums[j]
,两个指针同时递增,直到快指针截至,最终的新长度就是慢指针i的值。
/**
* 删除所有重复的val值,并返回数组的长度
* @param nums 传入的数组
* @param val 删除数组中的所有val
* @return 返回无重复数组的长度
*/
public int removeDuplicates(int[] nums, int val) {
int i = 0;
for (int j = 0; j < nums.length; j++) {
if (nums[j] != val) {
nums[i++] = nums[j];
}
}
return i;
}
复杂度分析
- 时间复杂度:O(n) 假设数组总共有n个元素,i和j分别最多遍历n步
- 空间复杂度:O(1)
题27解法二
题27的解法一很暴力,但是在遇到特殊情况时其实进行了多余的步骤:假如数组包含需要删除的数很少的时候,nums=[4,1,2,3,5],val=4
时,题目中特意提示可以不用考虑新数组的元素顺序,可以在遇到nums[i]==val
时,将当前元素和最后一个元素交换,并释放数组的最后一个元素。
如果被交换的最后一个元素正好也是想要删除的值,他会在下一次迭代时被发现。
public int removeDuplicates00(int[] nums, int val) {
int i = 0;
//定义变量len操作数组长度,因为本身是final的
int len = nums.length;
while (i < len) {
if (nums[i] == val) {
//相等的情况,将第一个与最后一个交换,
// 因为数组顺序可以更改,[4,1,2,3,5],val=4,
//可以避免不必要的移位
nums[i] = nums[len - 1];
len--;
} else {
i++;
}
}
return len;
}
复杂度分析
- 时间复杂度:O(n),i和n最多遍历n步,且赋值操作的次数等于将要删除元素的数量,如果要移除的元素很少,效率会很高。
- 空间复杂度:O(1)