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 链表专题,欢迎在评论区交流你遇到的链表难题,一起把链表彻底拿下!🚀

相关推荐
2601_962100541 小时前
python包和模块的内容整理
python·模块·导入·虚拟环境·
二进制漫游记1 小时前
FastAPI项目集成 Qdrant向量数据库+阿里云Embedding完整实战(工具封装+业务调用)
数据库·python·阿里云·embedding
Tisfy1 小时前
LeetCode 3876.构造奇偶一致的数组 II:三种情况分类讨论(其实还是脑筋急转弯)
算法·leetcode·题解·脑筋急转弯
-今昭-2 小时前
《V2V 迁移实战:将 VMware 虚拟机转为 OpenStack 可用 Glance qcow2 镜像》
开发语言·python
2601_962381582 小时前
【转】Python渗透测试工具:sqlmap
python·渗透测试·sql注入·sqlmap·数据库安全
用户3721574261353 小时前
如何使用 Python 从 Word 文档中提取图片
python
kiracrimson3 小时前
从缓存的角度看链表与线性表的差异
数据结构·链表·缓存
木井巳4 小时前
【BFS/DFS 解决 FloodFill 算法】太平洋大西洋水流问题
java·算法·leetcode·深度优先·广度优先·宽度优先·推荐算法
白山编程大哥4 小时前
Java 集合算法:从排序、查找到底层原理的实战指南
java·python·算法
Navigator_Z5 小时前
LeetCode //C - 1224. Maximum Equal Frequency
c语言·算法·leetcode