LeetCode 21 合并两个有序链表:彻底理解虚拟头节点(Dummy)套路

LeetCode 21 合并两个有序链表:彻底理解虚拟头节点(Dummy)套路

前言

刚开始刷链表题时,经常会遇到一个问题:

新链表的头节点到底该怎么处理?

比如今天这道经典题:

LeetCode 21:合并两个有序链表

很多同学第一次写的时候,代码里会出现大量判断:

python 复制代码
if head is None:
    head = node
else:
    ...

不仅逻辑复杂,而且容易出错。

而这道题最经典的解法,就是使用链表中的万能技巧:

虚拟头节点(Dummy Node)

学会这个技巧之后,后面大量链表题都能套用同样的思路。


题目描述

给你两个升序链表:

text 复制代码
list1 = 1 -> 2 -> 4

list2 = 1 -> 3 -> 4

要求将它们合并成一个新的升序链表:

text 复制代码
1 -> 1 -> 2 -> 3 -> 4 -> 4

并返回合并后的头节点。


为什么需要虚拟头节点?

假设不用 Dummy。

当我们第一次往新链表插入节点时:

python 复制代码
head = None

就必须额外判断:

python 复制代码
if head is None:
    head = node

后面每次添加节点又是另一套逻辑。

这样会导致:

  • 头节点需要特殊处理
  • 代码分支增多
  • 容易遗漏边界情况

于是就有了 Dummy 技巧。


Dummy 节点是什么?

本质上就是人为创建一个临时节点:

python 复制代码
dummy = ListNode()

例如:

text 复制代码
dummy -> None

以后所有节点都统一接在 dummy 后面:

text 复制代码
dummy -> 1 -> 2 -> 3

最终返回:

python 复制代码
dummy.next

即可获得真正的链表头节点。

这样就不用再考虑:

text 复制代码
谁是第一个节点

的问题了。


迭代法

核心思路

同时遍历两个链表:

  1. 比较当前节点值
  2. 选择较小节点拼接到新链表
  3. 对应链表后移
  4. cur 指针后移
  5. 其中一个链表结束后,直接拼接剩余部分

代码实现

python 复制代码
from typing import Optional

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next


class Solution:
    def mergeTwoLists(
        self,
        list1: Optional[ListNode],
        list2: Optional[ListNode]
    ) -> Optional[ListNode]:

        # 创建虚拟头节点
        dummy = ListNode()

        # 当前拼接位置
        cur = dummy

        # 同时遍历两个链表
        while list1 and list2:

            if list1.val < list2.val:

                cur.next = list1
                list1 = list1.next

            else:

                cur.next = list2
                list2 = list2.next

            # 拼接指针后移
            cur = cur.next

        # 直接拼接剩余部分
        cur.next = list1 if list1 else list2

        return dummy.next

代码执行过程

假设:

text 复制代码
list1 = 1 -> 2 -> 4

list2 = 1 -> 3 -> 4

初始状态:

text 复制代码
dummy

cur -> dummy

list1 -> 1

list2 -> 1

第一次比较

text 复制代码
1 >= 1

选择:

text 复制代码
list2

结果:

text 复制代码
dummy -> 1

第二次比较

text 复制代码
1 < 3

选择:

text 复制代码
list1

结果:

text 复制代码
dummy -> 1 -> 1

第三次比较

text 复制代码
2 < 3

结果:

text 复制代码
dummy -> 1 -> 1 -> 2

第四次比较

text 复制代码
4 > 3

结果:

text 复制代码
dummy -> 1 -> 1 -> 2 -> 3

第五次比较

text 复制代码
4 == 4

选择 list2:

text 复制代码
dummy -> 1 -> 1 -> 2 -> 3 -> 4

此时:

text 复制代码
list2 = None

循环结束。


拼接剩余节点

执行:

python 复制代码
cur.next = list1

得到:

text 复制代码
dummy
  ↓

1 -> 1 -> 2 -> 3 -> 4 -> 4

最终返回:

python 复制代码
dummy.next

即可。


边界情况分析

情况一:一个链表为空

python 复制代码
list1 = None

list2 = [2,3]

循环不会进入。

直接执行:

python 复制代码
cur.next = list2

结果正确。


情况二:两个链表都为空

python 复制代码
list1 = None

list2 = None

返回:

python 复制代码
None

结果正确。


情况三:长度差异很大

python 复制代码
list1 = [1,5,6]

list2 = [2]

当:

python 复制代码
list2

遍历结束后。

剩余:

python 复制代码
5 -> 6

会整体拼接到末尾。

无需继续比较。


时间复杂度分析

假设:

text 复制代码
list1长度 = m

list2长度 = n

每个节点最多访问一次。

因此:

text 复制代码
时间复杂度:O(m+n)

只使用了:

text 复制代码
dummy
cur

两个额外指针。

因此:

text 复制代码
空间复杂度:O(1)

Dummy 节点套路总结

这道题真正重要的并不是合并链表。

而是掌握:

虚拟头节点(Dummy)

使用场景:

  • 合并链表
  • 删除节点
  • 分隔链表
  • 反转链表
  • K个一组翻转链表

核心模板:

python 复制代码
dummy = ListNode()

cur = dummy

while 条件:

    cur.next = 某节点

    cur = cur.next

return dummy.next

建议直接背下来。

因为后面大量链表题都会出现。


总结

今天这道题看起来只是简单的链表合并,但背后隐藏着链表题中最重要的一个套路:

Dummy 虚拟头节点。

记住一句话:

能用 Dummy 的地方,尽量用 Dummy。

它最大的价值就是:

  • 统一头节点处理逻辑
  • 减少特殊判断
  • 提高代码可读性
  • 降低链表题出错概率

后面刷到链表题时,如果发现自己写了很多:

python 复制代码
if head is None:

不妨想想:

这里能不能引入一个 Dummy 节点?

很多时候,代码会瞬间清爽很多。


如果你正在刷 LeetCode 链表专题,欢迎在评论区交流你遇到的链表难题,一起把链表彻底拿下!🚀

相关推荐
从零开始学习人工智能16 分钟前
【踩坑实录】WSL2 解决 onnxruntime\-gpu ImportError: libcudart\.so\.13 无 CUDA13 运行库问题
python
卷无止境1 小时前
在 awesome-fastapi 里,哪些库值得一看?
后端·python
zhanghaha13142 小时前
Python进阶教程:6_JSON 数据解析 —— 新手完全指南
开发语言·python·json
Python私教2 小时前
API 输出模型怎么设计:从 model_dump() 到显式展示层
python·fastapi
Python私教2 小时前
本地 AI 工具服务该绑定 127.0.0.1 还是 0.0.0.0?
python·fastapi
卷无止境3 小时前
FastAPI 的Admin面板生态
后端·python
ctlover3 小时前
Streamlit 框架
python
ι:3 小时前
MATLAB 与 Python 搭建无人机地面站:优势、劣势与选型逻辑
python·matlab·无人机
船厂电气自动化ai大模型4 小时前
AI大模型与数学 第32课 函数凹凸性与二阶导数:拐点求解、凹凸区间计算(10道二阶导数计算题)
数据结构·人工智能·python·深度学习·算法
旖旎夜光4 小时前
LeetCode 3:无重复字符的最长子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口