日常练习
在最近刷LeetCode的日常练习中,我遇到了不少看似简单、但要求原地操作且时间复杂度为O(n)的数组/链表问题。苦苦思索后,我发现了“双指针”这个解题利器,并通过一道经典题目真正理解了它的精妙之处。
一、 今日目标:LeetCode 26. 删除排序数组中的重复项
题目要求很简单:给定一个升序排列的数组,你需要原地删除重复出现的元素,使每个元素只出现一次,并返回新数组的长度。不能使用额外的数组空间。
初始困惑:如果允许新数组,我直接用一个Set或者新建一个List就能解决。但“原地”和“O(n)”这两个限制,让问题一下子变得有趣起来。
二、 解题思路演进:从暴力到优雅
-
第一反应(错误示范):遇到重复元素,就把它后面的所有元素往前挪一位。这会导致O(n²)的时间复杂度,对于长数组来说太慢了。
-
关键洞察:题目说数组是排序的,这意味着所有重复元素必然是连续出现的。这给了我们优化的空间。
-
双指针:
◦ 慢指针(slow):指向下一个非重复元素应该放置的位置。◦ 快指针(fast):负责向前探索,寻找新的、不同于慢指针指向的元素。
三、 代码实现
def removeDuplicates(nums):
if not nums:
return 0
slow = 0 # 慢指针从索引0开始
for fast in range(1, len(nums)): # 快指针从索引1开始遍历
if nums[fast] != nums[slow]:
slow += 1 # 慢指针前进一步
nums[slow] = nums[fast] # 将新的不重复元素赋值到慢指针位置
# 新数组的长度为慢指针索引 + 1
return slow + 1
# 测试
arr = [0,0,1,1,1,2,2,3,3,4]
new_len = removeDuplicates(arr)
print(f"新长度: {new_len}, 处理后前{new_len}位: {arr[:new_len]}")
# 输出:新长度: 5, 处理后前5位: [0, 1, 2, 3, 4]
四、 过程模拟
以[0,0,1,1,2]为例,模拟一下:
- slow=0 (指向元素0), fast=1 (元素0),相同,fast继续走。
- fast=2 (元素1),与slow不同,slow前进到1,将nums[1]赋值为1。数组变为[0,1,1,1,2]。
- fast=3 (元素1),与slow(元素1)相同,fast继续。
- fast=4 (元素2),与slow(元素1)不同,slow前进到2,将nums[2]赋值为2。数组变为[0,1,2,1,2]。
- 循环结束。新数组有效部分为前slow+1=3位:[0,1,2]。
五、 举一反三
掌握了这个“快慢指针”模型后,我发现它能解决一系列问题:
• LeetCode 27. 移除元素:删除所有等于特定值的元素。
• LeetCode 283. 移动零:将所有0移动到数组末尾。
• 链表中判断环、找中点等问题也常用双指针(快慢指针)。

浙公网安备 33010602011771号