算法热门:合并两个有序链表(LeetCode 21)

这是链表系列的第三篇博文,本系列主要介绍链表相关的热门算法题,今天带来的是:《合并两个有序链表》;
话不多说,直接看题——
算法热门:合并两个有序链表(LeetCode 21)_第1张图片

题目解读

我们要合并的链表为l1和l2,它们有一个特点就是:非递减排序,这句话的言外之意就是——可能存在两个相同的数字在一个链表中;同时要求,l1和l2合并成升序链表;

思路简介

看到这道题,很容易回想起前些天我们做的反转单链表的解法二——
(算法热门)反转单链表(解法二)
那么很明显,今天我们这道题也可以延续这样的做法,即创建一个单链表,将两个链表中的结点一一比较大小,小的则将其接到新联表中,然后该结点所在链表的遍历指针往后移一位,也就是让它指向下一个节点,如此循环往复……
当某一个链表的遍历指针为空(NULL)时,那么剩下的那个链表也就可以直接接到新链表中(因为给我们的两个链表就是排序好的),那么我们的任务就完成啦;

图解思路

算法热门:合并两个有序链表(LeetCode 21)_第2张图片

这是题目给出的示例1,下面我们就来解剖它合并的过程——
首先比较两个链表的一号节点,假设判断条件为:

if(p1->val < p2->val)

那么p2就会尾插到新链表,且p2向后移一个——
算法热门:合并两个有序链表(LeetCode 21)_第3张图片
接下来就是重复劳动啦——
算法热门:合并两个有序链表(LeetCode 21)_第4张图片
算法热门:合并两个有序链表(LeetCode 21)_第5张图片
算法热门:合并两个有序链表(LeetCode 21)_第6张图片
算法热门:合并两个有序链表(LeetCode 21)_第7张图片
最后!
算法热门:合并两个有序链表(LeetCode 21)_第8张图片
注:图中有一些缺憾就是,新链表的尾部应该有一个NULL;

代码实现

好的,分析了这么多,代码实现一波——

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
    if(!l1)//l1为空链表
        return l2;
    if(!l2)//l2为空链表
        return l1;
    struct ListNode *newhead,*tail;
    if(l1->val < l2->val)
    {
        newhead=tail=l1;
        l1=l1->next;
    }
    else
    {
        newhead=tail=l2;
        l2=l2->next;
    }
    while(l1 && l2)//迭代比较,直到某一链表为空为止
    {
        if(l1->val < l2->val)
        {
            tail->next=l1;
            l1=l1->next;
        }
        else
        {
            tail->next=l2;
            l2=l2->next;
        }
        tail=tail->next;
    }
    tail->next = l1?l1:l2;//把非空的链表接到tail后
    return newhead;
}

同时分享一个带哨兵位的写法——

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
    if(!l1)//l1为空链表
        return l2;
    if(!l2)//l2为空链表
        return l1;
    struct ListNode* newlist=(struct ListNode*)malloc(sizeof(struct ListNode)),*cur=newlist;
    while(l1 && l2)
    {
        if(l1->val<l2->val)
        {
            cur->next=l1;
            l1=l1->next;
        }
        else
        {
            cur->next=l2;
            l2=l2->next;
        }
        cur=cur->next;
    }
    cur->next= l1?l1:l2;
    struct ListNode* ret=newlist->next;
    return ret;
}

这种写法的好处就是:不用先比较l1和l2以确定新链表的头指针!

小结一下

值得注意的是,我们新链表的tail一定要和newhead初始化为同一个值,即newtail=tail=l1或者l2;
如果tail初始化为newhead->next,那么由于newhead开始的时候指向l1或l2,则tail会指向l1或l2的下一个元素,此时再如此迭代下去就会出现一个现象:某一个链表因为tail指向的next的改变,而改变,从而l1或l2会因此断开;
换句话说就是,我们的新链表应当做到和l1与l2“零关联”,故而tail也要与l1和l2无关。

创作不易,留个点赞评论再走吧~关注博主,分享更多学习、刷题经验

你可能感兴趣的:(链表,算法,单链表,数据结构,面试)