1. 合并 K 个升序链表
解题思路:
- 暴力
O(N*K^2):一次合并两个有序链表,循环操作,直至所有链表合并为一个。虽然这种方法简单易想,但一旦K很大,时间复杂度就会特别高。
一个一个链表合并过于冗余,那能不能一次性合并所有链表呢?答案是可以的:每次选择K个链表中头节点值最小 的元素加入链表即可。因为链表都是有序的,因此我们考虑用堆的思想优化:
- 堆
O(N*K*logK):创建一个小根堆 ,放入所有的头节点 ,在合并链表时,每次选取堆顶元素尾插,另找一个头节点放入堆中。堆为空则说明所有的链表都已被合并了。

- 分治+递归
O(N*K*logK):在逐⼀⽐较时,答案链表会变得越来越**⻓** ,每个跟它合并的⼩链表的元素都需要⽐较很多次 才可以成功排序。因此,我们也可以用分治+递归 的方式优化链表长度,尽可能让长度相同的链表进行两两合并。

cpp
//堆解法
class Solution {
public:
typedef ListNode Node;
struct cmp //需要提供比较逻辑
{
bool operator()(Node* n1, Node* n2)
{
return n1->val > n2->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists)
{
priority_queue<Node*, vector<Node*>, cmp> heap;
//放入头节点
for(auto& l : lists)
{
if(l)
{
heap.push(l);
}
}
Node* newhead = new Node(0);
Node* cur = newhead;
while(!heap.empty())
{
Node* tmp = heap.top();
heap.pop();
cur->next = tmp;
if(tmp->next)
{
heap.push(tmp->next);
}
cur = cur->next;
}
Node* ret = newhead->next;
delete newhead;
return ret;
}
};
cpp
//递归+分治解法
class Solution {
public:
typedef ListNode Node;
ListNode* mergeKLists(vector<ListNode*>& lists)
{
int n = lists.size();
if(n == 0) return nullptr;
if(n == 1) return lists[0];
return Merge(lists, 0, n - 1);
}
Node* Merge(vector<ListNode*>& lists, int left, int right)
{
if(left == right) return lists[left];
if(left > right) return nullptr;
int mid = (left + right) / 2;
Node* n1 = Merge(lists, left, mid);
Node* n2 = Merge(lists, mid + 1, right);
return Merge_Sort(n1, n2);
}
Node* Merge_Sort(Node* n1, Node* n2)
{
if(n1 == nullptr) return n2;
if(n2 == nullptr) return n1;
Node* cur1 = n1;
Node* cur2 = n2;
Node* newhead = new Node(0);
Node* cur = newhead;
while(cur1 && cur2)
{
if(cur1->val <= cur2->val)
{
cur->next = cur1;
cur1 = cur1->next;
}
else
{
cur->next = cur2;
cur2 = cur2->next;
}
cur = cur->next;
}
if(cur1)
{
cur->next = cur1;
}
if(cur2)
{
cur->next = cur2;
}
Node* ret = newhead->next;
delete newhead;
return ret;
}
};
2. K个⼀组翻转链表
解题思路:
- 模拟 :链表具有单向性,我们不能直接在原链表中翻转,否则会丢失元素。因此采用头插法 的方式解决翻转。步骤如下:
step1 :遍历链表,记节点个数为n,n/k计算需要翻转多少组。
step2 :新建链表,创建prev,cur,tmp,p,tail五个变量,对每组元素使用头插 的方式放入链表:将链表按K个为⼀组进⾏分组,组内进⾏反转 ,并且记录反转后的头尾结点,使其可以和前、后连接起来。
step3:循环step2直至K组链表全部翻转,连接剩余元素并返回头节点。
cpp
class Solution {
public:
typedef ListNode Node;
ListNode* reverseKGroup(ListNode* head, int k)
{
if(k == 1) return head;
int num = 0;
for(Node* cur=head;cur; cur=cur->next)
{
num++;
}
num /= k;
Node* newhead = new Node(0);
Node* prev = newhead;
Node* cur1 = head;
Node* next = head->next;
Node* tail = cur1;
if(num == 0) return nullptr;
while(num--)
{
int cnt = 0;
while(cnt < k)
{
Node* tmp = prev->next;
prev->next = cur1;
cur1->next = tmp;
cur1 = next;
if(cur1)
{
next = next->next;
}
cnt++;
}
prev = tail;
tail = cur1;
}
prev->next = cur1; //链接剩余节点
Node* ret = newhead->next;
delete newhead;
return ret;
}
};
// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~