☰
双指针算法:原地处理有序数组重复项
2026/11/6 2:24:04 网站建设 项目流程

1. 问题背景与核心需求

这道算法题来自经典的数组操作场景,要求我们在原地修改有序数组,使得每个元素最多出现两次。举个例子,给定数组[1,1,1,2,2,3],处理后应该返回长度5,且前五个元素变为[1,1,2,2,3]。这类问题在实际开发中经常遇到,比如日志去重、数据清洗等场景。

与基础版(只保留单一元素)相比,这个变体增加了"允许保留两个重复项"的条件,考验我们对指针移动的精确控制能力。题目要求空间复杂度必须为O(1),这意味着不能使用额外数组,必须原地修改。

2. 双指针解法原理剖析

2.1 快慢指针的协同机制

我们采用快指针(fast)和慢指针(slow)两个标记:

  • 快指针:遍历数组的侦察兵,负责探测新元素
  • 慢指针:重建数组的工程师,决定哪些元素该保留

初始时两个指针都从索引2开始(因为前两个元素无论是否重复都无需处理)。快指针每次循环前进1步,而慢指针只在满足条件时才前进。

2.2 关键比较逻辑

核心判断条件是:nums[fast] != nums[slow-2]。这个精妙的比较实现了:

  • 当当前元素与慢指针前两位不同时,说明当前元素出现次数未超限
  • 反之则表示已经有两个相同元素,应该跳过当前项
def removeDuplicates(nums): if len(nums) <= 2: return len(nums) slow = 2 for fast in range(2, len(nums)): if nums[fast] != nums[slow-2]: nums[slow] = nums[fast] slow += 1 return slow

3. 边界情况与特殊处理

3.1 短数组处理

当数组长度≤2时直接返回原长度,因为:

  • 空数组返回0
  • 单元素数组必然满足条件
  • 两元素数组无论是否相同都符合要求

3.2 全相同元素场景

比如[1,1,1,1,1],算法会正确返回长度2。慢指针会在第三次比较时停止前进,最终保留前两个元素。

4. 复杂度分析与优化空间

4.1 时间复杂度

单次遍历完成操作,时间复杂度为O(n)。这是最优解,因为每个元素至少需要被访问一次。

4.2 空间复杂度

仅使用常数级别的额外空间(几个指针变量),满足O(1)要求。比使用哈希表等方案更节省内存。

5. 同类问题扩展思路

掌握这个解法后,可以轻松应对变种问题:

  • 保留k个重复项:只需将判断条件改为nums[slow-k]
  • 无序数组去重:先排序再使用相同逻辑
  • 链表去重:将数组操作改为链表节点操作

6. 实战中的常见错误

6.1 指针初始位置错误

新手常犯的错误是将双指针都初始化为0。实际上应该从索引2开始,因为前两个元素无论是否重复都无需处理。

6.2 比较对象选择不当

错误的写法如比较nums[fast]和nums[slow],这会漏判某些重复情况。正确的做法是比较nums[fast]和nums[slow-2]。

6.3 返回值混淆

注意题目要求返回的是新长度,而不是修改后的数组。虽然输入的数组会被修改,但函数应该返回的是整数。

7. 测试用例设计建议

完整的测试应该包含:

test_cases = [ ([1,1,1,2,2,3], 5), # 标准情况 ([0,0,1,1,1,1,2,3,3], 7), # 多段重复 ([1,1], 2), # 刚好两个 ([1], 1), # 单个元素 ([], 0), # 空数组 ([1,1,1,1], 2), # 全相同 ([1,2,3,4,5], 5) # 无重复 ]

8. 实际工程应用场景

这种算法在以下场景特别有用:

  1. 时间序列数据压缩:保留足够精度同时减少存储
  2. 日志去重处理:避免完全去重导致信息丢失
  3. 数据库查询优化:预处理结果集减少传输量
  4. 图像处理:去除噪点同时保留细节

在实现这类需求时,我通常会先考虑数据规模。对于小型数组,这个算法已经足够高效。但对于超大型数据(如数GB的日志文件),可能需要结合分块处理技术。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询