Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
解题思路
思路一:首先取第一个链表为中间结果。然后使用 merge2Lists 方法将中间结果链表和数组中下一个链表合并为一条新的链表作为中间结果,以此类推。假设每条链表平均有n个元素,则时间复杂度是
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
private:
// 合并两个链表
ListNode * merge2Lists(ListNode *l1, ListNode *l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
ListNode *head = new ListNode(0), *tail = head;
while (l1 && l2) {
ListNode *min = NULL;
if (l1->val <= l2->val) {
min = l1;
l1 = l1->next;
}
else {
min = l2;
l2 = l2->next;
}
min->next = NULL;
tail->next = min;
tail = min;
}
if (l1) tail->next = l1;
if (l2) tail->next = l2;
tail = head;
head = head->next;
delete tail;
return head;
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len = lists.size();
if (len < 1) return NULL;
// 先设当前结果为第一个链表
ListNode *result = lists[0];
// 将当前结果与 lists[i] 合并,并更新当前的合并结果
for (int i = 1; i < len; ++i) {
result = merge2Lists(result, lists[i]);
}
return result;
}
};
思路二:采用分治法。先把数组中 k 个链表分成前、后两部分;然后调用 mergeKLists 函数递归对这两部分进行处理;最后调用 merge2Lists 将前半部分和后半部分的结果进行合并。递归结束条件是 k==0 || k == 1。
假设总共有 k 个 list,每个 list 的最大长度是 n,那么运行时间满足递推式T(k)=2T(k/2)+O(n∗k),因此总的时间复杂度是O(nklogk)。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
private:
// 合并两个链表
ListNode * merge2Lists(ListNode *l1, ListNode *l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
ListNode *head = new ListNode(0), *tail = head;
while (l1 && l2) {
ListNode *min = NULL;
if (l1->val <= l2->val) {
min = l1;
l1 = l1->next;
}
else {
min = l2;
l2 = l2->next;
}
min->next = NULL;
tail->next = min;
tail = min;
}
if (l1) tail->next = l1;
if (l2) tail->next = l2;
tail = head;
head = head->next;
delete tail;
return head;
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len = lists.size();
if (len == 0) return NULL;
if (len == 1) return lists[0];
// 分治法:先递归的对数组的前半部分和后半部分进行mergeKLists
vector<ListNode *> leftLists(lists.begin(), lists.begin() + (len >> 1));
vector<ListNode *> rightLists(lists.begin() + (len >> 1), lists.end());
ListNode *leftResult = mergeKLists(leftLists);
ListNode *rightResult = mergeKLists(rightLists);
// 最后将调用 merge2Lists 合并前半部分和后半部分 mergeKLists 返回的结果
return merge2Lists(leftResult, rightResult);
}
};
思路三:另一个思路是将 k 个链表中具有最小值的表头结点加入到结果链表中,并将相应链表的表头指针移到下一个结点。重复该操作,直到说有元素都加入到结果链表中。
为了快速找到具有最小值的表头结点,我们维护一个大小为 k 的最小堆,每次取走堆顶的结点加入到结果链表中,并将该结点的下一个结点放入堆中。假设每个 list 的最大长度是 n,那么该方法的时间复杂度为O(nklogk)。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
struct cmp {
bool operator() (ListNode *a, ListNode *b) {
return a->val > b->val;
}
};
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len = lists.size();
// 建立大小为 len 的最小堆
priority_queue<ListNode *, vector<ListNode *>, cmp> minHeap;
for (int i = 0; i < len; ++i) {
if (lists[i] != NULL) {
minHeap.push(lists[i]);
}
}
ListNode *head = new ListNode(-1), *tail = head;
while (!minHeap.empty()) {
ListNode *top = minHeap.top();
minHeap.pop();
tail->next = top;
tail = top;
// Note
if (top->next) {
minHeap.push(top->next);
}
}
tail = head;
head = head->next;
delete tail;
return head;
}
};
高效合并多个已排序链表
本文探讨了如何高效地合并k个已排序的链表为一个有序链表,包括了时间复杂度分析、多种解题思路及实现代码。
1456

被折叠的 条评论
为什么被折叠?



