LeetCode21 - 合并两个有序链表

目录

方法一、暴力解法

1 - 解题思路

2 - 解题方法

3 - Code

 4 - 复杂度

5 - 总结

方法二、递归

1 - 解题思路

2 - 解题方法

3 - Code

4 - 复杂度

5 - 总结


方法一、暴力解法

1 - 解题思路

2 - 解题方法

3 - Code

4 - 复杂度

5 - 总结

1 - 解题思路

题目给出两个链表(不带头结点)首节点指针list1和list2。

/*

 Definition for singly-linked list.

 struct ListNode {

     int val;

     struct ListNode *next;

 };

 */

两个链表都是升序的,可以在原有的空间做修改,不需要创建新的空间,将两个链表连接起来,使用暴力解法需要对每个元素逐个比较。

链表问题考虑使用哑结点。

2 - 解题方法

创建两个结点指针 preprev和prev指向新的哑结点,另外两个指针list1和list2分别指向两个当前正在比较的结点。

通过修改prev->next将新的链表连接起来,prev->next指向较小的结点,然后修改prev,使其指向该结点。

同时修改list指向下一个结点,为下一次比较做准备。

循环终止条件是当list1或list2指向为NULL,此时让prev指向此时不为空的list即可。

函数返回prepre->next(即最终链表的首节点);

3 - Code

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
   struct ListNode* preprev=(struct ListNode* )malloc(sizeof(struct ListNode));
   struct ListNode* prev=preprev;
    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 preprev->next;
}

 4 - 复杂度

①时间复杂度:O(n)

②空间复杂度:O(1)

5 - 总结

①链表问题可以考虑使用哑结点(一般哑结点放在头结点和首节点之间,使每一个结点都有一个前驱结点,方便操作)。

方法二、递归

1 - 解题思路

2 - 解题方法

3 - Code

4 - 复杂度

5 - 总结

1 - 解题思路

需要解决的问题可以分成若干相同的子问题,因此可以用递归的算法求解。

list1和list2分别指向两个链表,假设list1指向的结点为较小的一个,那么此时要做的就是运算   list1-next=mergeTwoLists(list1->next,list2),即保留第一个结点为list1,接下来把list1后面的链表与list2合并起来再一次调用该函数。

2 - 解题方法

①这个问题的子问题是什么。合并链表。

②当前层要干什么事情。找到两个结点较小的一个,加入当前链表,并将其后续链表与另一个链表再递归一次,合并起来。

递归出口。①list1为空时②list2为空时

想清楚这几点就好啦。 很多刚接触递归的同学习惯往深处想,就想想清楚下一层,下下层到底咋回事,千万别!这样永远也想不清楚的,你只要把当前层的事情干好,边界条件找好,深层自然而然就是对的。千万别想那么深。

④调用递归步骤

/*(1,1):代表第一次进入递归函数,并且从第一个口进入,并且记录进入前链表的状态

merge(1,1): 1->4->5->null, 1->2->3->6->null             list1->next = (list1 ->next,list2)

    merge(2,2): 4->5->null, 1->2->3->6->null              list2->next = (list2 ->next,list1)

        merge(3,2): 4->5->null, 2->3->6->null               list2->next = (list2 ->next,list1)

            merge(4,2): 4->5->null, 3->6->null                list2->next = (list2 ->next,list1)

                merge(5,1): 4->5->null, 6->null                 list1->next = (list1 ->next,list2)

                    merge(6,1): 5->null, 6->null                  list1->next = (list1 ->next,list2)

                        merge(7): null, 6->null                      list1->next = (list1 ->next,list2)

                        return l2                                            递归出口

                    l1.next --- 5->6->null, return l1  

                l1.next --- 4->5->6->null, return l1  

            l2.next --- 3->4->5->6->null, return l2

        l2.next --- 2->3->4->5->6->null, return l2

    l2.next --- 1->2->3->4->5->6->null, return l2

l1.next --- 1->1->2->3->4->5->6->null, return l1*/

3 - Code

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    if(list1==NULL)
    return list2;
    else if(list2==NULL)
    return list1;

    else if(list1->val<list2->val)
    {
        list1->next=mergeTwoLists(list1->next,list2);
        return list1;
    }
    
    else
    {
         list2->next=mergeTwoLists(list2->next,list1);
         return list2;
    }
}

 

4 - 复杂度

①时间复杂度:O(n)

②空间复杂度:O(n)

5 - 总结

递归函数必须要有终止条件,否则会出错;
递归函数先不断调用自身,直到遇到终止条件后进行回溯,最终返回答案。

终止条件:当两个链表都为空时,表示我们对链表已合并完成。
如何递归:我们判断 l1 和 l2 头结点哪个更小,然后较小结点的 next 指针指向其余结点的合并结果。(调用递归)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值