问题描述
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
问题分析
思路1
这个问题是leetcode021合并两个有序列表的增强版:合并k个有序列表。
实现原理比较相似:每次取k个列表表头结点,依次找出这些头结点最小的(排序,便于后续利用),然后取最小结点所在列表的下一结点。这次学习了如何利用sort()函数给vector的非基本类型ListNode排序。
- 先写比较函数
bool cmp_by_ListNode(const ListNode *left, const ListNode *right){
return left->val < right->val;
}
- 调用sort()排序函数
sort(vecList.begin(), vecList.end(), cmp_by_ListNode);
- 代码
待优化
bool cmp_by_ListNode(const ListNode *left, const ListNode *right){
return left->val < right->val;
}
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
int size = lists.size();
// ListNode *ans_final = NULL;
ListNode *ans = NULL;
ListNode *ans_p = NULL;
vector<ListNode *> vecList;
for (int i = 0; i < size; i++){
if (lists[i]) { vecList.push_back(lists[i]); }
}
size = vecList.size();
if (!size) return NULL;
if (size == 1) return *(vecList.begin());
sort(vecList.begin(), vecList.end(), cmp_by_ListNode);
vector<ListNode *>::iterator it;
ListNode *k = NULL; //k结点的值来自于上次vecList表头值(即最小值所在结点)
int val;
int flag; //0:表示k结点的值比vecList中任何结点值都小,此时直接将k结点加入ans列表中,并让k后续指向k->next
while (vecList.size() > 1 || vecList.size() == 1 && k){
it = vecList.begin();
val = (*it)->val;
flag = 1;
if (k){
if (k->val <= val){
val = k->val;
flag = 0;
}
else{
//方案1,加入vecList再排序,复杂度O(n*logn),n为vecList结点数目,运行时间154 ms
/*vecList.push_back(k);
sort(vecList.begin(), vecList.end(), cmp_by_ListNode);*/
//因为vecList本身是有序的,所以无需排序,只要将k值插入合适位置即可使得vecList有序(减少计算量)
//方案2,直接插入,查找复杂度为O(n),插入复杂度也是O(n)[因为是连续内存,后面的元素需要腾出空间],运行时间65 ms
/*vector<ListNode *>::iterator loc;
for (loc = vecList.begin(); loc != vecList.end(); loc++){
if ((*loc)->val > k->val) break;
}
if (loc != vecList.end()){
vecList.insert(loc, k);
}
else{
vecList.push_back(k);
}*/
//方案2改进:使用二分法查找,运行时间:60 ms
int l = 0, r = vecList.size() - 1;
int mid =( l + r) / 2;
while (l<r){
if (k->val < vecList[mid]->val)
r = mid;
else
l = mid;
mid = (l + r) / 2;
if (mid == l) break;
}vector<ListNode *>::iterator loc;
if (k->val >= vecList[r]->val) vecList.push_back(k);
else if (r - l == 1 && k->val > vecList[l]->val){
loc = vecList.begin() + r;
vecList.insert(loc, k);
}
else{
loc = vecList.begin() + l;
vecList.insert(loc, k);
}
}
}
ListNode *node = new ListNode(val);
if (!ans) ans_p = ans = node;
else {
ans_p->next = node; ans_p = node;
}
if (flag){
k = (*it)->next;
vecList.erase(it);
}
else k = k->next;
}
//if (vecList.size() == 1 && (!k)){
if (!ans) ans = *vecList.begin();
else ans_p->next = *vecList.begin();
// }
return ans;
}
};
- 优化后的代码
上述思路每次取最小的思想归结为用最小堆,其建堆算法复杂度为O(n),且代码简化。基本和上述代码性能相当。
//运行时间:61 ms
bool cmp_by_ListNode(const ListNode *left, const ListNode *right){
return left->val > right->val;
}
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
int size = lists.size();
// ListNode *ans_final = NULL;
ListNode *ans = NULL;
ListNode *ans_p = NULL;
vector<ListNode *> vecList;
for (int i = 0; i < size; i++){
if (lists[i]) { vecList.push_back(lists[i]); }
}
size = vecList.size();
if (!size) return NULL;
if (size == 1) return vecList[0];
make_heap(vecList.begin(), vecList.end(), cmp_by_ListNode);
ListNode *k = NULL; //k结点的值来自于上次vecList表头值(即最小值所在结点)
int val;
int flag; //0:表示k结点的值比vecList中任何结点值都小,此时直接将k结点加入ans列表中,并让k后续指向k->next
while (vecList.size() > 1 || vecList.size() == 1 && k){
val = vecList.front()->val;
flag = 1;
if (k){
if (k->val <= val){
val = k->val;
flag = 0;
}
else{
vecList.push_back(k);
push_heap(vecList.begin(), vecList.end(), cmp_by_ListNode);
}
}
ListNode *node = new ListNode(val);
if (!ans) ans_p = ans = node;
else {
ans_p->next = node; ans_p = node;
}
if (flag){
k = vecList.front()->next;
pop_heap(vecList.begin(), vecList.end(), cmp_by_ListNode);
vecList.pop_back();
}
else k = k->next;
}
//if (vecList.size() == 1 && (!k)){
if (!ans) ans = *vecList.begin();
else ans_p->next = *vecList.begin();
// }
return ans;
}
};
思路2
直接把k个列表放一个列表里,然后快排或建最小堆,复杂度O(nlogn)
- 快排代码
//运行时间:43ms
bool cmp_by_ListNode(const ListNode *left, const ListNode *right){
return left->val < right->val;
}
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
int size = lists.size();
ListNode *ans = NULL;
ListNode *ans_p = NULL;
vector<ListNode *> vecList;
for (int i = 0; i < size; i++){
if (lists[i]) { vecList.push_back(lists[i]); }
}
size = vecList.size();
if (!size) return NULL;
if (size == 1) return vecList.front();
int i = 0;
while (i<size)
{
ListNode*p = vecList[i++];
while (p->next){
vecList.push_back(p->next);
p = p->next;
}
}
sort(vecList.begin(), vecList.end(), cmp_by_ListNode);
size = vecList.size();
i = 0;
while (i<size){
ListNode * node = new ListNode(vecList[i]->val);
if (!ans) ans_p = ans = node;
else { ans_p->next = node; ans_p = node; }
i++;
}
return ans;
}
};
- 最小堆代码
注:建立最小堆的比较函数是>,最大堆为<,STL默认建立最大堆。
//运行时间:52ms
bool cmp_by_ListNode(const ListNode *left, const ListNode *right){
return left->val > right->val;
}
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
int size = lists.size();
// ListNode *ans_final = NULL;
ListNode *ans = NULL;
ListNode *ans_p = NULL;
vector<ListNode *> vecList;
for (int i = 0; i < size; i++){
if (lists[i]) { vecList.push_back(lists[i]); }
}
size = vecList.size();
if (!size) return NULL;
if (size == 1) return vecList[0];
int i = 0;
while (i<size)
{
ListNode*p = vecList[i++];
while (p->next){
vecList.push_back(p->next);
p = p->next;
}
}
make_heap(vecList.begin(), vecList.end(), cmp_by_ListNode);
while (!vecList.empty()){
if (!ans) ans_p = ans = vecList.front();
else{ ans_p->next = vecList.front(); ans_p = vecList.front(); }
pop_heap(vecList.begin(), vecList.end(), cmp_by_ListNode);
vecList.pop_back();
}
if (ans_p) ans_p->next = NULL;
// }
return ans;
}
};