• 【面试经典150 | 链表】合并两个有序链表


    Tag

    【递归】【迭代】【链表】


    题目来源

    21. 合并两个有序链表


    题目解读

    合并两个有序链表


    解题思路

    一种朴素的想法是将两个链表中的值存入到数组中,然后对数组进行升序排序,最后将排序好的数组还原回链表,这是一种可行的思路,但是没有充分利用题目已知的两个链表有序的条件,大家可以自行尝试,练习基础语法与建立链表节点的知识。

    方法一:递归

    我们记两个链表的头节点分别为 l1l2,在合并两个链表的时候会遇到以下三种情况:

    • l1 为空,直接返回 l2
    • l2 为空,直接返回 l1;
    • 两节点都不为空,那么又会分为两种情况:
      • l1 节点值小于 l2 节点值,那么 l1 节点将会是合并后的节点新的头节点,剩下的部分是 l1->nextl2 合并的节点,而合并 l1->nextl2 是合并 l1l2 的子问题,也可以使用 mergeTwoLists 函数来解答,于是有 l1->next = mergeTwoLists(l1->next, l2),并返回 l1
      • 同理,l2 节点值小于 l1 节点值时,有 l2->next = mergeTwoLists(l1, l2->next),并返回 l1
    • 以上这种将问题转换为原问题的子问题的方法,称为递归方法。递归方法是一种边调用边填充的方法。

    实现代码

    C++

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
            if (list1 == nullptr) {
                return list2;
            }
            else if (list2 == nullptr) {
                return list1;
            }
            else if (list1->val < list2->val) {
                list1->next = mergeTwoLists(list1->next, list2);
                return list1;
            }
            else {
                list2->next = mergeTwoLists(list1, list2->next);
                return list2;
            }
        } 
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    python3

    # 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, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
            if l1 is None:
                return l2
            elif l2 is None:
                return l1
            elif l1.val < l2.val:
                l1.next = self.mergeTwoLists(l1.next,l2)
                return l1
            else:
                l2.next = self.mergeTwoLists(l1, l2.next)
                return l2        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    复杂度分析

    时间复杂度: O ( m + n ) O(m+n) O(m+n) m m m n n n 分别为两个链表的长度,每个节点都是被递归调用一次。

    空间复杂度: O ( m + n ) O(m+n) O(m+n),每调用一次函数 mergeTwoLists 都需要消耗栈空间,栈空间的大小取决于递归调用的深度。

    方法二:迭代

    迭代的方法是一种相对容易理解的方法。为了方便实现,我们定义一个哑节点 dummyListNode* dummy = new ListNode(-1);,最后只需要返回 dummy->next

    我们再定义一个节点 prev 用来指向当前值较小的节点,初始 prev = dummy,我们迭代枚举两链表中的节点:

    • prev 指向值较小的节点;
    • 值较小的节点更新为下一个节点,方便下一对节点的比较;
    • prev 更新为下一个节点,为存放下一个更小的节点做准备;
    • 如果有一个链表为空了,直接退出迭代循环;
    • 需要注意这时候,两个链表中可能还有一个链表非空,需要将剩下的非空部分接在 prev 后面。

    以上就是本题的迭代方法。

    实现代码

    C++

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
            ListNode* dummy = new ListNode(-1);
            ListNode* prev = dummy;
            
            while(list1 && list2){
                if(list1->val < list2->val){
                    prev->next = list1;
                    list1 = list1->next;
                }
                else{
                    prev->next = list2;
                    list2 = list2->next;
                }
                prev = prev->next;
            }
            prev->next = list1 ? list1 : list2;
            return dummy->next;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    python3

    # 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, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
            dummy = ListNode(-1)
    
            prev = dummy
            while l1 and l2:
                if l1.val < l2.val:
                    prev.next = l1
                    l1 = l1.next
                else:
                    prev.next = l2
                    l2 = l2.next
                prev = prev.next
            
            prev.next = l1 if l1 is not None else l2
            return dummy.next
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    复杂度分析

    时间复杂度: O ( m + n ) O(m+n) O(m+n) m m m n n n 分别为两个链表的长度,每个节点都是被递归调用一次。

    空间复杂度: O ( 1 ) O(1) O(1)


    写在最后

    如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

    如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

    最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。

  • 相关阅读:
    java-net-php-python-springboot基于SpringBoot的OA办公管理系统计算机毕业设计程序
    EXTJS 中grid 动态增加列的方法
    交换两个数的值 -- Java实现(加深对引用对象的理解)
    C++11绑定器bind及function机制
    classification_report加入topk计算
    【Excel】WPS单元格快速转换表格字母大小写
    力扣上《将数字变成0的操作次数》这道题num & 0x01和num >>= 1的讲解
    c语言之volatile关键字
    2022中国大健康展,山东大健康展,济南健康展,健康产业展
    如何按照一定的需求进行开发ALV报表
  • 原文地址:https://blog.csdn.net/weixin_54383080/article/details/134039776