第一题:合并两个有序链表
题目:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
先明确一下题目的要求,有两个升序排列的单链表,要求合并后得到一个新的升序链表,不修改原链表节点值,仅仅调整节点的next指向。我想到的方法是迭代法和递归法,经过我的不懈努力,我把代码写出来了。
先展示代码,我把知识点写在一起。
迭代法:
python
# Definition for singly-linked list.
# 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(-1)
current = dummy
# 遍历两个链表,每次选较小的节点接上
while list1 and list2:
if list1.val <= list2.val:
current.next = list1
list1 = list1.next
else:
current.next = list2
list2 = list2.next
current = current.next
# 把剩下的节点直接接上
current.next = list1 if list1 else list2
return dummy.next
递归法:
python
# Definition for singly-linked list.
# 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]:
if not list1:
return list2
if not list2:
return list1
if list1.val <= list2.val:
list1.next = self.mergeTwoLists(list1.next, list2)
return list1
else:
list2.next = self.mergeTwoLists(list1, list2.next)
return list2
迭代法的核心是"搭架子+逐个选小节点拼接",类比生活中合并两个有序的排队队伍:
1、先摆一个"空排头",也就是我设的虚拟头节点,避免处理第一个节点选谁的难题;
2、用一个指针指着当前队伍的末尾,负责接下一个节点;
3、两个队伍的排头对比,把较小的那个接到当前队伍末尾,然后该队伍排头后移;
4、重复步骤3,直到其中一个队伍空了;
5、把剩下的那个非空队伍直接接到当前队伍末尾,本身已经有序,无需再对比;
6、最后的结果是空排头的下一个节点,也就是要跳过排头。
用例子模拟一下:以 list1 = [1,3,5],list2 = [2,4,6]为例。
1、初始:dummy(-1) -> None,current指向dummy,list1指向1,list2指向2;
2、第一次循环:1 < 2 -> current.next=1,list1移到3,current移到1;
3、第二次循环:3 > 2 ->current.next = 2,list2移到4,current移到2;
4、第三次循环:3 < 4 -> current.next = 3,list1移到5,current移到3;
5、第四次循环:5 > 4 -> current.next = 4,list2移到6,current移到4;
6、第五次循环:5 < 6 -> current.next = 5,list1移到None,current移到5;
7、list1为空,循环结束,current.next = list2,也就是剩下的数字6;
8、最终结果:dummy(-1) -> 1 -> 2 -> 3 -> 4 -> 5 -> 6,返回dummy.next即1开头的链表。
分析一下时间复杂度:O(m + n),n和m分别是两个链表的节点数,每个节点仅仅被访问一次。
递归法:
核心思路:把大问题拆成小问题,直到触达终止条件。
当前问题:合并list1和list2,得到一个升序链表。
拆分子问题:
1、对比list1.val和list2.val,选较小的节点作为当前层的结果节点;
2、该节点的next,指向"该节点的下一个节点"和"另一个链表的当前节点";
3、终止条件:如果list1为空,直接返回list2;如果list2为空,直接返回list1。
用示例进行模拟验证:以 list1 = [1,3,5],list2 = [2,4,6]为例。
1、第一层调用:merge(1->3->5,2->4->6) -> 1 < 2 -> 1.next = merge(3->5,2->4->6),返回1;
2、第二层调用:merge(3->5,2->4->6) -> 3 > 2 ->2.next = merge(3->5,4->6),返回2;
3、第三层调用:merge(3->5,4->6) -> 4 > 3 ->3.next = merge(5,4->6),返回3;
4、第四层调用:merge(5,4->6) -> 5 > 4 ->4.next = merge(5,6),返回4;
5、第五层调用:merge(5,6) -> 6 > 5 ->5.next = merge(None,6),返回5;
6、第六层调用:merge(None,6) -> 触发终止条件,返回6;
7、递归回溯:从第六层开始,依次给上一层的next赋值,最终拼接成结果。
分析一下时间复杂度:O(m + n),n和m分别是两个链表的节点数,每个节点仅仅被访问一次。
ok,写完收工。