题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。
示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
你不需要考虑数组中超出新长度后面的元素。
思路:
只能说有时候官方题解还是不错的,理解了这道题的双指针的含义。
有两个指针,左指针表示的是待赋值的数,而右指针代表的是要处理的数(赋值给别人的数)。
右指针从左向右遍历。
如果右指针不等于val,那么就说明该数可以赋值给需要被赋值的数,所以就赋值,然后右指针和左指针都加1.
如果右指针不等val,就说明此时的数不可以赋值需要被赋值的数(左指针),那么右指针就得加1,去寻找下一个数。
所以代码就是如此:(参考力扣题解写出来)
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
for (int right = 0; right < nums.length; right++) {
if (nums[right] != val) {
nums[left] = nums[right];
left++;
}
}
return left;
}
}
要学习这种写代码的思路,右指针无论(左指针是否等于val,它都要++),所以右指针可以作为for循环的部分,每次++,而左指针只是满足条件才++,就写在判断条件里。
总结
所以这道题重要的是,找到右指针和左指针分别代表什么。
根据题解想出左指针是被赋值,右指针是赋值的数,这点挺重要也挺难想的,只能说多做题吧。
题做百遍,书读百遍,其义自现。
补充一下力扣题解的优化解法
思路
如果要移除的元素恰好在数组的开头,例如序列 [1,2,3,4,5][1,2,3,4,5],当 \textit{val}val 为 11 时,我们需要把每一个元素都左移一位。注意到题目中说:「元素的顺序可以改变」。实际上我们可以直接将最后一个元素 55 移动到序列开头,取代元素 11,得到序列 [5,2,3,4][5,2,3,4],同样满足题目要求。这个优化在序列中 \textit{val}val 元素的数量较少时非常有效。
实现方面,我们依然使用双指针,两个指针初始时分别位于数组的首尾,向中间移动遍历该序列。
算法
如果要移除的元素恰好在数组的开头,例如序列 [1,2,3,4,5],当 \textit{val}val 为 11 时,我们需要把每一个元素都左移一位。注意到题目中说:「元素的顺序可以改变」。实际上我们可以直接将最后一个元素 55 移动到序列开头,取代元素 11,得到序列 [5,2,3,4],同样满足题目要求。这个优化在序列中 \textit{val}val 元素的数量较少时非常有效。
实现方面,我们依然使用双指针,两个指针初始时分别位于数组的首尾,向中间移动遍历该序列。
如果左指针 left 指向的元素等于val,此时将右指针right 指向的元素复制到左指针 left 的位置,然后右指针 right 左移一位。如果赋值过来的元素恰好也等于 val,可以继续把右指针 right 指向的元素的值赋值过来(左指针left 指向的等于 val 的元素的位置继续被覆盖),直到左指针指向的元素的值不等于 val 为止。
当左指针left 和右指针right 重合的时候,左右指针遍历完数组中所有的元素。
这样的方法两个指针在最坏的情况下合起来只遍历了数组一次。与方法一不同的是,方法二避免了需要保留的元素的重复赋值操作。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-element/solution/yi-chu-yuan-su-by-leetcode-solution-svxi/
来源:力扣(LeetCode)
代码:
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length;//从length开始,下面就是-1
while (left < right) {//一般判断条件都是left<right,很少用等于
if (nums[left] == val) {//想说的是这里,当nums[left]和 val相等的时候,
//right--是显而易见的,然后我们同时很容易想去把left也++,但是没必要,因为这里不做操作
//下一个判断的时候自然会去进行left++的操作
nums[left] = nums[right - 1];
right--;
} else {
left++;
}
}
return left;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-element/solution/yi-chu-yuan-su-by-leetcode-solution-svxi/
来源:力扣(LeetCode)