☰
双指针算法实现有序数组去重与LeetCode 26题解析
2026/10/31 16:14:27 网站建设 项目流程

1. 问题背景与需求分析

在算法面试和日常编程中,处理有序数组的去重操作是一个经典问题。LeetCode第26题"删除有序数组中的重复项"要求我们原地修改输入数组,使得每个元素只出现一次,并返回新的长度。

这个问题看似简单,但考察了几个关键编程能力:

  • 对数组数据结构的理解
  • 双指针算法的应用
  • 原地修改数组的技巧
  • 边界条件的处理能力

实际业务场景中,类似操作也很常见。比如:

  • 处理日志数据时去除连续重复条目
  • 数据库查询结果去重
  • 时间序列数据的清洗

2. 问题描述与示例

给定一个非严格递增排列的数组nums,原地删除重复出现的元素,使每个元素只出现一次。返回删除后数组的新长度。

要求:

  • 必须原地修改输入数组
  • 使用O(1)额外空间
  • 元素的相对顺序必须保持一致

示例1: 输入:nums = [1,1,2] 输出:2, nums = [1,2,_] 解释:函数应返回长度2,原数组前两个元素修改为1和2

示例2: 输入:nums = [0,0,1,1,1,2,2,3,3,4] 输出:5, nums = [0,1,2,3,4,,,,,_]

3. 双指针解法详解

3.1 基本思路

最直观的解法是使用双指针技术:

  1. 慢指针(slow)指向当前唯一元素的最后一个位置
  2. 快指针(fast)遍历数组寻找下一个不重复元素
  3. 当找到不重复元素时,将其复制到slow+1位置,然后slow前进

这种方法的优势在于:

  • 时间复杂度O(n),只需遍历一次数组
  • 空间复杂度O(1),只使用了常数个额外变量
  • 保持了元素的原始顺序

3.2 代码实现

def removeDuplicates(nums): if not nums: return 0 slow = 0 for fast in range(1, len(nums)): if nums[fast] != nums[slow]: slow += 1 nums[slow] = nums[fast] return slow + 1

3.3 执行过程分析

以nums = [0,0,1,1,1,2,2,3,3,4]为例:

初始状态: slow=0, fast=1 nums[1]=0 == nums[0]=0 → 不操作

fast=2: nums[2]=1 != nums[0]=0 → slow=1, nums[1]=1 数组变为[0,1,1,1,1,2,2,3,3,4]

fast=3: nums[3]=1 == nums[1]=1 → 不操作

fast=4: nums[4]=1 == nums[1]=1 → 不操作

fast=5: nums[5]=2 != nums[1]=1 → slow=2, nums[2]=2 数组变为[0,1,2,1,1,2,2,3,3,4]

fast=6: nums[6]=2 == nums[2]=2 → 不操作

fast=7: nums[7]=3 != nums[2]=2 → slow=3, nums[3]=3 数组变为[0,1,2,3,1,2,2,3,3,4]

fast=8: nums[8]=3 == nums[3]=3 → 不操作

fast=9: nums[9]=4 != nums[3]=3 → slow=4, nums[4]=4 最终数组:[0,1,2,3,4,2,2,3,3,4] 返回长度:5

4. 边界条件与特殊情况处理

4.1 空数组处理

当输入数组为空时,应直接返回0。这是最容易忽略的边界情况。

if not nums: return 0

4.2 单元素数组

数组只有一个元素时,无需任何操作,直接返回1。

4.3 全相同元素数组

如nums = [1,1,1,1],应返回1,数组变为[1,,,_]

4.4 已去重数组

如nums = [1,2,3,4],应返回4,数组保持不变

5. 算法复杂度分析

时间复杂度:O(n)

  • 只需一次遍历数组,每个元素最多被访问一次

空间复杂度:O(1)

  • 只使用了slow和fast两个额外变量

6. 变种问题与扩展

6.1 允许最多保留k个重复项

LeetCode第80题是此问题的扩展,要求每个元素最多出现两次。通用解法:

