想系统提升编程能力、查看更完整的学习路线,欢迎访问 AI Compass:github.com/tingaicompa… 仓库持续更新刷题题解、Python 基础和 AI 实战内容,适合想高效进阶的你。
📖 第13课:合并两个有序数组
模块:双指针 | 难度:Easy ⭐⭐ LeetCode 链接:leetcode.cn/problems/me… 前置知识:第12课:删除有序数组中的重复项 预计学习时间:20分钟
🎯 题目描述
给定两个按非递减顺序排列的整数数组 nums1 和 nums2,以及两个整数 m 和 n,分别表示 nums1 和 nums2 中的有效元素数量。
请你将 nums2 合并到 nums1 中,使合并后的数组同样按非递减顺序排列。
注意:nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0,应忽略。nums2 的长度为 n。
示例:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:合并 [1,2,3] 和 [2,5,6] 的结果是 [1,2,2,3,5,6]
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:nums2 为空,直接返回 nums1
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:nums1 的有效元素为空,直接拷贝 nums2
约束条件:
nums1.length == m + nnums2.length == n0 <= m, n <= 2001 <= m + n <= 200-10^9 <= nums1[i], nums2[j] <= 10^9
🧪 边界用例(面试必考)
| 用例类型 | 输入 | 期望输出 | 考察点 |
|---|---|---|---|
| nums2 为空 | nums1=[1,2,3], m=3, nums2=[], n=0 | [1,2,3] | 无需合并 |
| nums1 有效部分为空 | nums1=[0], m=0, nums2=[1], n=1 | [1] | 直接拷贝 |
| 交叉合并 | nums1=[1,3,5,0,0,0], m=3, nums2=[2,4,6], n=3 | [1,2,3,4,5,6] | 典型归并 |
| nums2 全部更小 | nums1=[4,5,6,0,0,0], m=3, nums2=[1,2,3], n=3 | [1,2,3,4,5,6] | 极端情况 |
| nums2 全部更大 | nums1=[1,2,3,0,0,0], m=3, nums2=[4,5,6], n=3 | [1,2,3,4,5,6] | 无需交换 |
| 有重复元素 | nums1=[1,2,2,0,0], m=3, nums2=[2,3], n=2 | [1,2,2,2,3] | 重复值处理 |
💡 思路引导
生活化比喻
想象你有两叠已排好序的扑克牌,需要合并成一叠。
🐌 笨办法:创建一副新牌,从两叠牌的顶部(最小的)开始,每次取较小的牌放到新牌堆。但这需要额外的空间(新牌堆)。
🤔 进阶想法:能否原地合并?如果从前往后放,会遇到问题——第一叠牌的前面还有数据,一旦写入就会覆盖!
🚀 聪明办法:从后往前合并!因为
nums1的后半部分是空的(预留空间),从后往前放不会覆盖任何有效数据。两只手分别指向两叠牌的末尾(最大的),每次取较大的牌放到最后,逐步向前填充。
关键洞察
nums1 后半部分是空位 → 从后往前合并不会覆盖 → 双指针从末尾开始,取较大值填充!
🧠 解题思维链
这一节模拟你在面试中"从零开始思考"的过程。
Step 1:理解题目 → 锁定输入输出
- 输入:
nums1:长度为m+n的数组,前m个元素有效,后n个是占位符 0nums2:长度为n的数组,所有元素有效- 两个数组都已排序(非递减)
- 输出:原地修改
nums1,合并后长度为m+n - 限制:必须原地修改
nums1,不能使用额外数组
Step 2:先想朴素办法(额外空间)
最直接的思路:归并排序的合并步骤,用一个新数组存结果。
# 需要额外空间 O(m+n)
result = []
i, j = 0, 0
while i < m and j < n:
if nums1[i] <= nums2[j]:
result.append(nums1[i])
i += 1
else:
result.append(nums2[j])
j += 1
# 拷贝剩余元素...
# 再把 result 拷贝回 nums1
- 时间复杂度:O(m+n)
- 瓶颈在哪:需要 O(m+n) 额外空间,而且从前往后在
nums1中合并会覆盖原有数据
Step 3:瓶颈分析 → 优化方向
- 核心问题:如何原地合并,避免覆盖
nums1的有效数据? - 关键洞察:
nums1的后n个位置是空的! - 优化思路:能否从后往前合并,利用这些空位?
思考过程:
- 从前往后合并:会覆盖
nums1前面的有效数据 - 从后往前合并:
nums1[m+n-1], nums1[m+n-2], ...都是空位,不会覆盖! - 双指针分别指向
nums1[m-1](最后一个有效元素) 和nums2[n-1](最后一个元素) - 每次取较大值放到
nums1[m+n-1],然后向前移动
Step 4:选择武器
- 选用:双指针逆向归并
- 理由:从后往前填充,利用
nums1的预留空间,避免覆盖,O(1)空间完成原地合并
🔑 模式识别提示:当题目涉及"两个有序数组合并"且"目标数组有预留空间",优先考虑"逆向双指针归并"
🔑 解法一:正向归并(额外空间)
思路
使用额外数组,模拟归并排序的合并步骤,从前往后逐个比较取较小值。这是最直观的思路,但不符合原地修改要求。
图解过程
nums1 = [1, 2, 3, 0, 0, 0], m = 3
nums2 = [2, 5, 6], n = 3
使用额外数组 result:
Step 1: 比较 nums1[0]=1 和 nums2[0]=2, 取1
result = [1]
Step 2: 比较 nums1[1]=2 和 nums2[0]=2, 取nums1的2(或任意一个)
result = [1, 2]
Step 3: 比较 nums1[2]=3 和 nums2[0]=2, 取2
result = [1, 2, 2]
Step 4: 比较 nums1[2]=3 和 nums2[1]=5, 取3
result = [1, 2, 2, 3]
Step 5: nums1用完,拷贝nums2剩余元素
result = [1, 2, 2, 3, 5, 6]
最后拷贝回 nums1
Python代码
from typing import List
def merge_extra_space(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
解法一:正向归并(额外空间)
思路:经典归并,用额外数组存结果
"""
result = []
i, j = 0, 0
# 归并两个有序数组
while i < m and j < n:
if nums1[i] <= nums2[j]:
result.append(nums1[i])
i += 1
else:
result.append(nums2[j])
j += 1
# 拷贝 nums1 剩余元素
while i < m:
result.append(nums1[i])
i += 1
# 拷贝 nums2 剩余元素
while j < n:
result.append(nums2[j])
j += 1
# 拷贝回 nums1
for i in range(m + n):
nums1[i] = result[i]
# ✅ 测试
test1 = [1, 2, 3, 0, 0, 0]
merge_extra_space(test1, 3, [2, 5, 6], 3)
print(test1) # 期望: [1, 2, 2, 3, 5, 6]
test2 = [1]
merge_extra_space(test2, 1, [], 0)
print(test2) # 期望: [1]
复杂度分析
- 时间复杂度:O(m+n) — 遍历两个数组各一次
- 空间复杂度:O(m+n) — 需要额外数组存储结果
优缺点
- ✅ 思路清晰,标准归并算法
- ❌ 使用了额外空间,不符合原地修改要求
- ❌ 需要二次拷贝,性能略差
⚡ 解法二:逆向双指针归并(原地合并)
优化思路
从解法一的"从前往后"出发,思考如何避免覆盖?
关键在于:nums1 的后 n 个位置是空的!
- 如果从后往前填充,就不会覆盖任何有效数据
- 用三个指针:
p1 = m - 1:指向nums1有效部分的末尾p2 = n - 1:指向nums2的末尾p = m + n - 1:指向nums1的最后位置(写入位置)
- 每次比较
nums1[p1]和nums2[p2],取较大值填入nums1[p]
💡 关键想法:从后往前合并,每次放最大值,利用预留空间避免覆盖!
图解过程
nums1 = [1, 2, 3, 0, 0, 0], m = 3
nums2 = [2, 5, 6], n = 3
初始化:p1 = 2, p2 = 2, p = 5
[1, 2, 3, 0, 0, 0]
↑ ↑
p1 p
[2, 5, 6]
↑
p2
Step 1: 比较 nums1[p1]=3 和 nums2[p2]=6
6 > 3, 取6放到 nums1[p]=nums1[5]
[1, 2, 3, 0, 0, 6]
↑ ↑
p1 p
[2, 5, 6]
↑
p2 (p2--, p--)
Step 2: 比较 nums1[p1]=3 和 nums2[p2]=5
5 > 3, 取5放到 nums1[p]=nums1[4]
[1, 2, 3, 0, 5, 6]
↑ ↑
p1 p
[2, 5, 6]
↑
p2 (p2--, p--)
Step 3: 比较 nums1[p1]=3 和 nums2[p2]=2
3 > 2, 取3放到 nums1[p]=nums1[3]
[1, 2, 3, 3, 5, 6]
↑ ↑
p1 p
[2, 5, 6]
↑
p2 (p1--, p--)
Step 4: 比较 nums1[p1]=2 和 nums2[p2]=2
相等,取任意一个(这里取nums2的2)
[1, 2, 2, 3, 5, 6]
↑ ↑
p1 p
[2, 5, 6]
↑
p2 (p2--, p--)
Step 5: p2 < 0, nums2 用完,结束
(nums1 剩余的已经在正确位置)
最终结果:[1, 2, 2, 3, 5, 6]
Python代码
def merge(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
解法二:逆向双指针归并(原地)
思路:从后往前填充,避免覆盖
"""
# 三个指针
p1 = m - 1 # nums1 有效部分的末尾
p2 = n - 1 # nums2 的末尾
p = m + n - 1 # 写入位置(nums1 的末尾)
# 从后往前合并
while p1 >= 0 and p2 >= 0:
if nums1[p1] > nums2[p2]:
nums1[p] = nums1[p1] # 取 nums1 的元素
p1 -= 1
else:
nums1[p] = nums2[p2] # 取 nums2 的元素
p2 -= 1
p -= 1
# 如果 nums2 还有剩余,拷贝到 nums1
# (如果 nums1 有剩余,已经在正确位置,无需处理)
while p2 >= 0:
nums1[p] = nums2[p2]
p2 -= 1
p -= 1
# ✅ 测试
test1 = [1, 2, 3, 0, 0, 0]
merge(test1, 3, [2, 5, 6], 3)
print(test1) # 期望: [1, 2, 2, 3, 5, 6]
test2 = [1]
merge(test2, 1, [], 0)
print(test2) # 期望: [1]
test3 = [0]
merge(test3, 0, [1], 1)
print(test3) # 期望: [1]
test4 = [4, 5, 6, 0, 0, 0]
merge(test4, 3, [1, 2, 3], 3)
print(test4) # 期望: [1, 2, 3, 4, 5, 6]
复杂度分析
- 时间复杂度:O(m+n) — 每个元素最多被访问一次
- 具体地说:如果 m=100, n=100,最多需要 200 次比较
- 空间复杂度:O(1) — 只用了 3 个指针变量,原地修改
🐍 Pythonic 写法
可以利用 Python 的负数索引和切片,让代码更简洁:
def merge_pythonic(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Pythonic 写法:负数索引
"""
p1, p2, p = m - 1, n - 1, m + n - 1
while p1 >= 0 and p2 >= 0:
if nums1[p1] > nums2[p2]:
nums1[p], p1 = nums1[p1], p1 - 1
else:
nums1[p], p2 = nums2[p2], p2 - 1
p -= 1
# 简化:直接切片拷贝剩余的 nums2
nums1[:p2 + 1] = nums2[:p2 + 1]
# 极简写法(利用排序,虽然不是最优解)
def merge_sort(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
最简单但不推荐:拷贝后排序 O((m+n)log(m+n))
"""
nums1[m:] = nums2 # 拷贝 nums2 到 nums1 后半部分
nums1.sort() # 排序
⚠️ 面试建议:优先使用标准的逆向双指针解法(解法二),清晰且高效。排序法虽然简洁但时间复杂度更高,不推荐在面试中使用。
📊 解法对比
| 维度 | 解法一:正向归并(额外空间) | 解法二:逆向双指针(原地) | 排序法 |
|---|---|---|---|
| 时间复杂度 | O(m+n) | O(m+n) | O((m+n)log(m+n)) |
| 空间复杂度 | O(m+n) | O(1) | O(1) |
| 代码难度 | 简单 | 中等 | 极简 |
| 面试推荐 | ⭐⭐ | ⭐⭐⭐ | ⭐ |
| 适用场景 | 需要保留原数组 | 原地合并(推荐) | 不关心时间效率 |
面试建议:直接给出解法二(逆向双指针),这是最优解。关键在于解释为什么要从后往前,以及如何处理边界情况(nums1 或 nums2 有剩余)。
🎤 面试现场
模拟面试中的完整对话流程,帮你练习"边想边说"。
面试官:请你将两个有序数组合并到第一个数组中,第一个数组有足够的空间。
你:(审题30秒)好的,这道题要求将 nums2 合并到 nums1 中,nums1 已经预留了空间(长度 m+n)。让我先想一下...
最直观的思路是归并排序的合并步骤:从前往后比较,取较小值。但这会遇到问题——从前往后填充会覆盖 nums1 的有效数据,需要额外数组。
既然 nums1 的后半部分是空的(预留空间),我想到可以从后往前合并:
- 用三个指针:
p1指向nums1[m-1],p2指向nums2[n-1],p指向nums1[m+n-1] - 每次比较
nums1[p1]和nums2[p2],取较大值填入nums1[p] - 这样从后往前填充,不会覆盖任何有效数据
时间复杂度 O(m+n),空间复杂度 O(1),原地完成合并。
面试官:很好,请写一下代码。
你:(边写边说)
def merge(nums1, m, nums2, n):
p1, p2, p = m - 1, n - 1, m + n - 1
# 从后往前归并
while p1 >= 0 and p2 >= 0:
if nums1[p1] > nums2[p2]:
nums1[p] = nums1[p1]
p1 -= 1
else:
nums1[p] = nums2[p2]
p2 -= 1
p -= 1
# 如果 nums2 还有剩余,拷贝到 nums1
while p2 >= 0:
nums1[p] = nums2[p2]
p2 -= 1
p -= 1
关键点:
- 从后往前避免覆盖
- 取较大值(而不是较小值)
- 只需处理 nums2 的剩余,nums1 的剩余已经在正确位置
面试官:为什么只处理 nums2 的剩余?
你:因为 nums1 本身就在目标位置!如果 nums1 有剩余元素,它们已经在 nums1 的前面,不需要移动。只有 nums2 的剩余需要拷贝到 nums1 前面。
例如 nums1=[1,5,9,0,0,0], nums2=[2,3,4]:
- 归并到后面都放了5,9和部分nums2
- 如果nums2还剩[2,3,4],需要拷贝到前面
- 如果nums1还剩[1],已经在正确位置了!
面试官:测试一下?
你:用示例 nums1=[1,2,3,0,0,0], m=3, nums2=[2,5,6], n=3:
- 初始:
p1=2(指向3),p2=2(指向6),p=5 - 比较3和6,取6放到位置5,
p2--,p-- - 比较3和5,取5放到位置4,
p2--,p-- - 比较3和2,取3放到位置3,
p1--,p-- - 比较2和2,取nums2的2放到位置2,
p2--,p-- p2<0,nums2用完,结束- 结果
[1,2,2,3,5,6],正确!
再测边界 nums1=[0], m=0, nums2=[1], n=1:
p1=-1,直接进入第二个循环- 拷贝
nums2[0]=1到nums1[0] - 结果
[1],正确!
高频追问
| 追问 | 应答策略 |
|---|---|
| "能否从前往后合并?" | 可以但需要额外空间 O(m+n)。从前往后会覆盖 nums1 的有效数据,必须先拷贝到临时数组。从后往前利用了预留空间,更优。 |
| "如果 nums1 没有预留空间呢?" | 必须用额外数组。可以创建长度为 m+n 的新数组,归并后返回,或者让 nums1 重新分配空间(但这不是原地操作)。 |
| "这个算法稳定吗?" | 稳定。当 nums1[p1] == nums2[p2] 时,我们先取 nums2 的元素,保证了相同元素的相对顺序不变。如果改成先取 nums1,也是稳定的。 |
| "时间能否优化到 O(log(m+n))?" | 不能。必须读取所有元素至少一次才能合并,所以 O(m+n) 是最优的。二分查找在这里帮不上忙,因为不是查找问题。 |
🎓 知识点总结
Python技巧卡片 🐍
# 技巧1:同时更新多个变量(元组解包)
nums1[p], p1 = nums1[p1], p1 - 1 # 同时赋值和更新
# 技巧2:切片赋值拷贝剩余元素
nums1[:p2 + 1] = nums2[:p2 + 1] # 拷贝 nums2 的前 p2+1 个元素到 nums1
# 技巧3:负数索引(从后往前)
nums1[-1] # 最后一个元素
nums1[-2] # 倒数第二个元素
# 技巧4:range 倒序
for i in range(n - 1, -1, -1): # 从 n-1 到 0
pass
💡 底层原理(选读)
为什么从后往前不会覆盖?
关键在于写入位置永远在读取位置之后(或相同):
- 初始:
p = m + n - 1,p1 = m - 1,p2 = n - 1- 显然
p >= p1且p >= p2(因为n >= 0)- 每次循环,
p和p1(或p2) 同时递减- 所以始终保持
p >= p1,不会覆盖还没读的元素为什么只需处理 nums2 的剩余?
- 如果
p1 >= 0且p2 < 0:
nums1的[0, p1]还有元素,但它们已经在正确位置- 因为这些元素 ≤ 已归并的所有元素,不需要移动
- 如果
p2 >= 0且p1 < 0:
nums2的[0, p2]还有元素,需要拷贝到nums1[0, p]- 这些元素是最小的,应该在最前面
归并排序的经典应用
- 归并排序的"合并"步骤通常需要额外空间
- 这道题巧妙利用了预留空间,实现了 O(1) 空间的归并
- 这种"逆向思维"在很多原地算法中都有应用
算法模式卡片 📐
- 模式名称:逆向双指针归并
- 适用条件:
- 合并两个有序数组/链表
- 目标数组有预留空间(或可以从后往前操作)
- 要求原地修改(O(1)空间)
- 识别关键词:"合并有序数组"、"原地"、"预留空间"、"从后往前"
- 模板代码:
def merge_template(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""逆向双指针归并模板"""
p1, p2, p = m - 1, n - 1, m + n - 1
# 从后往前归并
while p1 >= 0 and p2 >= 0:
if nums1[p1] > nums2[p2]:
nums1[p] = nums1[p1]
p1 -= 1
else:
nums1[p] = nums2[p2]
p2 -= 1
p -= 1
# 处理 nums2 剩余
nums1[:p2 + 1] = nums2[:p2 + 1]
易错点 ⚠️
-
比较时取较小值而不是较大值
- ❌ 错误:从后往前,但每次取
min(nums1[p1], nums2[p2]) - ⚠️ 为什么错:从后往前要先放最大值!取小值会导致顺序错误
- ✅ 正确:取
max,从大到小填充
- ❌ 错误:从后往前,但每次取
-
忘记处理剩余元素
- ❌ 错误:只有主循环,没有后续拷贝
- ⚠️ 为什么错:当一个数组先用完,另一个的剩余元素没处理
- ✅ 正确:循环后检查
p2 >= 0,拷贝nums2剩余
-
处理了 nums1 的剩余
- ❌ 错误:
while p1 >= 0: nums1[p] = nums1[p1]; ... - ⚠️ 为什么错:
nums1剩余已经在正确位置,不需要移动!只需处理nums2 - ✅ 正确:只处理
nums2剩余
- ❌ 错误:
-
指针初始化错误
- ❌ 错误:
p1 = m,p2 = n,p = m + n - ⚠️ 为什么错:索引从0开始,应该是
m-1、n-1、m+n-1 - ✅ 正确:
p1 = m - 1, p2 = n - 1, p = m + n - 1
- ❌ 错误:
🏗️ 工程实战(选读)
这个算法思想在真实项目中的应用,让你知道"学了有什么用"。
-
场景1:外部排序(External Sort) — 处理超大文件(无法全部加载到内存)时,先将文件分块排序,然后多路归并。归并时可以用类似的双指针技巧,从两个有序文件读取数据归并到输出文件。
-
场景2:数据库索引合并 — 数据库执行多条件查询时,可能需要合并多个有序的索引结果集。例如查询
WHERE age > 20 AND city = 'Beijing',先用两个索引分别查出有序ID列表,再归并得到最终结果。 -
场景3:Git 合并冲突解决 — Git 合并代码时,如果两个分支都修改了同一文件的不同位置(已排序),Git 内部会用类似归并的算法自动合并,只在冲突处需要人工介入。
-
场景4:时序数据合并 — 物联网系统中,多个传感器产生按时间戳排序的数据流,服务端需要将多路数据流合并成一个全局有序的事件流,用多路归并(K-way merge)实现。
🏋️ 举一反三
完成本课后,试试这些同类题目来巩固知识:
| 题目 | 难度 | 相关知识点 | 提示 |
|---|---|---|---|
| LeetCode 21. 合并两个有序链表 | Easy | 双指针归并 | 链表版本,不需要考虑覆盖问题,直接用双指针 |
| LeetCode 23. 合并K个升序链表 | Hard | 多路归并+堆 | 用最小堆维护 K 个链表的当前最小节点 |
| LeetCode 977. 有序数组的平方 | Easy | 双指针归并 | 负数平方后变大,从两端向中间归并 |
| LeetCode 986. 区间列表的交集 | Medium | 双指针归并 | 归并两个区间列表,找交集 |
📝 课后小测
试试这道变体题,不要看答案,自己先想5分钟!
题目:给定两个有序数组 nums1 和 nums2,找出两个数组合并后的中位数。要求时间复杂度 O(log(m+n))。
例如:nums1 = [1,3], nums2 = [2],合并后 [1,2,3],中位数是 2。
💡 提示(实在想不出来再点开)
不能用归并(O(m+n)太慢)!用二分查找找第 k 小元素,k = (m+n)/2。每次排除一半元素,O(log(m+n))。这是 LeetCode 4 的原题。
✅ 参考答案
def findMedianSortedArrays(nums1: List[int], nums2: List[int]) -> float:
"""
找两个有序数组的中位数 O(log(m+n))
核心:二分查找第 k 小元素
"""
def find_kth(k: int) -> int:
"""找第 k 小元素(k从1开始)"""
i, j = 0, 0 # 两个数组的起始位置
while True:
# 边界:一个数组用完,直接返回另一个数组的第k个
if i >= len(nums1):
return nums2[j + k - 1]
if j >= len(nums2):
return nums1[i + k - 1]
# 边界:k=1,返回两个数组当前位置的较小值
if k == 1:
return min(nums1[i], nums2[j])
# 二分:比较两个数组的第 k//2 个元素
half = k // 2
new_i = min(i + half, len(nums1)) - 1
new_j = min(j + half, len(nums2)) - 1
if nums1[new_i] <= nums2[new_j]:
# 排除 nums1 的前 half 个元素
k -= (new_i - i + 1)
i = new_i + 1
else:
# 排除 nums2 的前 half 个元素
k -= (new_j - j + 1)
j = new_j + 1
m, n = len(nums1), len(nums2)
total = m + n
if total % 2 == 1:
# 奇数:中位数是第 (total//2 + 1) 个元素
return find_kth(total // 2 + 1)
else:
# 偶数:中位数是中间两个的平均值
return (find_kth(total // 2) + find_kth(total // 2 + 1)) / 2.0
# 测试
print(findMedianSortedArrays([1, 3], [2])) # 期望: 2.0
print(findMedianSortedArrays([1, 2], [3, 4])) # 期望: 2.5
核心思路:
- 合并后找中位数 = 找第 k 小元素(k = (m+n)//2 或 (m+n)//2+1)
- 用二分查找:每次排除 k//2 个元素
- 比较
nums1[k//2]和nums2[k//2] - 较小的那一半一定不包含第 k 小元素,可以排除
- 递归查找剩余部分的第
k - k//2小元素
- 比较
- 时间复杂度:每次排除一半,O(log(k)) = O(log(m+n))
这道题是 LeetCode Hard 难度,展示了二分查找在有序数组中的高级应用!
🎉 恭喜完成双指针模块!
你已经掌握了双指针的核心技巧:
- ✅ 快慢指针(移动零、去重)
- ✅ 对撞指针(盛水容器、三数之和)
- ✅ 三指针分区(颜色分类)
- ✅ 逆向双指针(归并有序数组)
双指针是面试高频模式,这个模块的题目一定要多练习,做到看到题目就能快速识别指针移动策略!
下一个模块是滑动窗口,它是双指针的进阶应用,专门处理连续子串/子数组问题。加油!💪
如果这篇内容对你有帮助,推荐收藏 AI Compass:github.com/tingaicompa… 更多系统化题解、编程基础和 AI 学习资料都在这里,后续复习和拓展会更省时间。