题目描述
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素。元素的顺序可能发生改变。然后返回 nums
中与 val
不同的元素的数量。
双指针解题方法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for (int fast = 0; fast <nums.size(); fast++){
if(nums[fast]!=val){
nums[slow]=nums[fast];
slow++;
}
}
return slow;
}
};
双指针是非常好用的方法,在数组和链表题目中较常使用。暴力法比较好理解就不做赘述了,这里只记录一下对双指针解法的理解。
fast
指针遍历整个数组,当遇到元素不等于val
时,将该元素复制到slow
指针的位置,并递增slow
指针;最终,slow
指针的值即为移除指定元素后的新数组长度,原数组的前slow
个元素即为有效结果,其余元素可忽略。算法的时间复杂度为O(n),空间复杂度为O(1),使用一个循环解决暴力算法两个循环的工作量。原书目录的讲解见代码随想录
个人理解
起初在写的时候,惯性思维地写了if(nums[fast]==val),认为等于是判断条件,因此在理解上陷入泥潭。就按原书中给的示例数组[0,1,2,3,3,0,4],val=2为例。
代码起初定义慢指针和快指针都在第一个元素上,对于前两个元素,代码执行的逻辑是一样的,先判断快指针元素和目标值是否相同,如果不相同,那么把快指针指向的元素赋值给慢指针,随后慢指针移动,快指针移动。
很好,现在遇到了新的情况,那就是快指针的元素和目标值一样了,我们需要把这个元素用后续的元素覆盖掉,为了让这个逻辑“一步到位”,让快指针发挥它的作用,先走一步,慢指针原地不动。
接着进入下一轮循环,我们发现,再次满足了if(nums[fast]!=val)的条件,这个时候神奇的事情就发发生了:由于之前快慢指针所指的元素是一样的,所以代码中nums[slow]=nums[fast];的功能没有展现,而现在他们指向不同的元素,元素覆盖的操作也出现了效果。
类似地,我们看到现在快指针比慢指针要领先一个身为,且对于后续元素3,0,4,都会满足if(nums[fast]!=val)的条件,那么后续的操作就是元素的逐个覆盖了。到最后一步,slow的自增操作优先于fast的移动,因此会把空缺的一步size补上,此时,返回慢指针的索引也就是返回了移除元素后的数组尺寸。(初始size=7,移除后size=6)
结语
ok没啥难的,是一个很直观的栗子,只是希望再过一次这样的笔记能加深自己的印象~