📖 第13课:合并两个有序数组

3 阅读20分钟

想系统提升编程能力、查看更完整的学习路线,欢迎访问 AI Compass:github.com/tingaicompa… 仓库持续更新刷题题解、Python 基础和 AI 实战内容,适合想高效进阶的你。

📖 第13课:合并两个有序数组

模块:双指针 | 难度:Easy ⭐⭐ LeetCode 链接:leetcode.cn/problems/me… 前置知识:第12课:删除有序数组中的重复项 预计学习时间:20分钟


🎯 题目描述

给定两个按非递减顺序排列的整数数组 nums1nums2,以及两个整数 mn,分别表示 nums1nums2 中的有效元素数量。

请你将 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 + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= 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 个是占位符 0
    • nums2:长度为 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]=3nums2[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]=3nums2[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]=3nums2[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]=2nums2[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

关键点:

  1. 从后往前避免覆盖
  2. 取较大值(而不是较小值)
  3. 只需处理 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]=1nums1[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 >= p1p >= p2(因为 n >= 0)
  • 每次循环,pp1(或 p2) 同时递减
  • 所以始终保持 p >= p1,不会覆盖还没读的元素

为什么只需处理 nums2 的剩余?

  • 如果 p1 >= 0p2 < 0:
    • nums1[0, p1] 还有元素,但它们已经在正确位置
    • 因为这些元素 ≤ 已归并的所有元素,不需要移动
  • 如果 p2 >= 0p1 < 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]

易错点 ⚠️

  1. 比较时取较小值而不是较大值

    • ❌ 错误:从后往前,但每次取 min(nums1[p1], nums2[p2])
    • ⚠️ 为什么错:从后往前要先放最大值!取小值会导致顺序错误
    • ✅ 正确:取 max,从大到小填充
  2. 忘记处理剩余元素

    • ❌ 错误:只有主循环,没有后续拷贝
    • ⚠️ 为什么错:当一个数组先用完,另一个的剩余元素没处理
    • ✅ 正确:循环后检查 p2 >= 0,拷贝 nums2 剩余
  3. 处理了 nums1 的剩余

    • ❌ 错误:while p1 >= 0: nums1[p] = nums1[p1]; ...
    • ⚠️ 为什么错:nums1 剩余已经在正确位置,不需要移动!只需处理 nums2
    • ✅ 正确:只处理 nums2 剩余
  4. 指针初始化错误

    • ❌ 错误:p1 = m,p2 = n,p = m + n
    • ⚠️ 为什么错:索引从0开始,应该是 m-1n-1m+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分钟!

题目:给定两个有序数组 nums1nums2,找出两个数组合并后的中位数。要求时间复杂度 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 学习资料都在这里,后续复习和拓展会更省时间。