23. 合并 K 个升序链表
class Solution {
static ListNode *MergeTwoLists(ListNode *A, ListNode *B) {
ListNode Dummy;
ListNode *Prev = &Dummy;
while (A && B) {
if (A->val < B->val) {
Prev->next = A;
Prev = Prev->next;
A = A->next;
} else {
Prev->next = B;
Prev = Prev->next;
B = B->next;
}
}
if (A)
Prev->next = A;
if (B)
Prev->next = B;
return Dummy.next;
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.empty())
return {};
return std::reduce(std::next(lists.begin()), lists.end(), lists[0], MergeTwoLists);
}
};
std::reduce() 的类型检查还挺完备,用 {} 来初始化 init 都不行,必须得用 ListNode * 类型。
暴力解法本身没什么好说的,这个二分和优先队列解法倒是很有意思,可以学学。二分跟暴力相比基本上也是一样的,唯一的就是 controller 部分不太相同:
ListNode* merge(vector <ListNode*> &lists, int l, int r) {
if (l == r) return lists[l];
if (l > r) return nullptr;
int mid = (l + r) >> 1;
return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
return merge(lists, 0, lists.size() - 1);
}
优先队列解法就更好玩了,我管你这那的先放队列里面,每次就取最小的那个 head,这多好。
class Solution {
struct ListNodeComparator {
bool operator()(ListNode *Lhs, ListNode *Rhs) {
return Lhs->val > Rhs->val;
}
};
priority_queue<ListNode *, vector<ListNode *>, ListNodeComparator> PQ;
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode Dummy;
for (auto Head : lists) {
if (Head)
PQ.push(Head);
}
ListNode *Prev = &Dummy;
while (!PQ.empty()) {
ListNode *Smallest = PQ.top();
PQ.pop();
Prev->next = Smallest;
Prev = Prev->next;
// Keep in mind that we're processing LISTS, not NODES, so we need to
// manually push tails into the queue.
if (Smallest->next)
PQ.push(Smallest->next);
}
return Dummy.next;
}
};
但是注意,这里有坑:在 std::less<>() 下,priority_queue 默认建立的是大根堆!所以你重载的 operator() 得写成大于号而不是小于号才对……
二刷
class Solution {
ListNode *Merge(ListNode *L1, ListNode *L2) {
ListNode Dummy(0, L1), *Prev = &Dummy;
while (L1 && L2) {
if (L1->val < L2->val) {
Prev->next = L1;
L1 = L1->next;
} else {
Prev->next = L2;
L2 = L2->next;
}
Prev = Prev->next;
}
if (L1)
Prev->next = L1;
if (L2)
Prev->next = L2;
return Dummy.next;
}
ListNode *MergeSort(vector<ListNode *>& V, int L, int R) {
if (L == R)
return nullptr;
if (L + 1 == R)
return V[L];
int M = L + (R - L) / 2;
ListNode *Left = MergeSort(V, L, M);
ListNode *Right = MergeSort(V, M, R);
// No need to break the chain here, since we're processing VECTOR of nodes,
// not a whole sequence of a list!
return Merge(Left, Right);
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
return MergeSort(lists, 0, lists.size());
}
};
我人傻了,我还想着 Merge() 之前得给链表断一下链,结果发现其实用不着干这个事情,因为题目给我们的是 vector<ListNode *>,不是一整串 ListNode *!从某种程度上来讲,这道题其实还算是比较简单的了,因为如果给你一整串的话……分有序点都得自己分。
class Solution {
struct Compare {
bool operator()(const ListNode *Lhs, const ListNode *Rhs) {
// Keep in mind that priority_queue default to MAXIMUM heap!
return Lhs->val > Rhs->val;
}
};
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode *, vector<ListNode *>, Compare> PQ;
// Push heads ONLY, not all elements altogether.
for (ListNode *Head : lists) {
// Avoid pushing null heads.
if (Head)
PQ.push(Head);
}
ListNode Dummy, *Prev = &Dummy;
while (!PQ.empty()) {
// Get smallest HEAD, not smallest elements THEMSELVES. We're doing this
// because we only connect to the first element of each list. we won't
// connect them altogether.
ListNode *Smallest = PQ.top();
PQ.pop();
// Form the ordered list using Prev.
Prev->next = Smallest;
Prev = Prev->next;
// Consume the smallest element, and push back into the queue. Note that
// we won't push nullptrs. We don't need to care about the tail since
// every list in given vector is null-terminated already.
if (Smallest->next)
PQ.push(Smallest->next);
}
return Dummy.next;
}
};
这个优先队列解法的关键在于,我们每次要考察的并不是整条列表,而仅仅考虑链表头部那个元素的大小。
事实上在优先队列给予了我们每次挑选最小元素的能力、并且我们确定每个链表都是升序的前提下,我们完全没有必要考虑除头元素之外的其它任何元素。这就是堆排序这种排序方法的关键所在,也是它能实现 \(\mathrm O(kn\log k)\) 的原因。
其实你写归并的时候也是这样,你想想是不是——首先把每个数组分成单元有序,然后组起来的时候也是每次只关注头部。