算法导论4.14:
构造一个堆,包含k个堆中所有元素。算法的复杂度是多少?
思路
这题可以用Divide and Conquer,迭代分组合并两个堆。
假设每个堆元素最多为n个,用数组实现的算法因为每次merge比较复杂,会有O(nk*log(n))的复杂度,用链表实现因为直接连起来就可以,所以复杂度只有O(log(k)log(n))。
所以这题最好使用链表实现堆来写,但书上给的堆都是用数组实现的,所以就自己写一个左式堆(LeftistHeap)吧,左式堆天然地支持快速合并,用来实现这个算法再合适不过了。之后用一个vector把k个堆装起来,迭代地k_merge前一半的堆和后一半的堆就实现了算法。
代码
//LeftisHeap.h
#ifndef LeftistHeap_H
#define LeftistHeap_H
template<class Comparable>
class LeftistHeap
{
public:
LeftistHeap(){ root = nullptr; }
LeftistHeap(const LeftistHeap &rhs)
{
root = clone(rhs.root);
}
~LeftistHeap()
{
makeEmpty();
}
LeftistHeap& operator=(const LeftistHeap &rhs)
{
if (this != &rhs)
{
makeEmpty();
root = clone(rhs.root);
}
return *this;
}
bool isEmpty()const
{
return root == nullptr;
}
const Comparable& findMax()const
{
if (!root)
return -1;
return root->element;
}
void insert(const Comparable x)
{
root = merge(new LeftistNode(x), root);
}
void deleteMax()
{
if (!isEmpty())
{
LeftistNode *old = root;
root = merge(root->left, root->right);
delete old;
}
}
void deleteMax(Comparable &x)
{
x = findMax();
deleteMax();
}
void makeEmpty()
{
reclaimMemory(root);
root = nullptr;
}
void merge(LeftistHeap &rhs)
{
if (this == &rhs)
return;
root = merge(root, rhs.root);
rhs.root = nullptr;
}
void print()
{
LeftistHeap backup_heap;
vector<Comparable> print_v;
while (this->root)
{
print_v.push_back(this->findMax());
backup_heap.insert(this->findMax());
this->deleteMax();
}
for (vector<Comparable>::size_type i = 0; i < print_v.size(); i++)
{
cout << print_v[i] << " ";
}
cout << endl;
*this = backup_heap;
}
private:
struct LeftistNode
{
Comparable element;
LeftistNode *left;
LeftistNode *right;
int npl;
LeftistNode(const Comparable &e, LeftistNode *l = nullptr, LeftistNode *r = nullptr, int x = 0)
:element(e), left(l), right(r), npl(x){}
};
LeftistNode *root;
LeftistNode *merge(LeftistNode *h1, LeftistNode *h2)
{
if (h1 == nullptr)
return h2;
if (h2 == nullptr)
return h1;
if (h1->element > h2->element)
return merge1(h1, h2);
else
return merge1(h2, h1);
}
LeftistNode *merge1(LeftistNode *h1, LeftistNode *h2)
{
if (h1->left == nullptr)
h1->left = h2;
else
{
h1->right = merge(h1->right, h2);
if (h1->left->npl < h1->right->npl)
swapChildren(h1);
h1->npl = h1->right->npl + 1;
}
return h1;
}
void swapChildren(LeftistNode *h1)
{
if (h1 != nullptr)
{
LeftistNode *h = h1->left;
h1->left = h1->right;
h1->right = h;
}
}
void LeftistHeap::reclaimMemory(LeftistNode *t)
{
if (t != nullptr)
{
reclaimMemory(t->left);
reclaimMemory(t->right);
delete t;
}
}
LeftistNode *clone(const LeftistNode *t)
{
if (t == nullptr)
return nullptr;
return new LeftistNode(t->element, clone(t->left), clone(t->right), t->npl);
}
};
#endif
//main_k_merge.cpp
#include "LeftistHeap.h"
#include <iostream>
#include <vector>
using namespace std;
template <class T>
vector<LeftistHeap<T> > build_heaps()
{
vector<LeftistHeap<T> > ret;
int i = 1;
while (true)
{
T data = -1;
cout << "Input the Heap No." << i
<< " ~, input neg numbers to end Heap, C^Z to end"
<< endl;
if (!(cin >> data))
break;
LeftistHeap<T> *h = new LeftistHeap<T>;
do
{
if (data < 0)
break;
h->insert(data);
} while (cin >> data);
ret.push_back(*h);
i++;
}
return ret;
}
LeftistHeap<int> merge_k(vector<LeftistHeap<int> > &heaps)
{
if (heaps.size() < 2)
return heaps[0];
if (heaps.size() == 2)
{
heaps[0].merge(heaps[1]);
return heaps[0];
}
vector<LeftistHeap<int> > v1(heaps.begin(), heaps.begin() + heaps.size()/2);
vector<LeftistHeap<int> > v2(heaps.begin() + heaps.size() / 2, heaps.end());
LeftistHeap<int> merged = merge_k(v1);
LeftistHeap<int> merge_2 = merge_k(v2);
merged.merge(merge_2);
return merged;
}
int main()
{
vector<LeftistHeap<int> > heap_sets = build_heaps<int>();
cout << "Input heaps:" << endl;
for (vector<LeftistHeap<int> >::size_type i = 0; i != heap_sets.size(); i++)
{
heap_sets[i].print();
cout << endl;
}
LeftistHeap<int> merged_heap = merge_k(heap_sets);
cout << "The merged heap: " << endl;
merged_heap.print();
system("pause");
return 0;
}
参考资料
《数据结构与算法设计:C++实现》