数据结构与算法|链表基础之力扣leetcode21合并两个有序链表(cpp、Java实现)

21 合并两个有序链表

题源:力扣21

力扣

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

解决思路与数组一样,一般有两种。一种是新建一个链表,然后分别遍历两个链表,每次都选最小的结点接到新链表上,最后排完。另外一个就是将一个链表结点拆下来,逐个合并到另外一个对应位置上去。这个过程本身就是链表插入和删除操作的拓展,难度不算大,这时候代码是否优美就比较重要了。

方法一:递归

  1. 判断两个链表是否有一个为空,如果有,则返回另一个链表。
  2. 比较两个链表当前节点的值,将较小的节点作为新链表的头节点。
  3. 将较小节点的下一个节点和较大节点递归地进行合并,得到的链表连接到较小节点后面。
  4. 返回新链表的头部。

以下是cpp代码:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr) {
            return l2;
        } else if (l2 == nullptr) {
            return l1;
        } else if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        } else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
};

以下是Java代码:

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        } else if (l2 == null) {
            return l1;
        } else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

Java和cpp在这个程序中的两个区别就是Java用null,cpp用nullptr;Java用l1.val,cpp用l1->val,即指针表示方法有些不同

为什么需要考虑边界情况呢?这样做是为了确保我们的代码在处理不同情形时是安全可靠的。在这个函数中,边界情况指的是当两个链表中至少有一个为空时:l1为空或者l2为空。在这种情况下,我们只需要返回另一个链表即可,因为没有多余的节点可以合并。这样的判断有利于处理特殊情况,确保代码的正常执行。

方法二:迭代(新建一个链表)(循环+双链表)

  1. 判断两个链表是否有一个为空,如果有,则返回另一个链表。
  2. 比较两个链表当前节点的值,将较小的节点作为新链表的头节点。
  3. 将较小节点的下一个节点和较大节点递归地进行合并,得到的链表连接到较小节点后面。
  4. 返回新链表的头部。

从头开始比较两个链表的节点值,将较小的节点添加到新链表中,然后移动指针继续比较,直到其中一个链表遍历完,将剩余的节点直接连接到新链表的末尾。

以下是Java代码:

public ListNode mergeTwoLists (ListNode list1, ListNode list2) {
     ListNode newHead=new ListNode(-1);
     ListNode res=newHead;
     while(list1!=null||list2!=null){ 
         //情况1:都不为空的情况
         if(list1!=null&&list2!=null){
             if(list1.val<list2.val){
                 newHead.next=list1;
                 list1=list1.next;
             }else if(list1.val>list2.val){
                 newHead.next=list2;
                 list2=list2.next;
             }else{ //相等的情况,分别接两个链
                 newHead.next=list2;
                 list2=list2.next;
                 newHead=newHead.next;
                 newHead.next=list1;
                 list1=list1.next;
             }
             newHead=newHead.next;
          //情况2:假如还有链表一个不为空
         }else if(list1!=null&&list2==null){
             newHead.next=list1;
             list1=list1.next;
             newHead=newHead.next;
         }else if(list1==null&&list2!=null){
             newHead.next=list2;
             list2=list2.next;
             newHead=newHead.next;
         }
     }
     return res.next;
 }

代码优化:

优化一:第一个大while里有三种情况,我们可以将其合并成两个

优化二:后面两个小的while循环

以下是Java代码:

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode prehead = new ListNode(-1);
        ListNode prev = prehead;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                prev.next = list1;
                list1 = list1.next;
            } else {
                prev.next = list2;
                list2 = list2.next;
            }
            prev = prev.next;
        }
        // 最多只有一个还未被合并完,直接接上去就行了,这是链表合并比数组合并方便的地方
        prev.next = list1 == null ? list2 : list1;
        return prehead.next;
    }

以下是cpp代码:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* preHead = new ListNode(-1);
        ListNode* prev = preHead;
        while (l1 != nullptr && l2 != nullptr) {
            if (l1->val < l2->val) {
                prev->next = l1;
                l1 = l1->next;
            } else {
                prev->next = l2;
                l2 = l2->next;
            }
            prev = prev->next;
        }
        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev->next = l1 == nullptr ? l2 : l1;
        return preHead->next;
    }
};

方法三:优先队列

  1. 创建一个优先队列,将两个链表的所有节点依次加入队列,队列会按节点的值进行排序。
  2. 创建一个新的链表,并从队列中逐个弹出节点,将弹出的节点加入到新链表的尾部。
  3. 返回新链表的头部。

【笔者还没学到优先队列,就不列代码了】

总结

对于将两个升序链表合并的问题,以下是三种常见的解决方法:

方法一:迭代法

  1. 初始化一个新的链表,并设立一个指针指向新链表的头部。
  2. 比较两个链表当前节点的值,将较小的节点添加到新链表中,并将指针移到下一个位置。
  3. 重复步骤2,直到其中一个链表遍历完。
  4. 将剩下的链表连接到新链表的后面。
  5. 返回新链表的头部。

方法二:递归法

  1. 判断两个链表是否有一个为空,如果有,则返回另一个链表。
  2. 比较两个链表当前节点的值,将较小的节点作为新链表的头节点。
  3. 将较小节点的下一个节点和较大节点递归地进行合并,得到的链表连接到较小节点后面。
  4. 返回新链表的头部。

方法三:优先队列

  1. 创建一个优先队列,将两个链表的所有节点依次加入队列,队列会按节点的值进行排序。
  2. 创建一个新的链表,并从队列中逐个弹出节点,将弹出的节点加入到新链表的尾部。
  3. 返回新链表的头部。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值