原题地址: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>