PS: 将两个排序链表和将K个排序链表是同一类问题,下面主要从合并两个链表展开到合并K个链表的情况。
合并两个排序链表
#include<iostream>
#include <cstring>
#include <algorithm>
using namespace std;
// 类似 归并排序
class Solution3 {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if (!pHead1)
return pHead2;
if (!pHead2)
return pHead1;
ListNode* Head;
ListNode* p;
//取较小值作头结点
if (pHead1->val <= pHead2->val) {
Head = pHead1;
pHead1 = pHead1->next;
}
else {
Head = pHead2;
pHead2 = pHead2->next;
}
//开始遍历合并
p = Head; //p为合并后的链表的工作指针
while (pHead1 && pHead2) { //当有一个链表到结尾时,循环结束
if (pHead1->val <= pHead2->val) { //如果链表1的结点小于链表2的结点
p->next = pHead1; //取这个结点加入合并链表
pHead1 = pHead1->next; //链表1后移一位
p = p->next; //工作指针后移一位
}
else { //否则取链表2的结点
p->next = pHead2;
pHead2 = pHead2->next;
p = p->next;
}
}
if (pHead1 == NULL) //链表1遍历完了
p->next = pHead2; //如果链表2也遍历完了,则pHead2=NULL
if (pHead2 == NULL) //链表2遍历完了
p->next = pHead1; ///如果链表1也遍历完了,则pHead1=NULL
return Head;
}
};
// 使用递归法
class Solution2 {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if (pHead1 == NULL) return pHead2;
if (pHead2 == NULL) return pHead1;
ListNode* mergeList = NULL;
if (pHead1->val < pHead2->val) {
mergeList = pHead1;
mergeList->next = Merge(pHead1->next, pHead2);
}
else {
mergeList = pHead2;
mergeList->next = Merge(pHead1, pHead2->next);
}
return mergeList;
}
};
合并K个排序链表
方法一:K个链表可以看成是由若干个两两组成的链表,因此可以借鉴两个链表合并的情况。
class Solution {
public:
// 合并两个有序链表
ListNode* merge(ListNode* p1, ListNode* p2) {
if (!p1) return p2;
if (!p2) return p1;
if (p1->val <= p2->val) {
p1->next = merge(p1->next, p2);
return p1;
}
else {
p2->next = merge(p1, p2->next);
return p2;
}
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.size() == 0) return nullptr;
ListNode* head = lists[0];
for (int i = 1; i < lists.size(); ++i) {
if (lists[i]) head = merge(head, lists[i]);
}
return head;
}
};
方法二:类似于归并排序,分而治之
class Solution {
public:
// 合并两个有序链表
ListNode* merge_process(ListNode* p1, ListNode* p2) {
if (!p1) return p2;
if (!p2) return p1;
if (p1->val <= p2->val) {
p1->next = merge(p1->next, p2);
return p1;
}
else {
p2->next = merge(p1, p2->next);
return p2;
}
}
ListNode* merge(vector<ListNode*>& lists, int start, int end) {
if (start == end) return lists[start];
int mid = (start + end) / 2;
ListNode* l1 = merge(lists, start, mid);
ListNode* l2 = merge(lists, mid + 1, end);
return merge_process(l1, l2);
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.size() == 0) return nullptr;
return merge(lists, 0, lists.size() - 1);
}
};
方法三: 使用极小堆,维护一个size为所有节点大小的极小堆
// 使用最小堆, 时间复杂度O(Nlogk),空间复杂度O(N) class Solution { public: // 小根堆的回调函数 struct cmp { bool operator()(ListNode* a, ListNode* b) { return a->val > b->val; } }; ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<ListNode*, vector<ListNode*>, cmp> pri_queue; // 建立大小为k的小根堆 for (auto it : lists) { if (it) pri_queue.push(it); } // 可以使用哑节点/哨兵节点 ListNode* dummy = new ListNode(-1); ListNode* p = dummy; // 开始出队 while (!pri_queue.empty()) { ListNode* top = pri_queue.top(); pri_queue.pop(); p->next = top; p = top; if (top->next) pri_queue.push(top->next); } return dummy->next; } };
注意事项:
1.两两合并的过程就是链表的归并排序的过程,可能存在一看就会,一写就废的现象。
2.递归比较简洁,但是需要深刻理解这个合并的过程。