21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
思路:
循环对比两个链表当前值, 每次取较小的加入结果链表,循环后需要判断两个链表中是否存在一个没有遍历完,将剩余的链表加入结果链表,实现上可通过哑节点降低复杂度。
查看代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy=new ListNode();
ListNode* cur=dummy;
while(list1&&list2){
if(list1->val>list2->val){//取较小的作为下一个节点
cur->next=list2;
list2=list2->next;
}
else{
cur->next=list1;
list1=list1->next;
}
cur=cur->next;
}
if(list1!=nullptr){//是否有一个未完
cur->next=list1;
}
else if(list2!=nullptr){
cur->next=list2;
}
return dummy->next;
}
};
23. 合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i]
按 升序 排列lists[i].length
的总和不超过10^4
思路:
上一个的题合并两个链表的代码可以直接复制过来调用
法一:逐一合并,比如先合并list[0]+list[1]=>list[1]
,接下来list[1]+list[2]=>list[2]...
共合并K-1
次,且每次合并得到的链表会越来越长,与下一个链表合并时明显对比次数会很大。如第n
次合并得到的list[n]
和下一个链表list[n]
,长度很不均横。
复杂度分析来自官方题解
法二:
分治合并
示意图如下:
每轮两两合并,每轮合并后,下一轮需要合并的数量折半。
在实现上,设置步长(代表两个下标的距离) 每轮翻倍,interval
依次等于1,2,4...
,每次合并list[idx]和list[idx+internal]
。即list[0]
和list[0+1]
合并,下一轮list[0]
和list[0+2]
合并...
而idx
通过每次增加两倍步长更新。
查看代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
//上一题的函数
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy=new ListNode();
ListNode* cur=dummy;
while(list1&&list2){
if(list1->val>list2->val){//取较小的作为下一个节点
cur->next=list2;
list2=list2->next;
}
else{
cur->next=list1;
list1=list1->next;
}
cur=cur->next;
}
if(list1!=nullptr){//有一个未完
cur->next=list1;
}
else if(list2!=nullptr){
cur->next=list2;
}
return dummy->next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len=lists.size();
if(len==0)
return nullptr;
//每次两两合并
int interval=1;//步长
while(interval<len){//步长小于总长
for(int idx=0;idx<len;idx+=interval*2)//下标每次+2*步长
{
if(idx+interval<len){//当前下标加步长不能越界
//合并下标idx和idx+interval
lists[idx]=mergeTwoLists(lists[idx],lists[idx+interval]);
}
}
interval*=2;//步长每次*2
}
return lists[0];//合并完之后list[0]为结果
}
};
148. 排序链表
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
输入:head = []
输出:[]
提示:
- 链表中节点的数目在范围
[0, 5 * 104]
内 -105 <= Node.val <= 105
进阶:你可以在 O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
思路:
和上一题类似,采用分治合并,每轮两两合并,随着轮数增加需要合并的链表数量会折半,并且长度均衡增加。
调用第一题的合并函数,合并时自动排序。在实现上要注意指针移动的细节。
head = [4,2,1,3]
输出:[1,2,3,4]
4 -> 2 1 -> 3
|_____| |____| 每轮两两合并
| |
2->4 1->3
|________|
|
1->2->3->4
查看代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head==nullptr)
return head;
//求总长
int length = 0;
ListNode* node = head;
while(node!=nullptr){
length++;
node=node->next;
}
ListNode* dummy=new ListNode(0,head);
for(int interval=1;interval<length;interval*=2){//每轮步长翻倍
ListNode* pre=dummy;
ListNode* cur=dummy->next;
while(cur){//每轮中两两合并排序
//每次排序两个链表,先找到第一个链表
ListNode* head1=cur;
for(int i=1;i<interval&&cur->next;i++){
cur=cur->next;
}
//第一个链表找完了,cur下一个节点为第二个链表头,再从cur断开,将第一个链表分离出来
ListNode* head2=cur->next;
cur->next=nullptr;//断开
cur=head2;//恢复cur
//找第二个
for(int i=1;i<interval&&cur&&cur->next;i++){
cur=cur->next;
}
ListNode* next=nullptr;//记录cur的下一个节点,再从cur断开,将第二个链表分离出来
if(cur){
next=cur->next;
cur->next=nullptr;
}
ListNode* merged=mergeTwoLists(head1,head2);//合并
pre->next=merged;//cur更新为合并后的链表
//pre也要更新
while(pre->next){
pre=pre->next;
}
cur=next;//利用next恢复cur
}
}
return dummy->next;
}
//之前的函数
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy=new ListNode();
ListNode* cur=dummy;
while(list1&&list2){
if(list1->val>list2->val){//取较小的作为下一个节点
cur->next=list2;
list2=list2->next;
}
else{
cur->next=list1;
list1=list1->next;
}
cur=cur->next;
}
if(list1!=nullptr){//有一个未完
cur->next=list1;
}
else if(list2!=nullptr){
cur->next=list2;
}
return dummy->next;
}
};
标签:list2,ListNode,cur,list1,next,链表,升序,排序 From: https://www.cnblogs.com/fudanxi/p/17078562.html