前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >链表是有序的,如何快速合并呢?

链表是有序的,如何快速合并呢?

作者头像
程序员小熊
发布2021-08-13 16:58:29
5650
发布2021-08-13 16:58:29
举报
前言

大家好,我是来自于华为程序员小熊。今天给大家带来一道链表相关的题目,这道题同时也是字节、腾讯、亚马逊和微软等大厂的面试题,即力扣上的第21题-合并两个有序链表。

本文主要介绍递归迭代两种策略来解答此题,供大家参考,希望对大家有所帮助。

合并两个有序链表

代码语言:javascript
复制
将两个升序链表合并为一个新的升序链表并返回。

新链表是通过拼接给定的两个链表的所有节点组成的。 

示例1

示例及提示

解题思路

要想将两个升序链表合并成一个新的升序链表,比较容易想到通过递归去实现。

方法一:递归

采用递归的主要思路

假设链表分别为 A 和 B,先比较 A 和 B 的头节点的值的大小,选择头节点值较小者(假设为 A)作为新的链表的头节点;然后再比较 A 的第二个节点的值与 B 的头节点的值的大小关系,选择较小者作为新链表的第二个节点;依次类推找出新链表的第三个节点...

❝递归终止条件:两链表一个为空时,结束递归。 ❞

Show me the Code

C

代码语言:javascript
复制
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    /* 两个链表其中一个为空,递归结束 */
    if(l1 == NULL || l2 == NULL) {
        return l1 == NULL ? l2 : l1;
    }
        
    /* 其中一个链表的头节点的值小于另一个,则选值较小者作为新链表头节点 */    
    if(l1->val < l2->val) {
        /* 将该链表的剩余部分(子链表)跟另一个合并 */
        l1->next = mergeTwoLists(l1->next,l2);
        return l1;
    } else {
        l2->next = mergeTwoLists(l1,l2->next);
        return l2;
    }
}

C++

代码语言:javascript
复制
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    if(l1 == nullptr || l2 == nullptr) {
        return l1 == nullptr ? l2 : l1;
    }
        
    if(l1->val < l2->val) {
        l1->next = mergeTwoLists(l1->next,l2);
        return l1;
    } else {
        l2->next = mergeTwoLists(l1,l2->next);
        return l2;
    }
}

Java

代码语言:javascript
复制
ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    if(l1 == null || l2 == null) {
        return l1 == null ? l2 : l1;
    }
        
    if(l1.val < l2.val) {
        l1.next = mergeTwoLists(l1.next,l2);
        return l1;
    } else {
        l2.next = mergeTwoLists(l1,l2.next);
        return l2;
    }
}

Python3

代码语言:javascript
复制
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
    if l1 is None or l2 is None:
        return l2 if l1 is None else l1
    
    if l1.val < l2.val:
        l1.next = self.mergeTwoLists(l1.next,l2)
        return l1
    else:
        l2.next = self.mergeTwoLists(l1,l2.next);
        return l2;

Golang

代码语言:javascript
复制
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
    if l1 == nil {
        return l2
    } else if l2 == nil {
        return l1
    }

    if l1.Val < l2.Val {
        l1.Next = mergeTwoLists(l1.Next, l2)
        return l1
    } else {
        l2.Next = mergeTwoLists(l1, l2.Next)
        return l2
    }
}

复杂度分析

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

空间复杂度:O(n + m)

方法二:迭代

除了采用递归外,还可以采用迭代的方法,具体如何操作,如下例子所示:

举例

以链表l1: 1->4->null 和链表l2: 2->3->null 为例。

例子

设置两个指针 cur1 和 cur2,分别指向两个链表的头节点;

设置指针

比较 cur1 和 cur2 指向的节点的值的大小,右移指向的节点值较小的 cur1;

比较节点值大小,并移动节点值较小的指针

设置一个指针 pre1,记录上次比较时值较小的节点;

设置一个节点,记录上一步右移前其指向的节点

设置一个指针 next,记录 cur2 指向节点的下一节点;

记录 cur2 下一节点

