【Leetcode刷题笔记之链表篇】21. 合并两个有序链表

😈博客主页:🐼大家好我叫张同学🐼
💖 欢迎点赞 👍 收藏 💗留言 📝 欢迎讨论! 👀
🎵本文由 【大家好我叫张同学】 原创,首发于 CSDN 🌟🌟🌟
精品专栏(不定时更新) 【数据结构+算法】 【做题笔记】【C语言编程学习】
☀️ 精品文章推荐
【C语言进阶学习笔记】三、字符串函数详解(1)(爆肝吐血整理,建议收藏!!!)
【C语言基础学习笔记】+【C语言进阶学习笔记】总结篇(坚持才有收获!)


前言

为什么要写刷题笔记
写博客的过程也是对自己刷题过程的梳理总结,是一种耗时有效的方法。
当自己分享的博客帮助到他人时,又会给自己带来额外的快乐和幸福。
(刷题的快乐+博客的快乐,简直是奖励翻倍,快乐翻倍有木有QAQ🙈)

题目内容

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

在这里插入图片描述
原题链接(点击跳转)

暴力求解法

我们可以将list2选为合并后待返回的链表,然后将list1中的结点合并到list2中。

算法图解

在这里插入图片描述

函数实现
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode *cur1 = list1,*cur2 = list2;
    //以list2作为最终合并后的链表,当list2为空表,直接返回list1
    if(cur2 == NULL)
        return list1;
    while(cur1){
     //先保存cur1的下个结点位置,避免丢失
    struct ListNode* next1 = cur1->next,*next2 = cur2->next;
         while(next2 && cur2->val < cur1->val && next2->val < cur1->val){
            cur2 = next2;
            next2 = cur2->next;
           }
        if(cur2->val > cur1->val){//头插
            cur1->next = cur2;
            list2 = cur1;
            cur2 = list2;
            cur1 = next1;
            continue;
       }
        cur1->next = cur2->next;
        cur2->next = cur1;
        cur1 = next1;
    }
    return list2;
}

注意:在实际写代码的时候,我们会发现仅有一个next用来保存cur1的下一个结点是不够的。cur2的下一个结点的信息也需要保存(假设下一个结点不为空,若为空直接cur1结点插入cur2后即可),因为在比较的时候,当cur2->val小于cur1->valcur2->next->val大于cur1->val时(cur2->next->val就是next2->val),才能将list1cur1指向的结点插入到list2中。否则,cur2需要向后移动,直到满足条件为主。
在这里插入图片描述

小技巧
很多OJ题的测试用例非常多,即便我们画图分析了,可能还有很多场景没能想到。这个时候就需要借助OJ中错误的测试用例来帮助我们完善调整优化代码。

在这里插入图片描述
在这里插入图片描述

这道题目很多场景我都是通过失败的测试用例才发现的,比如说:
list1不为空,list2为空,这时候需要直接返回list1,无需合并。
list1中的很多结点一开始就小于list2中的结点,这个时候就不能仅用尾插或者中间插入实现,就需要头插~

整体而言,这种暴力求解的方式写起来并不轻松,有时候需要考虑的条件和场景可能比其他方式更多!!!
接下来我们将学习一种更简单、更易于实现的方式~


新建链表法

为了方便合并链表,我们可以新建一个链表newlist,然后每次从list1list2中将val值小的结点尾插newlist中。

算法图解

在这里插入图片描述

函数实现
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* newlist = NULL,*tail = newlist;
    struct ListNode *cur1 = list1,*cur2 = list2;
    if(list1 == NULL && list2 == NULL)
         return NULL;
    else if(list1 == NULL)
         return list2;
    else if(list2 == NULL)
         return list1;
    while(cur1 && cur2){
        struct ListNode* next1 = cur1->next;
        struct ListNode* next2 = cur2->next;
        if(cur1->val <= cur2->val){
            if(tail == NULL){//newlist为NULL单独处理
                cur1->next = tail;
                newlist = cur1;
                tail = newlist;
            }
            else{
                cur1->next = tail->next;
                tail->next = cur1;
                tail = tail->next;
            }
            cur1 = next1;
        }
        else{
                if(tail == NULL){//newlist为NULL单独处理
                cur2->next = tail;
                newlist = cur2;
                tail = newlist;
            }
            else{
                cur2->next = tail->next;
                tail->next = cur2;
                tail = tail->next;
            } 
            cur2 = next2;
        }
    }
    if(cur1)
       tail->next = cur1;
    else
       tail->next = cur2;
    return newlist;
}

在这里插入图片描述
这种方式比暴力求解的方法好了很多,至少没有那么多场景(什么时候要头插,什么时候中间插入等等)需要考虑,但是仍然有优化的空间。我们可以看到当新建的链表newlist为不带哨兵位guard的链表时,每次都需要去考虑newlist == NULL这个条件,真是烦死人啦!为此,我们对以上代码进行优化,创建一个带哨兵位(带头)的链表newlist

代码优化
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    //创建哨兵位头节点,不存放有效数据
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* newlist = guard,*tail = newlist;
    struct ListNode *cur1 = list1,*cur2 = list2;
    if(list1 == NULL && list2 == NULL)
         return NULL;
    else if(list1 == NULL)
         return list2;
    else if(list2 == NULL)
         return list1;
    while(cur1 && cur2){
        struct ListNode* next1 = cur1->next;
        struct ListNode* next2 = cur2->next;
        if(cur1->val <= cur2->val){
            cur1->next = tail->next;
            tail->next = cur1;
            tail = tail->next;
            cur1 = next1;
        }
        else{
            cur2->next = tail->next;
            tail->next = cur2;
            tail = tail->next;
            cur2 = next2;
        }
    }
    if(cur1)
       tail->next = cur1;
    else
       tail->next = cur2;
    //释放哨兵位头节点
    newlist = newlist->next;
    free(guard);
    return newlist;
}

在这里插入图片描述


原创不易,求点赞+关注+收藏~
  • 20
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大家好我叫张同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值