❝
这是一道 「简单」 题
https://leetcode.cn/problems/merge-two-sorted-lists/❞
题目
将两个升序链表合并为一个新的 「升序」 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
-
两个链表的节点数目范围是 -
-
和 均按 「非递减顺序」 排列
递归
取两个链表的第一个节点中的最小值,那么这个最小的节点一定是合并后链表的第一个节点, 记录为结果的 head
。
假如这个最小节点 head
是 l1
的第一个节点,那么将 l1
的后面的节点和 l2
合并后的结果就应该赋值给 head.next
。l2
同理。
合并 l1
的后面的节点和 l2
同样是合并两个有序链表,是「本题目的子问题」。求子问题是典型的「递归」实现。
递归边界: 当 l1
或 l2
任意一个链表为空的时候,直接返回另外一个链表。
Java 代码实现
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
if(list1.val < list2.val){
// list1 位 head
list1.next = mergeTwoLists(list1.next, list2);
return list1;
}else{
// list2 位 head
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
}
Go 代码实现
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
if list1 == nil {
return list2
}
if list2 == nil {
return list1
}
if list1.Val < list2.Val {
list1.Next = mergeTwoLists(list1.Next, list2)
return list1
}else {
list2.Next = mergeTwoLists(list1, list2.Next)
return list2
}
}
复杂度分析
时间复杂度: ,M
和 N
分别为两个链表中的节点个数。因为每次递归都是只取一个最小的节点,且时间复杂度为,两个链表总共有 M + N
个节点,所以总的时间复杂度为 。
空间复杂度: ,M
和 N
分别为两个链表中的节点个数,空间复杂度取决于递归调用栈的深度,为 M + N
。
双指针
为了方便起见,先创建一个保护节点 protect
,这样就消除了第一个节点和其他节点的差异,将后续每一步得到的最小值节点逐个链接到以 protect
开头的链表的末尾节点 tail
后面即可,最终返回答案 protect.next
。
定义两个指针 list1
和 list2
,分别指向两个链表的头节点。取 list1
和 list2
中的最小值链接到以 protect
开头的链表的末尾。假如 list1
比较小,那么 list1
就往后移动一个节点(list1 = list1.next
)。然后继续比较 list1
和 list2
,将两者中比较小的那个链接到以 protect
开头的链表的末尾。以此类推,直到其中一个为空,然后将另外一个不为空的链表直接链接到以 protect
开头的链表的末尾。
Java 代码实现
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
ListNode protect = new ListNode();
ListNode tail = protect;
protect.next = tail;
while(list1 != null && list2 != null){
if(list1.val < list2.val){
tail.next = list1;
tail = list1;
list1 = list1.next;
}else{
tail.next = list2;
tail = list2;
list2 = list2.next;
}
}
tail.next = list1 == null ? list2 : list1;
return protect.next;
}
}
Go 代码实现
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
if list1 == nil {
return list2
}
if list2 == nil {
return list1
}
protect := &ListNode{-1, nil}
tail := protect
protect.Next = tail
for list1 != nil && list2 != nil {
if list1.Val < list2.Val {
tail.Next = list1
tail = list1
list1 = list1.Next
}else {
tail.Next = list2
tail = list2
list2 = list2.Next
}
}
if list1 == nil {
tail.Next = list2
}else {
tail.Next = list1
}
return protect.Next
}
复杂度分析
时间复杂度: ,M
和 N
分别为两个链表中的节点个数。每次比较完毕后,两个指针肯定有一个向后移动一步,最多移动 M + N
步。
空间复杂度: ,只额外开辟了一个保护节点。
– End –
原文始发于微信公众号(i余数):【算法题解】58. 合并两个有序链表
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/193613.html