题目详情
输入两个递增的链表,单个链表的长度为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;
}
};