第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); };
#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; }
总结:虽然二叉堆简单,但不适合合并操作,而左式堆和斜堆高效支持合并操作。