LeetCode23合并K个升序链表C++
题目
题目描述:
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]] |
示例 2:
输入: [] |
示例 3:
输入: [ [] ] |
方法 1:逐对合并(分治法)
分治法的思路是将K个链表分成两部分,递归合并。
时间复杂度:
- O(N log K),其中N是所有链表中元素的总数,K是链表的个数。
- 每次合并的时间复杂度是O(N),但分治递归的深度是log K。
代码实现:class Solution {
public:
// 递归分治合并 K 个链表
ListNode* findRes1(vector<ListNode*>& lists, int left, int right) {
if (right == left) return lists[left];
int mid = left + (right - left) / 2;
ListNode* node1 = findRes1(lists, left, mid);
ListNode* node2 = findRes1(lists, mid + 1, right);
return findRes2(node1, node2);
}
// 合并两个已排序链表
ListNode* findRes2(ListNode* node1, ListNode* node2) {
ListNode* head = new ListNode(0);
ListNode* cur = head;
while (node1 != NULL && node2 != NULL) {
if (node1->val < node2->val) {
cur->next = node1;
node1 = node1->next;
} else {
cur->next = node2;
node2 = node2->next;
}
cur = cur->next;
}
if (node1 != NULL) cur->next = node1;
if (node2 != NULL) cur->next = node2;
return head->next;
}
// 合并 K 个链表
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.empty()) return NULL;
return findRes1(lists, 0, lists.size() - 1);
}
};
方法 2:最小堆(优先队列)
利用最小堆来合并所有链表。每次从所有链表中取出当前最小的节点,并将其添加到结果链表中。
时间复杂度:
- O(N log K),其中N是所有链表中的节点总数,K是链表的数量。
- 堆的操作时间复杂度是log K,每次从堆中取出一个元素,最多会执行N次。
代码实现:class Solution {
public:
struct Compare {
bool operator()(ListNode* a, ListNode* b) {
return a->val > b->val; // 小根堆
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode*, vector<ListNode*>, Compare> pq;
// 将每个链表的头节点加入堆
for (auto list : lists) {
if (list) pq.push(list);
}
ListNode* dummy = new ListNode(0);
ListNode* current = dummy;
// 从堆中取出最小节点,加入结果链表,并将该节点的下一个节点加入堆
while (!pq.empty()) {
ListNode* node = pq.top();
pq.pop();
current->next = node;
current = current->next;
if (node->next) {
pq.push(node->next);
}
}
return dummy->next;
}
};
方法 3:迭代合并(超时)
通过逐个链表合并,先将第一个和第二个链表合并,再与第三个链表合并,以此类推。
时间复杂度:
- O(KN),其中K是链表的个数,N是每个链表的平均长度。
- 这个方法每次合并两个链表,时间复杂度是O(N),但是需要执行K-1次合并。
代码实现:class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.empty()) return nullptr;
ListNode* result = lists[0];
for (int i = 1; i < lists.size(); ++i) {
result = mergeTwoLists(result, lists[i]);
}
return result;
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode(0);
ListNode* current = dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
current->next = l1;
l1 = l1->next;
} else {
current->next = l2;
l2 = l2->next;
}
current = current->next;
}
if (l1) current->next = l1;
if (l2) current->next = l2;
return dummy->next;
}
};