LeetCode 21. Merge Two Sorted Lists && 剑指24. 合并两个排序的链表

题目

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

Example:

Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4

2024.6.28

又时隔两年我又回来了。

迭代写出来了,一次bug free了,虽然多了一些无用操作,算是比以前有进步了吧。递归,还是不会return,但是整体的思路已经写出来了,除了不知道咋return以外。其实就是改谁就return谁就好了。总体和两年前的水平对比居然是进步了的,我大为震惊。

2022.9.14 Java

时隔两年我又回来了。

递归的做法,再次没有想到,反省。不过后来自己画了一遍就好了。

迭代的做法还是一如既往地简单,但也一如既往地忘记移动curr指针了,导致偷看了这篇笔记才意识到忘了移curr了。

这次的迭代代码写的和以前不太一样,虽然最开始写的和以前一样但因为忘了移指针以为是我的做法有问题就重写了一遍,就也往这贴一下好了。

/**
 * 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 l1, ListNode l2) {
        ListNode dummyHead = new ListNode();
        ListNode curr = dummyHead;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                curr.next = l1;
                l1 = l1.next;
            } else {
                curr.next = l2;
                l2 = l2.next;
            }
            curr = curr.next;
        }
        if (l1 == null) {
            curr.next = l2;
        }
        if (l2 == null) {
            curr.next = l1;
        }
        return dummyHead.next;
    }
}

递归画的图:

2020.10.13 Java

普通的做法:

Runtime: 0 ms, faster than 100.00% of Java online submissions for Merge Two Sorted Lists.

Memory Usage: 38.5 MB, less than 8.45% of Java online submissions for Merge Two Sorted Lists.

/**
 * 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 l1, ListNode l2) {
        ListNode dummyHead = new ListNode();
        ListNode curr = dummyHead;
        while (l1 != null || l2 != null) {
            if (l1 == null) {
                curr.next = l2;
                l2 = l2.next;
            } else if (l2 == null) {
                curr.next = l1;
                l1 = l1.next;
            } else {
                if (l1.val < l2.val) {
                    curr.next = l1;
                    l1 = l1.next;
                } else {
                    curr.next = l2;
                    l2 = l2.next;
                }
            }
            curr = curr.next;
        }
        return dummyHead.next;
    }
}

还有一种递归做法,然鹅还是没想到并且代码不太会写。思路大概是:比较两个node的大小,更小的那个的下一个node是继续调用这个函数以后返回的结果。

Runtime: 0 ms, faster than 100.00% of Java online submissions for Merge Two Sorted Lists.

Memory Usage: 38.3 MB, less than 8.45% of Java online submissions for Merge Two Sorted Lists.

/**
 * 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 l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

以下是一年半前处于超级菜鸡状态的笔记……别看了吧。


这道题乍一看思路很简单,仔细一看发现是链表,卒。之前学数据结构的时候感觉链表和二叉树都练的很少,导致看到这俩就心里一紧,感觉学了跟没学一样,现在终于要直面链表了,内心是一万个拒绝的。然而这是迟早都得面对的,不如早点把它解决了。

由于不会写链表,因此也只能试图去理解其他人写的代码,然而发现理解起来都困难啊哭辽。

解法一:不使用dummy head,运行时间12ms

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* head = NULL;
        if (l1->val > l2->val) {
            head = l2;
            l2 = l2->next;
        }
        else {
            head = l1;
            l1 = l1->next;
        }
        
        ListNode* result = head; // 之前没想到
        
        while (l1 != NULL && l2 != NULL) {
            if (l1->val > l2->val) {
                result->next = l2;
                result = result->next;
                l2 = l2->next;
            }
            else {
                result->next = l1;
                result = result->next;
                l1 = l1->next;
            }
        }
        if (l1 != NULL) {
            result->next = l1;
        }
        if (l2 != NULL) {
            result->next = l2;
        }
        return head;
    }
};

这个代码是最接近我的思路的代码(因为不知道dummy head的存在),但是我最开始的时候一直没有想到在确定头节点(代码中为head)以后,还需要重新再设置一个ListNode(代码中为result)来进行接下来的操作,最后却要返回最初设置的head。现在大概搞懂了一点为什么要这么设置,第一个head是头指针,result为尾指针,每次在result = result->next以后就把前面的节点都丢了,而之前在result = head的时候相当于直接在head后面接上了result(?最后这句话真的很不确定了,没完全弄懂head为什么能把所有的都接在一起)。拿[1, 2, 4] [1, 3, 4]举例,result返回的是[4, 4],而head则是整个排序后的链表。另外在这里发现,把一个ListNode赋值到另一个ListNode中时,相当于把这个node后续接的所有node都赋值过去了。

参考链接:Loading...

解法二:使用dummy head,时间复杂度O(m+n),空间复杂度O(1),运行时间4ms

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* dummy = new ListNode(0);
        ListNode* cur = dummy;
        
        while (l1 != NULL && l2 != NULL) {
            if (l1->val > l2->val) {
                cur->next = l2;
                l2 = l2->next;
            }
            else {
                cur->next = l1;
                l1 = l1->next;
            }
            cur = cur->next;
        }
        
        if (l1 != NULL) {
            cur->next = l1;
        }
        else {
            cur->next = l2;
        }
        
        return dummy->next;
    }
};

看了一个youtube上的这道题的题解视频讲解的就是采用dummy head的,然后才有信心去看dummy head的代码,结果居然发现和我上面写的那个几乎一毛一样,只是随意设置了一个头节点,而不是先从两个list中仔细挑选出最小的那个作为头节点,因此最后返回的时候需要返回dummy->next因为头节点是瞎设的,而之前的方法就整个list都是我们需要的内容。

参考链接:Loading...

解法三:递归解法,时间复杂度O(m+n),空间复杂度O(m+n),运行时间4ms(m、n分别为两个list的长度)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == NULL) {
            return l2;
        }
        if (l2 == NULL) {
            return l1;
        }
               
        if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
};

完全没有想到递归能解决这个问题,看完这个做法真的觉得太优雅了!但是在初步了解递归的思路以后尝试自己写一遍代码,发现还是写不出来orz 需要再理顺一下这个思路:确定数值较小的node以后,接下来的list应该是merge这个node的下一个节点和另一个list的结果。在自己写代码的时候发现自己在递归方面的知识漏洞也很大,太多要补的东西了哇的一声哭出来QAQ

参考链接:Loading...[LeetCode] 21. Merge Two Sorted Lists 混合插入有序链表 - Grandyang - 博客园


链表真是太令人头大了,在谷歌的过程中也发现了一些还不错的介绍链表的资料,这里先贴出来,方便后续查找:

谈指神通:谈指神通 - SegmentFault 思否  这篇文章把链表归纳得特别详细和有条理,但是还没看orz

Linked List - 链表:Linked List · 数据结构与算法/leetcode/lintcode题解 · 看云  感觉这个网站非常强大,还有题目的详解,但是在我找链表介绍的这里感觉它介绍数据结构的文章并不是非常全面。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值