合并两个排序的链表 | 牛客刷题 | 剑指offer JZ25

题目详情

输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0≤n≤10000≤n≤1000,−1000≤节点值≤1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:
示意图

解题详解

笔者希望能将一串链表有序拼接到另一个链表上,只需要返回最后的整条链表即可!
下个定义,小串 → \rightarrow 第一位数字最小是两串链表中的最小值【一定是合并后的最小值】,大串 → \rightarrow 第一个数字较大~【一定不是合并后的最小值】
【前插法】将大串的数值跟小串的数值进行比对,比小串小的值就插入到小串中!
这个方法有一点不好就是——容易造成越界访问
我们能确定拼接后整串链表的大小就是2n,但是无法确定合并之后最大值是在小串smaller里还是大串bigger里。
👊 尾部处理一定要干净利落,不能【越界】!
分情况讨论
1️⃣ 【最大值在大串bigger小串smaller先遍历完,在检查到小串的next==null并且 小串smaller 的最大值比 大串bigger 当前指针所指的值要,直接将剩余的 大串bigger 移植到 小串smaller 后边即可。
2️⃣ 【最大值在小串smaller大串bigger先遍历完,就已经合并完成了。
剩下的就是对比值之后,将大串bigger的值插入到小串smaller里即可。
指针操作一定小心,【先断再连接】的先后次序一定搞清楚。

完整代码

  • 自己写的【绞尽脑汁】
/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 *  ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
  public:
    /**
     * @param pHead1 ListNode类
     * @param pHead2 ListNode类
     * @return ListNode类
     */
    ListNode* mer(ListNode* smaller, ListNode* bigger) {
        ListNode* p1 = smaller, *p2 = bigger;
        int n = 0;
        while (p1 != nullptr) {
            p1 = p1->next;
            n++;
        }
        p1 = smaller;
        for (int i = 0; i < 2*n-1; i++) {
            //p1可能越界!!【尾部处理】
            if(p1->next==nullptr && p1->val <= p2->val){//指向倒数第二个
                p1->next=p2;
                break;
            }
            if(p2==nullptr){
                break;
            }
            if (p2->val <= (p1->next)->val ) {
                cout << "插入" << endl;
                if(bigger->next!=nullptr){
                    bigger = bigger->next; //大串后移一位
                }
                else{
                    bigger=nullptr;//大串结束了,置空
                }
                //拼接
                p2->next = p1->next;
                p1->next = p2;
                //大串指针归位
                p2 = bigger;
            }
            p1 = p1->next;
            
        }
        return smaller;
    }
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        // write code here
        //p1 and p2 都为空;p1不为空,p2为空
        if ((pHead1 == nullptr && pHead2 == nullptr) || (pHead1 != nullptr &&
                pHead2 == nullptr)) {
            return pHead1;
        }
        //p1为空,p2不为空
        if (pHead2 != nullptr && pHead1 == nullptr) {
            return pHead2;
        }

        ListNode* smaller = nullptr, *bigger = nullptr;
        //先判断哪一个链表的头部最小
        if (pHead1->val < pHead2->val) {
            smaller = pHead1;
            bigger = pHead2;
            
        } else {
            smaller = pHead2;
            bigger = pHead1;
        }
        mer(smaller,bigger);
        return smaller;
    }
};
  • 大佬写的
    十分干净!💪强烈推荐!
    分析一下就是,开辟一个堆空间来放这一串合并过后的链表。
    相当于说,创建了一个新的链表pHead来指向这个合并过后的链表。
    并且还初始化了一个操作指针cur
    由于链表有序!我们可以直接对两个链表进行遍历,小的那一个分给cur
    通过cur的移动来控制整条有序链表的拼接。
    • 最有意思的【尾部处理】
      注意看while的判断条件是任意一个链表遍历完成后,直接结束循环。
      ❓那另一个链表怎么办?
      另外一个的尾部必然比已经结束的那一个链表要大,直接将剩下的接到cur上就好了!
      cur->next=pHead1?pHead1:pHead2;
      
      这个三目运算符运用的很巧妙啊!!❗️
      🌻condition ? case1 : case2 当条件condition为真时,取冒号:前面的第一种情况case1,反之取第二种情况case2
      ✅如果pHead1已经遍历完成,说明pHead1是空指针,返回值为0,也就是判断为假,直接取用pHead2拼接到尾部,反之同理。
/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        ListNode* pHead=new ListNode(-1);
        ListNode* cur=pHead;
        while(pHead1&&pHead2){
            if(pHead1->val<=pHead2->val){
                cur->next=pHead1;
                pHead1=pHead1->next;
            }
            else{
                cur->next=pHead2;
                pHead2=pHead2->next;
            }
            cur=cur->next;
        }
        cur->next=pHead1?pHead1:pHead2;
        return pHead->next;
    }
   
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值