「这是我参与11月更文挑战的12天,活动详情查看:2021最后一次更文挑战」。
前言
一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第12题021 合并两个有序链表。
题目
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。\ 示例 1:
输入: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
}
}
}