日常练习

在最近刷LeetCode的日常练习中,我遇到了不少看似简单、但要求原地操作且时间复杂度为O(n)的数组/链表问题。苦苦思索后,我发现了“双指针”这个解题利器,并通过一道经典题目真正理解了它的精妙之处。

一、 今日目标:LeetCode 26. 删除排序数组中的重复项

题目要求很简单:给定一个升序排列的数组,你需要原地删除重复出现的元素,使每个元素只出现一次,并返回新数组的长度。不能使用额外的数组空间。

初始困惑:如果允许新数组,我直接用一个Set或者新建一个List就能解决。但“原地”和“O(n)”这两个限制,让问题一下子变得有趣起来。

二、 解题思路演进:从暴力到优雅

  1. 第一反应(错误示范):遇到重复元素,就把它后面的所有元素往前挪一位。这会导致O(n²)的时间复杂度,对于长数组来说太慢了。

  2. 关键洞察:题目说数组是排序的,这意味着所有重复元素必然是连续出现的。这给了我们优化的空间。

  3. 双指针:
    ◦ 慢指针(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]为例,模拟一下:

  1. slow=0 (指向元素0), fast=1 (元素0),相同,fast继续走。
  2. fast=2 (元素1),与slow不同,slow前进到1,将nums[1]赋值为1。数组变为[0,1,1,1,2]。
  3. fast=3 (元素1),与slow(元素1)相同,fast继续。
  4. fast=4 (元素2),与slow(元素1)不同,slow前进到2,将nums[2]赋值为2。数组变为[0,1,2,1,2]。
  5. 循环结束。新数组有效部分为前slow+1=3位:[0,1,2]。

五、 举一反三

掌握了这个“快慢指针”模型后,我发现它能解决一系列问题:
• LeetCode 27. 移除元素:删除所有等于特定值的元素。

• LeetCode 283. 移动零:将所有0移动到数组末尾。

• 链表中判断环、找中点等问题也常用双指针(快慢指针)。

posted @ 2026-02-26 09:25  老汤姆233  阅读(20)  评论(0)    收藏  举报