左式堆
第1,左式堆以二叉树的形式构建。
第2,左式堆的任意结点的值比其子树任意结点值均小(最小堆的特性)。但左式堆不是一棵完全二叉树,而是一棵不平衡的树。
第3,NPL是 null path length 的缩写,指的是从该结点到达一个没有两个孩子的结点(一个或无孩子)的最短距离,NULL的Npl为-1。左式堆左儿子的NPL大于等于
右儿子的 NPL,即左式堆是向左增加深度。容易得出,任意结点的Npl是它右儿子的NLP+1。
源代码:
.h
struct Node
{
Node *left;
Node *right;
int data;
int npl;
};
class LeftHeap
{
public:
LeftHeap();
~LeftHeap();
void Insert(int data);
int Delete();
int GetMin() const;
bool IsEmpty() const;
private:
Node *_leftHeap;
Node* _Merge(Node* h1, Node* h2);
Node* _MergeImp(Node* h1, Node* h2);
void _SwapChildren(Node* root);
};
.cpp
LeftHeap::LeftHeap()
{
_leftHeap = NULL;
}
LeftHeap::~LeftHeap()
{
while(IsEmpty() == false)
{
Delete();
}
}
void LeftHeap::Insert(int data)
{
Node *node = new Node;
node->data = data;
node->left = node->right = NULL;
node->npl = 0;
_leftHeap = _Merge(_leftHeap, node);
}
int LeftHeap::Delete()
{
int data = _leftHeap->data;
Node *left = _leftHeap->left;
Node *right = _leftHeap->right;
delete _leftHeap;
_leftHeap = _Merge(left, right);
return data;
}
int LeftHeap::GetMin() const
{
return _leftHeap->data;
}
bool LeftHeap::IsEmpty() const
{
return (_leftHeap == NULL);
}
void LeftHeap::_SwapChildren(Node* root)
{
Node *tmp = root->left;
root->left = root->right;
root->right = tmp;
}
Node* LeftHeap::_Merge(Node* h1, Node* h2)
{
if(h1 == NULL)
return h2;
if(h2 == NULL)
return h1;
if(h1->data < h2->data)
return _MergeImp(h1, h2);
else
return _MergeImp(h2, h1);
}
Node* LeftHeap::_MergeImp(Node* h1, Node* h2)
{
if(h1->left == NULL)
{
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;
}
斜堆
斜堆是左式堆的自动调节形式。
由于左式堆的合并都是沿着最右路径进行合并的,经过合并之后,新斜堆的最右路径长度必然增加,这会影响下一次合并的效率。所以左式堆在进行合并的同时,
检查最右路径节点的距离(NPL),并通过交换左右子树,使整棵树的最右路径长度非常小。然而斜堆不记录节点的距离,在操作时,从下往上,沿着合并的路径,
在每个节点处都交换左右子树。通过不断交换左右子树,斜堆把最右路径甩向左边了。
.h
struct Node
{
Node *left;
Node *right;
int data;
};
class LeftHeap
{
public:
LeftHeap();
~LeftHeap();
void Insert(int data);
int Delete();
int GetMin() const;
bool IsEmpty() const;
private:
Node *_leftHeap;
Node* _Merge(Node* h1, Node* h2);
Node* _MergeImp(Node* h1, Node* h2);
void _SwapChildren(Node* root);
};
.cpp
#define NULL 0
LeftHeap::LeftHeap()
{
_leftHeap = NULL;
}
LeftHeap::~LeftHeap()
{
while(IsEmpty() == false)
{
Delete();
}
}
void LeftHeap::Insert(int data)
{
Node *node = new Node;
node->data = data;
node->left = node->right = NULL;
_leftHeap = _Merge(_leftHeap, node);
}
int LeftHeap::Delete()
{
int data = _leftHeap->data;
Node *left = _leftHeap->left;
Node *right = _leftHeap->right;
delete _leftHeap;
_leftHeap = _Merge(left, right);
return data;
}
int LeftHeap::GetMin() const
{
return _leftHeap->data;
}
bool LeftHeap::IsEmpty() const
{
return (_leftHeap == NULL);
}
void LeftHeap::_SwapChildren(Node* root)
{
Node *tmp = root->left;
root->left = root->right;
root->right = tmp;
}
Node* LeftHeap::_Merge(Node* h1, Node* h2)
{
if(h1 == NULL)
return h2;
if(h2 == NULL)
return h1;
if(h1->data < h2->data)
return _MergeImp(h1, h2);
else
return _MergeImp(h2, h1);
}
Node* LeftHeap::_MergeImp(Node* h1, Node* h2)
{
if(h1->left == NULL)
{
h1->left = h2;
}
else
{
h1->right = _Merge(h1->right, h2);
_SwapChildren(h1);
}
return h1;
}
总结:虽然二叉堆简单,但不适合合并操作,而左式堆和斜堆高效支持合并操作。