【算法】Merge k Sorted Lists
Merge k Sorted Lists
题目描述:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. k个链表,都已经排序好了,现在要将k个链表合并。
解析:
最直接想到的是归并排序,归并排序的思想就是将一个数组分成两个子数组,分别进行排序,排序好之后再进行合并,合并的方式是用两个指针指向数组的第一个元素,比较两个数,依次向后移动,所用的时间是O(n),根据这个思想,写出的代码如下:
具体代码如下:
#include "MergekSortedLists .h"
#include <vector>
#include <stdlib.h>
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
ListNode *res = (ListNode *)malloc(sizeof(ListNode));//返回的链表
int k = (int)lists.size();//求k路归并的k是多少
//创建k个指针分别指向k路的第一个node
ListNode **pp = (ListNode **)malloc(sizeof(ListNode*)*k);
for (int i = 0 ; i < k; i++) {
pp[i] = lists[i];
}
while (true) {
ListNode *small;
for (int i = 0; i < k; i++) {
if (pp[i] != NULL) {
if (small == NULL && small->val < pp[i]->val) {
small = pp[i];
}
pp[i] = pp[i]->next;
}
}
if(small == NULL){
break;
}else{
res->next = small;
}
}
return res->next;
}
};
遗憾的是上述代码在leetcode上time out了,说好的bug free呢... :(
然后就想到k的值可能非常大,每次对全部k个链表头结点查找最大,需要的时间是O(k),整个算法的时间是O(k*n),n是所有链表包含节点数之和。自然的发现每次找最大值而进行全扫描是多此一举的,最小堆是一个更好的选择,最小堆的插入、弹出都是O(logK),在C++使用最小堆用priority_queue即可,具体用法参见references.代码如下:
#include <vector>
#include <stdlib.h>
#include <queue>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
struct cmp{
bool operator()(ListNode* t1,ListNode* t2)
{
if ( !t1 || !t2 )
return !t2;
return t1->val>t2->val;
}
};
class MergekSortedLists {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
if (lists.empty()) return NULL;
int k = (int)lists.size();//求k路归并的k是多少
priority_queue<ListNode*,vector<ListNode*>,cmp> Q;
for(int i = 0;i < k;i++){
if ( lists[i] != NULL){
Q.push(lists[i]);
}
}
ListNode guard(-1);
ListNode* tail=&guard;
while(!Q.empty())
{
ListNode* toAdd = Q.top();
Q.pop();
tail->next = toAdd;
tail = tail->next;
if (toAdd->next) Q.push(toAdd->next);//如果新加入的Node还指向另一个指针,那么把新的指针加入到其中来
}
return guard.next;
}
};
说个好玩的事
请考虑如下这道简单到白痴的题目:
在含有n个数的数组中找到最小的数
这个问题,有不止一个同学提出了这样的想法:将n个数两两比较,较小的数获胜,再进行第二轮比较,依次进行下去,就像体育比赛中的淘汰赛,最后获胜的就是我们要找个数,而整个过程时间复杂度是O(logN)。而线性扫描的时间是O(N)!我的亲娘四舅姥爷,这方法太好了!
当初一T同学信心满满的提出这个算法时,我们一致表示真是天才,后来X同学不理解,我们对X同学嗤之以鼻,直到...直到X同学用数学证明了上述算法的时间复杂度依然是O(N),你只要稍加思考,就会发现真相...
实际上,O(logN)指的是比较的轮数,而不是比较的次数,比较的次数可以用等比数列求和公式进行计算,结果依然是O(N)。
更有意思的是后来有FW同学自己又想出了这个方法,目前他仍然认为这个方法和使用最小堆的时间复杂是一样的!
别让你所学的知识蒙蔽了你自己!