双指针的一个应用
本文最后更新于:2024年4月22日 晚上
Leetcode上的一个双指针题的解法与简化
题目:80. 删除有序数组中的重复项 II
题目链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/description/
给你一个有序数组nums
,请你**原地** 删除重复出现的元素,使得出现次数超过两次的元素只出现两次,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
题解
读完题目之后,我们应该注意到:nums数组是有序的!这意味着:相同元素必然连续。
我们可以设置快慢两个指针:fast
和slow
,运用for循环进行遍历,判断哪些元素应该保留,哪些应该剔除。
具体而言,slow
指针表示处理出的数组的长度,fast指针表示已经检查过的数组的长度。即nums[fast]
表示待检查的第一个元素,nums[slow − 1]
为上一个应该被保留的元素所移动到的指定位置。
因为本题要求相同元素最多出现两次,所以我们需要检查上上个应该被保留的元素nums[slow − 2]
是否和当前待检查元素nums[fast]
相同。当且仅当nums[slow − 2] == nums[fast]
时,当前待检查元素nums[fast]
不应该被保留(因为此时必然有nums[slow − 2] == nums[slow − 1] == nums[fast]
)。
最后,slow即为处理好的数组的长度。
特别地,数组的前两个数必然可以被保留,因此对于长度不超过2的数组,我们无需进行任何处理,对于长度超过2的数组,我们直接将双指针的初始值设为2即可。
于是便有了这样的解答:
1 |
|
优化
基于上面显式的双指针法,我们可以进一步抽象简化。
首先应该考虑,我们使用双指针的目的是什么?其实是方便指向我们要使用的数字。所以指针对应的数值才是主角。
在下面的题解中,我们应该尽量少使用指针,让数值自己出来比较:
联想到C++的for循环语法:for (int num : nums)
,可以让num自身当右指针;
可以再令一个i代表左指针。
结合上面的思想,我们可以得到下面的代码:
1 |
|
扩展
如果每个元素最多出现K次,你要怎么修改上面的代码?
很简单,改成if (i < k || nums[i - k] < num)
就好了!
(这能不能看作这类问题的通解呢?)