一、斜堆的介绍
斜堆(Skew heap)也叫自适应堆(self-adjusting heap),它是左倾堆的一个变种。和左倾堆一样,它通常也用于实现优先队列。它的合并操作的时间复杂度也是O(lg n)。
相比于左倾堆,斜堆的节点没有"零距离"这个属性。除此之外,它们斜堆的合并操作也不同。斜堆的合并操作算法如下:
(1) 如果一个空斜堆与一个非空斜堆合并,返回非空斜堆。
(2) 如果两个斜堆都非空,那么比较两个根节点,取较小堆的根节点为新的根节点。将"较小堆的根节点的右孩子"和"较大堆"进行合并。
(3) 合并后,交换新堆根节点的左孩子和右孩子。
第(03)步是斜堆和左倾堆的合并操作差别的关键所在,如果是左倾堆,则合并后要比较左右孩子的零距离大小,若右孩子的零距离 > 左孩子的零距离,则交换左右孩子;最后,在设置根的零距离。
二、斜堆的解析
1. 基本定义
1 template <class T> 2 class SkewNode{ 3 public: 4 T key; // 关键字(键值) 5 SkewNode *left; // 左孩子 6 SkewNode *right; // 右孩子 7 8 SkewNode(T value, SkewNode *l, SkewNode *r): 9 key(value), left(l),right(r) {} 10 };
SkewNode是斜堆对应的节点类。
1 template <class T> 2 class SkewHeap { 3 private: 4 SkewNode<T> *mRoot; // 根结点 5 6 public: 7 SkewHeap(); 8 ~SkewHeap(); 9 10 // 前序遍历"斜堆" 11 void preOrder(); 12 // 中序遍历"斜堆" 13 void inOrder(); 14 // 后序遍历"斜堆" 15 void postOrder(); 16 17 // 将other的斜堆合并到this中。 18 void merge(SkewHeap<T>* other); 19 // 将结点(key为节点键值)插入到斜堆中 20 void insert(T key); 21 // 删除结点(key为节点键值) 22 void remove(); 23 24 // 销毁斜堆 25 void destroy(); 26 27 // 打印斜堆 28 void print(); 29 private: 30 31 // 前序遍历"斜堆" 32 void preOrder(SkewNode<T>* heap) const; 33 // 中序遍历"斜堆" 34 void inOrder(SkewNode<T>* heap) const; 35 // 后序遍历"斜堆" 36 void postOrder(SkewNode<T>* heap) const; 37 38 // 交换节点x和节点y 39 void swapNode(SkewNode<T> *&x, SkewNode<T> *&y); 40 // 合并"斜堆x"和"斜堆y" 41 SkewNode<T>* merge(SkewNode<T>* &x, SkewNode<T>* &y); 42 43 // 销毁斜堆 44 void destroy(SkewNode<T>* &heap); 45 46 // 打印斜堆 47 void print(SkewNode<T>* heap, T key, int direction); 48 };
SkewHeap是斜堆类,它包含了斜堆的根节点,以及斜堆的操作。
2. 合并
1 /* 2 * 合并"斜堆x"和"斜堆y" 3 */ 4 template <class T> 5 SkewNode<T>* SkewHeap<T>::merge(SkewNode<T>* &x, SkewNode<T>* &y) 6 { 7 if(x == NULL) 8 return y; 9 if(y == NULL) 10 return x; 11 12 // 合并x和y时,将x作为合并后的树的根; 13 // 这里的操作是保证: x的key < y的key 14 if(x->key > y->key) 15 swapNode(x, y); 16 17 // 将x的右孩子和y合并, 18 // 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。 19 SkewNode<T> *tmp = merge(x->right, y); 20 x->right = x->left; 21 x->left = tmp; 22 23 return x; 24 } 25 26 /* 27 * 将other的斜堆合并到this中。 28 */ 29 template <class T> 30 void SkewHeap<T>::merge(SkewHeap<T>* other) 31 { 32 mRoot = merge(mRoot, other->mRoot); 33 }
merge(x, y)是内部接口,作用是合并x和y这两个斜堆,并返回得到的新堆的根节点。
merge(other)是外部接口,作用是将other合并到当前堆中
3. 添加
1 /* 2 * 新建键值为key的结点并将其插入到斜堆中 3 * 4 * 参数说明: 5 * heap 斜堆的根结点 6 * key 插入的结点的键值 7 * 返回值: 8 * 根节点 9 */ 10 template <class T> 11 void SkewHeap<T>::insert(T key) 12 { 13 SkewNode<T> *node; // 新建结点 14 15 // 新建节点 16 node = new SkewNode<T>(key, NULL, NULL); 17 if (node==NULL) 18 { 19 cout << "ERROR: create node failed!" << endl; 20 return ; 21 } 22 23 mRoot = merge(mRoot, node); 24 }
insert(key)的作用是新建键值为key的节点,并将其加入到当前斜堆中。
4. 删除
1 /* 2 * 删除结点 3 */ 4 template <class T> 5 void SkewHeap<T>::remove() 6 { 7 if (mRoot == NULL) 8 return NULL; 9 10 SkewNode<T> *l = mRoot->left; 11 SkewNode<T> *r = mRoot->right; 12 13 // 删除根节点 14 delete mRoot; 15 // 左右子树合并后的新树 16 mRoot = merge(l, r); 17 }
remove()的作用是删除斜堆的最小节点。