本次的算法题目是Merge k Sorted Lists。
原题链接
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
题目要求是把k个已经排好序的链表重新组合成一个新的有序链表。
解题想法依然是先尝试最简单粗暴的方法,如果有问题,再在其基础上做出修改。
对于k个链表,由于其是有序的,因此我们每一次比较k个链表的首项,然后取出最小的即刻。假设每个链表的元素个数都为m,那么k个链表就有km个节点个数。排序的速度为O((km)^2),快速排序为O(km log(km))。那么利用其有序的特点,我们这种最直接的算法的算法复杂度为O(mk^2)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode* head = NULL;
ListNode* cur = NULL;
int i = 0;
int min_index = -1;
vector<ListNode*> temp_lists;
for (i = 0; i < lists.size(); i++) {
temp_lists.push_back(lists[i]);
}
// 每次提取出一个最小值
while (true) {
// 找出最小值所在的位置
for (i = 0; i < temp_lists.size(); i++) {
if (temp_lists[i] != NULL) {
if (min_index == -1) {
min_index = i;
}
else {
if (temp_lists[i]->val < temp_lists[min_index]->val) {
min_index = i;
}
}
}
}
// 如果最小值不存在,则已经完成了合并了
if (min_index == -1) {
break;
}
// 将现在指针的next指向最小值所在的节点
else {
if (head == NULL) {
head = temp_lists[min_index];
cur = temp_lists[min_index];
}
else {
cur->next = temp_lists[min_index];
cur = cur->next;
}
}
temp_lists[min_index] = temp_lists[min_index]->next;
min_index = -1;
}
return head;
}
};
这一种方法的复杂度可以说是稳定的,但是其运行结果可以看到是相当差的,甚至比全部拿来进行一次快排还要差。
因此进一步的做法可以如下所述。
对于k个链表,我们找出首项最小的链表a,然后找出首项第二小的链表b,然后对链表a向后检索,直到找到比链表b首项大的元素为止,这才是第一次循环结束。
对于最好情况,我们只需要m+k次比较就能够对m个节点排好序,而最坏情况,也就跟上诉的第一种方法一样需要比较mk次。
那么,再往下延伸,我们可以对第一次比较的k个首项排个序,然后第t小的在第t-1小的上做合并。(1 <= t <=k)
那么最好情况是如何的呢?第一次比较k个首项使用快排,就是k log(k),然后链表与链表直接合并是最理想的情况,也就是两条链表只需要比较m次就能够合并了,总共做k-1次,的出来的结果就是k log(k) + (k-1)m。而最坏情况,无非也就是mk^2,但是这个复杂度的期望,比较难算清楚.#>_<#。