原题地址:https://leetcode-cn.com/problems/merge-k-sorted-lists/description/
题目描述:
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入: [ 1->4->5, 1->3->4, 2->6 ] 输出: 1->1->2->3->4->4->5->6
解题方法:
/** * 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) { int n = lists.size(); if(n == 0) return NULL; ListNode* ans = lists[0]; ListNode* pre; ListNode* p; ListNode* q; for(int i = 1; i < n; i ++){ ListNode* pre; p = ans; q = lists[i]; if(!ans){ ans = lists[i]; continue; } if(!lists[i]){ continue; } if(ans->val <= lists[i]->val){ pre = ans; p = p->next; } else{ pre = lists[i]; ans = pre; q = q->next; } while(p && q){ if(p->val <= q->val){ pre->next = p; p = p->next; } else{ pre->next = q; q = q->next; } pre = pre->next; } if(p){ pre->next = p; } if(q){ pre->next = q; } } return ans; } };
方法用的不怎么样,时间复杂度较大。
学习使用小根堆的方法,原文地址:https://blog.csdn.net/gatieme/article/details/51097730
class Comp { public: bool operator() (const ListNode* left, const ListNode* right) const { return (left->val > right->val); } }; class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { /// 将空链表 vector<ListNode*>::iterator it = lists.begin(); while(it != lists.end()) { if(*it == NULL) { lists.erase(it); } else { ++it; } } if(lists.size( ) == 0) { return NULL; } ListNode *head = NULL; ListNode *curr = NULL; // 首先构造一个小顶堆 // 这个操作会对lists中每个链表的第一个元素组成的序列建立一个堆 make_heap(lists.begin( ), lists.end( ), Comp( )); while(lists.size() > 0) { // 堆顶的元素就是最小的元素 ListNode *smallNode = lists[0]; // 将smallNode插入到新链表中 if(head == NULL) { curr = smallNode; head = smallNode; } else { curr->next = smallNode; curr = curr->next; } // 将最小的元素弹出 // BUG, 不能简单的使用pop操作, // 因为数据结构是链表不是数组 // 如果将首指针弹出后, 会丢失整个单链表 // 我们期待做的仅仅是需改指针的指向 // 因此可以这么处理 // 当链表中仍然有元素时, 仅仅修改首元素指针的指向 // 只有当当前链表无其他元素时, 可直接弹出 //pop_heap把堆顶元素取出来,放到了数组或者是vector的末尾,用原来末尾元素去替代, pop_heap(lists.begin( ), lists.end( ), Comp( )); // 此时smallNode被交换到了末尾 if(lists[lists.size( ) - 1]->next == NULL) // 如果当前链表已经没有其他元素了那么可以直接弹出 { lists.pop_back( ); // 将为指针弹出后,相当于把整个单链表从lists中删除 } else // 当前最小元素所在的链表仍有其他元素, 仅仅修改首元素的指向 { // 使用二重指针修改其指向或者直接用list[0]修改 //ListNode **node = &lists[0]; //*node = (*node)->next; lists[lists.size( ) - 1] = lists[lists.size( ) - 1]->next; push_heap(lists.begin( ), lists.end( ), Comp( )); } } return head; } };
学习C++堆的用法:https://blog.csdn.net/flyyufenfei/article/details/78175511
在STL中,heap是算法的形式提供给我们使用的。包括下面几个函数:
make_heap: 根据指定的迭代器区间以及一个可选的比较函数,来创建一个heap. O(N) 默认的为大根堆
push_heap: 把指定区间的最后一个元素插入到heap中. O(logN)
pop_heap: 弹出heap顶元素, 将其放置于区间末尾. O(logN)
sort_heap:堆排序算法,通常通过反复调用pop_heap来实现. N*O(logN)
C++11加入了两个新成员:
is_heap: 判断给定区间是否是一个heap. O(N)
is_heap_until: 找出区间中第一个不满足heap条件的位置. O(N)
因为heap以算法的形式提供,所以要使用这几个api需要包含 #include <algorithm>