由于 pre1 指向的节点值小于 cur2 指向的节点值,连接它们;

新链表的头节点与第二个节点连接

同时由于 cur2 指向的节点小于 cur1 指向的节点,连接它们;

连接新链表

右移 pre1 和 cur2 分别到 cur2 和 next 位置;

移动指针

由于 cur1 指向的节点值大于 cur2 指向的节点值,将 pre1 指向的节点连接 cur2 指向的节点;

比较判断,重新获取第三个节点

由于 cur2 指向的节点值小于 cur1 指向的节点值,连接它们;

获取新链表的第四个节点

采用迭代法,合并两个上升链表到一个的完整过程,如下动图示:

迭代法的完整操作过程

Show me the Code

C

代码语言:javascript
复制
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    if (l1 == NULL || l2 == NULL) {
        return l1 != NULL ? l1 : l2;
    }

    struct ListNode* mergeHead = l1->val > l2->val ? l2 : l1;  
    struct ListNode* cur1 = mergeHead == l1 ? l1 : l2;         
    struct ListNode* cur2 = mergeHead == l1 ? l2 : l1;
    /* 记录上次比较时节点值小的节点 */
    struct ListNode* pre = NULL;  
    /* 记录当前节点值大的节点的下一节点 */                              
    struct ListNode* next = NULL;
    while (cur1 != NULL && cur2 != NULL) {
        if (cur1->val <= cur2->val) {
            pre = cur1;
            cur1 = cur1->next;
        } else {
            next = cur2->next;  
            /* 将值较小的节点连接到另一链表节点值大的节点 */
            pre->next = cur2;   
            /* 将另一链表中节点值大的节点连接到值较小的节点的下一节点 */
            cur2->next = cur1;  
            pre = cur2;
            cur2 = next;            
        }
    }
    /* 合并后 l1 和 l2 最多有一个未被合并完,链表末尾指向未合并完的链表 */
    pre->next = cur1 == NULL ? cur2 : cur1;
    return mergeHead;
}

C++

代码语言:javascript
复制
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    if (l1 == nullptr || l2 == nullptr) {
        return l1 != nullptr ? l1 : l2;
    }

    ListNode* pre = nullptr;                              
    ListNode* next = nullptr;
    ListNode* mergeHead = l1->val > l2->val ? l2 : l1;  
    ListNode* cur1 = mergeHead == l1 ? l1 : l2;         
    ListNode* cur2 = mergeHead == l1 ? l2 : l1;        
    while (cur1 != nullptr && cur2 != nullptr) {
        if (cur1->val <= cur2->val) {
            pre = cur1;
            cur1 = cur1->next;
        } else {
            next = cur2->next;  
            pre->next = cur2;   
            cur2->next = cur1;  
            pre = cur2;
            cur2 = next;            
        }
    }

    pre->next = cur1 == nullptr ? cur2 : cur1;
    return mergeHead;
}

Java

代码语言:javascript
复制
ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    if (l1 == null || l2 == null) {
        return l1 != null ? l1 : l2;
    }

    ListNode pre = null;                              
    ListNode next = null;
    ListNode mergeHead = l1.val > l2.val ? l2 : l1;  
    ListNode cur1 = mergeHead == l1 ? l1 : l2;         
    ListNode cur2 = mergeHead == l1 ? l2 : l1;        
    while (cur1 != null && cur2 != null) {
        if (cur1.val <= cur2.val) {
            pre = cur1;
            cur1 = cur1.next;
        } else {
            next = cur2.next;  
            pre.next = cur2;   
            cur2.next = cur1;  
            pre = cur2;
            cur2 = next;            
        }
    }

    pre.next = cur1 == null ? cur2 : cur1;
    return mergeHead;
}

复杂度分析

时间复杂度:O(n + m),其中 n 和 m 分别为两个链表的长度,需要遍历两个链表的每个节点。

空间复杂度:O(1),未开辟额外空间。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员小熊 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 合并两个有序链表
  • 解题思路
  • 方法一:递归
    • Show me the Code
    • 方法二:迭代
      • Show me the Code
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档