LeetCode之HOT100--021 合并两个有序链表

162 阅读1分钟

「这是我参与11月更文挑战的12天,活动详情查看:2021最后一次更文挑战」。

前言

一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第12题021 合并两个有序链表。

题目

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

merge_ex1.jpg

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

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

分析

本题的目的是合并两个有序链表,我们在之前的 004寻找两个正序数组的中位数 中提到过类似的方法,当时我们是解题思路就是先将两个有序数组进行合并,然后直接取出中位数。当时我们做的有序数组的合并是通过遍历的方式完成的。
循环遍历法
本题也可以通过这种方式完成,基本思路如下:

1. 创建一个虚拟头结点dummy为ListNode(-1),并创建一个临时节点curNode表示当前节点,初始化指向dummy,创建两个临时节点curL1和curL2分别指向两个数组的头结点
2. 找出curL1和curL2中较小的节点,并将较小的节点赋值给curNode.next
3. 将curL1和curL2中较小的节点向后移一个节点,并且curNode也向后移一个节点,为下一轮循环准备
4. 如果curL1和curL2都不为nil,则表示两个链表都没有到最后,则继续步骤2~4,否则继续步骤5
5. 将curL1和curL2不为nil的节点赋值给curNode.next
6. 返回虚拟头结点的下一个节点dummy.next

递归法
其实在上述的循环遍历法中我们将curL1和curL2中较小的节点进行处理后,剩下的还是两个有序链表之间的合并问题,所以可以采用递归的方式进行解决。基本思路如下:

1. 递归结束条件为:l1 或 l2至少有一个为空
2. 如果l1.val <= l2.val,则继续合并mergeTwoLists(l1.next, l2),并将合并结果赋值给l1.next,并返回l1
3. 如果l1.val > l2.val,则继续合并mergeTwoLists(l1, l2.next),并将合并结果赋值给l2.next,并返回l2

题解

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init() { self.val = 0; self.next = nil; }
 *     public init(_ val: Int) { self.val = val; self.next = nil; }
 *     public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
 * }
 */
class KLLC021 {

    func mergeTwoLists(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
        if l1 == nil {
            return l2
        }
        if l2 == nil {
            return l1
        }
        if l1!.val <= l2!.val {
            l1!.next = mergeTwoLists(l1!.next, l2)
            return l1
        } else {
            l2!.next = mergeTwoLists(l1, l2!.next)
            return l2
        }
    }
}