什么是左式堆
左式堆是一种有效地支持堆的合并操作的高级数据结构。左式堆既有二叉堆的结构性质,又有堆序性质。但和二叉堆不同,左式堆同时也是一棵二叉树,而二叉堆可以简化为数组。左式堆不是理想平衡的,事实上它也是趋于不平衡的。
左式堆的性质
我们把任一节点X的零路径长(null path length)npl(x)定义为从X节点到一个不具有两个儿子的节点的最短路径的长度。
因此,具有0个或1个孩子的节点的npl值为0,而npl(null)= -1。比如如下这棵树,节点里的数字表示该节点的零路径长。
1 1
/ \ / \
1 0 1* 0
/ \ / \
0 0 0 1
/ / \
0 0 0
k1 k2
这里可以简单概括一个规律:任一节点的零路径长(npl)等于它的两个儿子中的最小值+1。
而左式堆的性质是:对于堆中的任一节点X,左儿子的零路径长总大于或等于右儿子的零路径长。
比如上图,k1是左式堆而k2不是,因为k2中打*的节点左儿子的npl比右儿子小。
因此对于左式堆,结合左式堆的性质和总结出的规律,可以得到:左式堆中任一节点的零路径长等于右儿子的零路径长+1,即 npl(t) = npl(t->right) + 1
。这对于没有右孩子的节点同样适用,因为npl(NULL) = -1
。
左式堆的操作
#include<iostream>
using namespace std;
template<typename T>
class LeftistNode{
public:
T element;
LeftistNode*left;
LeftistNode*right;
int npl; //记录当前节点的npl值
public:
LeftistNode(int e, LeftistNode*l = NULL, LeftistNode*r = NULL, int n=0)
:element(e), left(l), right(r),npl(n){}
};
template<typename T>
class LeftistHeap{
private:
LeftistNode<T>* root;
public:
LeftistHeap(const T & x){
root = new LeftistNode<T>(x);
}
LeftistHeap(){}
bool isEmpty(){
return root == NULL;
}
const T & findMin()const{
return root->element;
}
void insert(const T & x){
root = merge(new LeftistNode<int>(x), root);
}
/*
特殊的删除方法
删除根节点
把剩余的两个堆合并
*/
void deleteMin(){
if (isEmpty()){
return;
}
LeftistNode* old = root;
root = merge(root->left, root->right);
delete old;
}
void deleteMin(T & x){
x = findMin();
deleteMin();
}
//合并两个堆
void merge(LeftistHeap & rhs){
if (this == &rhs){
return;
}
this->root = merge(root, rhs.root);
rhs.root = NULL;
}
void printTree()const{
printTree(root);
cout << endl;
printTree(root, 1);
cout <<endl;
}
private:
/*
对两个堆的情况分类处理
不分先后
如果有一个是空堆就返回另一个堆
否则调用merge1()
*/
LeftistNode<T> * merge(LeftistNode<T> * h1, LeftistNode<T> * h2){
if (h1 == NULL){
return h2;
}
if (h2 == NULL){
return h1;
}
if (h1->element < h2->element){
return merge1(h1, h2);
}
if(h1->element > h2->element){
return merge1(h2, h1);
}
}
//默认h1的根比h2小
LeftistNode<T>* merge1(LeftistNode<T>* h1, LeftistNode<T>* h2){
//如果左子树为空
//就把h2接上去
if (h1->left == NULL){
h1->left = h2;
}
else{
//把右子树和h2做比较,递归调用merge()合并
h1->right = merge(h1->right, h2);
//合并完成后检验根节点是否为左式堆
//如果不是左式堆就交换两个孩子
if (h1->left->npl < h1->right->npl){
swapChildren(h1);
}
//重置根的npl值
h1->npl = h1->right->npl + 1;
}
return h1;
}
//交换两个子树
void swapChildren(LeftistNode<T>*t){
LeftistNode<T> * temp = t->left;
t->left = t->right;
t->right = temp;
}
void printTree(const LeftistNode<T>* t)const{
if (t != NULL){
printTree(t->left);
cout << t->element << ' ';
printTree(t->right);
}
}
void printTree(const LeftistNode<T>* t,int x)const{
if (t != NULL){
printTree(t->left,1);
printTree(t->right,1);
cout << t->element << ' ';
}
}
};