删除元素(不是删除而是覆盖)快慢指针 慢指针是覆盖位置,快指针找元素
📝 题目:移除元素
题目描述: 给定数组和值val,原地移除所有等于val的元素,返回新长度。
例子: nums = [3,2,2,3], val = 3 → nums = [2,2,_,_], return 2
🔥 暴力法思路:
暴力法想法:
"我要删除所有等于val的元素,
那我就从头开始找,
遇到一个val就删除它,
然后所有后面的元素都向前移动一位。"
暴力法代码:
def removeElement_brute(nums, val):i = 0while i < len(nums):if nums[i] == val:# 删除当前元素:所有后面元素前移for j in range(i, len(nums) - 1):nums[j] = nums[j + 1]nums.pop() # 删除最后一个元素# 注意:i不自增,因为删除后要检查新的nums[i]else:i += 1return len(nums)
暴力法执行过程:
初始: [3, 2, 2, 3], val = 3
i=0: nums[0]=3==val,删除
删除后: [2, 2, 3, _],长度变成3
i=0: nums[0]=2!=val,i++
i=1: nums[1]=2!=val,i++
i=2: nums[2]=3==val,删除
删除后: [2, 2, _, _],长度变成2
结束: 返回2
暴力法问题: 每次删除都要移动大量元素,效率低!
● 🎯 双指针法思路(快慢指针):
双指针法想法:
"我不删除元素,而是重新整理数组!
用两个指针:
- slow指针:指向下一个要放入好元素的位置
- fast指针:遍历所有元素
如果fast指向的元素不是val,就放到slow位置"
核心理念: "覆盖代替删除"
不要真的删除,而是用好的元素覆盖坏的位置!
双指针法代码:
def removeElement_two_pointers(nums, val):slow = 0 # 慢指针:下一个好元素要放的位置for fast in range(len(nums)): # 快指针:遍历所有元素if nums[fast] != val: # 如果是好元素nums[slow] = nums[fast] # 放到slow位置slow += 1 # slow后移return slow # slow就是新数组的长度
双指针法执行过程:
初始: [3, 2, 2, 3], val = 3
slow=0, fast=0
fast=0: nums[0]=3==val,跳过
slow=0 不变
fast=1: nums[1]=2!=val,是好元素
nums[0] = nums[1] = 2
数组变成: [2, 2, 2, 3]
slow = 1
fast=2: nums[2]=2!=val,是好元素
nums[1] = nums[2] = 2
数组变成: [2, 2, 2, 3]
slow = 2
fast=3: nums[3]=3==val,跳过
slow=2 不变
结果: 数组前2位是[2,2],返回长度2
🔍 双指针法的巧妙之处:
1. 两个指针的分工:
slow指针:维护"已整理好的部分"
fast指针:探索"还没处理的部分"
2. 状态变化:
初始: [需要处理的部分 ]
slow=0
进行中: [已整理好] [需要处理的部分]
↑slow ↑fast
最终: [已整理好] [垃圾区域]
↑slow
● 🧠 两种方法的思维对比:
| 维度 | 暴力法思维 | 双指针法思维 |
|-------|---------|---------|
| 操作方式 | 真实删除元素 | 覆盖重排元素 |
| 思考角度 | "删掉坏的" | "保留好的" |
| 处理策略 | 遇到坏元素就删 | 遇到好元素就搬 |
| 时间复杂度 | O(n²) | O(n) |
| 空间复杂度 | O(1) | O(1) |
💭 思维转换的关键:
暴力法思维:
"我要删除所有val=3的元素"
↓ 直接但低效
真的去删除和移动
双指针法思维:
"我要构建一个新数组,只包含不等于val的元素"
↓ 间接但高效
原地重新排列
🎯 双指针法的核心洞察:
关键洞察1: 不需要真的删除,只需要重新排列
关键洞察2: 好元素的相对顺序不变
关键洞察3: 只关心前面的好元素,后面的垃圾不管
为什么双指针法更快?
暴力法:每次删除都要移动O(n)个元素 → 总共O(n²)
双指针法:每个元素只被访问一次,只做O(1)操作 → 总共O(n)
★ Insight ─────────────────────────────────────
双指针法的精髓是"重新定义问题":从"删除不要的元素"重新定义为"收集想要的元素"。这种思维转换让我们从删除操作(需要移动大量元素)变成了覆盖操作(只需要一次赋
值),从而大幅提升效率。
─────────────────────────────────────────────────
● 总结:暴力法="删掉坏的",双指针法="收集好的"。关键思维转换:从删除操作变成覆盖操作,效率从O(n²)提升到O(n)!