def removeDuplicates(nums, k=2): if len(nums) <= k: return len(nums) slow = k for fast in range(k, len(nums)): if nums[fast] != nums[slow - k]: nums[slow] = nums[fast] slow += 1 return slow

6.2 无序数组去重

如果数组无序,可以使用哈希表记录已出现元素:

def removeDuplicates(nums): seen = set() slow = 0 for fast in range(len(nums)): if nums[fast] not in seen: seen.add(nums[fast]) nums[slow] = nums[fast] slow += 1 return slow

但这种方法空间复杂度为O(n),不符合本题要求。

6.3 返回新数组而非修改原数组

如果允许返回新数组,可以直接使用集合:

def removeDuplicates(nums): return list(set(nums))

但会丢失元素顺序,且空间复杂度为O(n)

7. 实际应用场景

7.1 数据库查询结果去重

SELECT DISTINCT column FROM table

在内存中处理时,可能需要类似的去重算法

7.2 日志处理

去除连续的重复日志条目,减少存储空间

7.3 时间序列数据清洗

去除传感器采集的重复数据点

8. 常见错误与调试技巧

8.1 数组越界错误

忘记检查数组是否为空,直接访问nums[0]会导致错误

8.2 逻辑错误

错误地将slow指针初始化为1,导致第一个元素被跳过

8.3 测试用例建议

应测试以下情况:

  • 空数组
  • 单元素数组
  • 全相同元素数组
  • 已去重数组
  • 常规有重复数组

8.4 调试技巧

可以在循环中加入打印语句,观察指针移动和数组变化:

print(f"slow={slow}, fast={fast}, nums={nums}")

9. 性能优化与语言特性

9.1 Python中的优化

在Python中,列表操作可能较慢。对于大型数组,可以考虑使用numpy数组:

import numpy as np def removeDuplicates(nums): nums = np.array(nums) if len(nums) == 0: return 0 slow = 0 for fast in range(1, len(nums)): if nums[fast] != nums[slow]: slow += 1 nums[slow] = nums[fast] return slow + 1

9.2 C++实现

int removeDuplicates(vector<int>& nums) { if (nums.empty()) return 0; int slow = 0; for (int fast = 1; fast < nums.size(); ++fast) { if (nums[fast] != nums[slow]) { nums[++slow] = nums[fast]; } } return slow + 1; }

9.3 Java实现

public int removeDuplicates(int[] nums) { if (nums.length == 0) return 0; int slow = 0; for (int fast = 1; fast < nums.length; fast++) { if (nums[fast] != nums[slow]) { nums[++slow] = nums[fast]; } } return slow + 1; }

10. 面试技巧与注意事项

10.1 面试官可能问的问题

  • 为什么选择双指针解法?
  • 如何处理边界条件?
  • 时间复杂度和空间复杂度是多少?
  • 如何测试你的代码?
  • 如果数组很大,如何优化?

10.2 回答技巧

  • 先明确问题要求和约束条件
  • 解释算法思路,画图说明指针移动
  • 主动讨论边界条件和测试用例
  • 分析复杂度,讨论优化可能性

10.3 代码风格建议

  • 添加有意义的变量名(slow/fast比i/j更好)
  • 添加必要的注释
  • 先处理边界条件
  • 保持代码简洁但可读

11. 相关题目推荐

  1. LeetCode 27 - 移除元素
  2. LeetCode 80 - 删除有序数组中的重复项 II
  3. LeetCode 283 - 移动零
  4. LeetCode 844 - 比较含退格的字符串
  5. LeetCode 977 - 有序数组的平方

12. 学习资源与进阶

  1. 《算法导论》 - 基础算法部分
  2. 《编程珠玑》 - 数组处理技巧
  3. LeetCode探索卡片 - 数组与字符串
  4. 双指针算法专题
  5. 原地算法(in-place algorithm)研究

在实际刷题过程中,我发现理解双指针的核心思想比记忆特定解法更重要。这个问题的关键在于认识到我们可以利用数组的有序性,通过比较相邻元素来识别重复项。这种模式可以推广到许多其他数组处理问题中。

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

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

立即咨询