【算法题解】58. 合并两个有序链表

这是一道 「简单」
https://leetcode.cn/problems/merge-two-sorted-lists/

题目

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

示例 1:【算法题解】58. 合并两个有序链表

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

示例 2:

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

示例 3:

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

提示:

  • 两个链表的节点数目范围是
  • 均按 「非递减顺序」 排列

递归

取两个链表的第一个节点中的最小值,那么这个最小的节点一定是合并后链表的第一个节点, 记录为结果的 head

假如这个最小节点 headl1 的第一个节点,那么将 l1 的后面的节点和 l2 合并后的结果就应该赋值给 head.nextl2 同理。

合并 l1 的后面的节点和 l2 同样是合并两个有序链表,是「本题目的子问题」。求子问题是典型的「递归」实现。

递归边界:l1l2 任意一个链表为空的时候,直接返回另外一个链表。

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
    }

}

复杂度分析

时间复杂度: MN 分别为两个链表中的节点个数。因为每次递归都是只取一个最小的节点,且时间复杂度为,两个链表总共有 M + N 个节点,所以总的时间复杂度为

空间复杂度: MN 分别为两个链表中的节点个数,空间复杂度取决于递归调用栈的深度,为 M + N

双指针

为了方便起见,先创建一个保护节点 protect,这样就消除了第一个节点和其他节点的差异,将后续每一步得到的最小值节点逐个链接到以 protect 开头的链表的末尾节点 tail 后面即可,最终返回答案 protect.next

定义两个指针 list1list2,分别指向两个链表的头节点。取 list1list2 中的最小值链接到以 protect 开头的链表的末尾。假如 list1 比较小,那么 list1 就往后移动一个节点(list1 = list1.next)。然后继续比较 list1list2,将两者中比较小的那个链接到以 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{-1nil}
    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

}

复杂度分析

时间复杂度: MN 分别为两个链表中的节点个数。每次比较完毕后,两个指针肯定有一个向后移动一步,最多移动 M + N 步。

空间复杂度: ,只额外开辟了一个保护节点。


– End –


【算法题解】58. 合并两个有序链表
如果觉得有所收获,就顺道点个关注吧!【算法题解】58. 合并两个有序链表 

原文始发于微信公众号(i余数):【算法题解】58. 合并两个有序链表

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/193613.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!