力扣100题之21合并两个有序链表

题目描述:

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

复制代码
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

复制代码
输入:l1 = [], l2 = []
输出:[]

示例 3:

复制代码
输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

前置python链表知识

节点定义

python 复制代码
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

注意 __init__ 是前后各两个下划线 ,写成 _init_ 会报错。

从数组建链表

python 复制代码
def build_list(nums):
    dummy = ListNode()
    cur = dummy
    for x in nums:
        cur.next = ListNode(x)
        cur = cur.next
    return dummy.next

遍历

python 复制代码
cur = head
while cur:
    print(cur.val)
    cur = cur.next

哑节点 dummy

避免处理头节点被删/插入的特殊情况,最后返回 dummy.next。

python 复制代码
dummy = ListNode(0, head)
cur = dummy
# 操作 cur.next
return dummy.next

判断链表为空:

python 复制代码
if not head:          # 空链表(head 是 None)
if head is None:      # 等价写法

判断不为空:

python 复制代码
if head:
if head is not None:

遍历到末尾:

python 复制代码
while cur:            # cur 不为 None 就继续
    cur = cur.next

判断尾节点:

python 复制代码
if not cur.next:      # 下一个是 None,说明是尾节点

本题解法1:

python 复制代码
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val        # 节点存的值
        self.next = next      # 指向下一个节点,默认 None


class Solution:
    def mergeTwoLists(self, list1, list2):
        dummy = ListNode(-1)      # 造一个哑节点,值随便给(-1),只用来占位
        cur = dummy               # cur 是"结果链表的尾巴",初始指向 dummy

        while list1 and list2:    # 两个链表都还有节点时,循环比较
            if list1.val <= list2.val:
                cur.next = list1  # list1 的当前节点更小(或相等),接到结果尾部
                list1 = list1.next  # list1 往后走一步
            else:
                cur.next = list2  # 否则接 list2 的当前节点
                list2 = list2.next  # list2 往后走一步
            cur = cur.next        # 结果尾部指针往后走一步

        # 循环结束时,必有一个链表走完了
        # 把还没走完的那个链表整段接上(它本身有序,无需再逐个比)
        cur.next = list1 if list1 else list2

        return dummy.next         # dummy 是占位的,真正的头是 dummy.next

两个链表都是升序的,那结果也应该是升序的。所以想到用两个指针,每次比较当前两个节点的值,谁小就先把谁接上。这样从头到尾走一遍,出来的就是有序的。

问题是:结果链表一开始是空的,头节点怎么处理?如果不用技巧,就要单独判断"结果为空"的情况,很麻烦。后来想到用哑节点 dummy,让它当"假头",真正的头是 dummy.next,这样就统一了。

复杂度分析:时间复杂度是 O(m+n),其中 m、n 分别是两个链表的长度------因为 while 循环每次比较都会"消费"掉一个节点,最坏情况下要把两个链表的全部节点各走一遍,每次循环内部只是比较和改指针,都是常数操作,所以总时间与两链表长度之和成正比;空间复杂度是 O(1),因为只新建了一个哑节点 dummy 和 cur、list1、list2 几个固定数量的指针变量,没有开数组、没有递归、也没有 new 新节点,原链表的节点都是原地复用,只是把 next 指针重新串起来,所以额外空间是常数级。

解法2:

python 复制代码
class Solution(object):
    def mergeTwoLists(self, list1, list2):
        # 出口1:list1 空了,剩下 list2 本身就是有序的,直接还回去
        if not list1:
            return list2

        # 出口2:list2 空了,剩下 list1 直接还回去
        if not list2:
            return list1

        # 比较两个链表当前头节点的值,决定谁当合并后的头
        if list1.val <= list2.val:
            # list1 的头更小,所以它当这一层的头
            # list1 后面接什么?接"list1 剩下的部分"和"list2"合并的结果
            # 这里递归调用自己,把子问题丢给函数,返回值是拼好的后半段
            list1.next = self.mergeTwoLists(list1.next, list2)
            # 把拼好的整条链表交回给上一层
            return list1
        else:
            # list2 的头更小,它当这一层的头
            # list2 后面接"list1"和"list2 剩下的部分"合并的结果
            list2.next = self.mergeTwoLists(list1, list2.next)
            # 交回给上一层
            return list2

这道题是链表递归的入门经典,核心思想只有一句话:只看当前这一步,剩下的交给函数自己 。递归三要素在这里体现得非常清楚:出口 是任意一个链表为空时直接返回另一个(因为它本身有序,不用再处理);当前这一步 是比较两个头节点的值,谁小谁当这一层结果的头;递归 则是把"头后面该接什么"这个问题原封不动丢给函数自己,也就是 list1.next = mergeTwoLists(list1.next, list2)。很多人第一次想不出来,是因为习惯了迭代"从头往后一个个接"的思路,而递归是反着来的------先一路往下钻到出口,再在返回的路上,一层层把当前节点接到下层返回的整条链表前面,每一层返回的都是一条已经拼好的完整链表,最后自然就串成了答案。代码只有短短十行,却把"最小子问题 + 递归调用 + 组合结果"的套路演绎得很干净,非常适合拿来理解递归的思维方式;不过要注意递归的空间复杂度是 O(m+n)(调用栈),链表很长时会爆栈,所以工程上更推荐迭代写法------递归用来理解思路,迭代用来实际提交,这是刷链表题的一个实用原则。

相关推荐
旖旎夜光1 小时前
LeetCode 137 : 只出现一次的数字 II(位运算) —— 题解
c++·笔记·学习·算法·leetcode·力控
旋生万物2 小时前
Agent System Prompt 注入螺旋公理:让 AI 推理不跑偏的可复制模板(附完整 Prompt)
人工智能·算法
深圳雨林凯AI2 小时前
图案裂变的空间关系原理:元素放大为何必然挤压负空间,以及三个可调变量|雨林凯AI技术拆解
人工智能·算法
tellmewhoisi2 小时前
机器学习:聚类算法
算法·机器学习·聚类
yi0113 小时前
LeetCode 134 加油站:从 双循环暴力 演变到 贪心
linux·算法·leetcode
sogw-三叶草️3 小时前
LeetCode 989. 数组形式的整数加法
数据结构·算法
维克兜率天3 小时前
【维克】均值回归:跌多了会涨,涨多了会跌
开发语言·笔记·python·算法·均值算法·回归·量化
yi0113 小时前
DAY 14: LeetCode 394. 字符串解码|递归和栈到底怎么处理嵌套?
数据结构·笔记·python·算法·leetcode
P.D.Wei4 小时前
[Leetcode 1614] 括号的最大嵌套深度
算法·leetcode·职场和发展