11、【堆】斜堆

一、斜堆的介绍

斜堆(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()的作用是删除斜堆的最小节点。

原文地址:https://www.cnblogs.com/Long-w/p/9786194.html

时间: 2024-10-17 08:26:36

11、【堆】斜堆的相关文章

斜堆,非旋转treap,替罪羊树

一.斜堆 斜堆是一种可以合并的堆 节点信息: struct Node { int v; Node *ch[2]; }; 主要利用merge函数 Node *merge(Node *x, Node *y) { if(!x) return y; if(!y) return x; if(x->v < y->v) swap(x, y); x->ch[1] = merge(x->ch[1], y); return swap(x->ch[0], x->ch[1]), x; }

堆之左式堆和斜堆

d-堆 类似于二叉堆,但是它有d个儿子,此时,d-堆比二叉堆要浅很多,因此插入操作更快了,但是相对的删除操作更耗时.因为,需要在d个儿子中找到最大的,但是很多算法中插入操作要远多于删除操作,因此,这种加速是现实的. 除了不能执行find去查找一般的元素外,两个堆的合并也很困难. 左式堆 左式堆可以有效的解决上面说的堆合并的问题.合并就涉及插入删除,很显然使用数组不合适,因此,左式堆使用指针来实现.左式堆和二叉堆的区别:左式堆是不平衡的.它两个重要属性:键值和零距离 零距离(英文名NPL,即Nul

[BZOJ1455]罗马游戏-斜堆/左偏树-并查集(+数据生成器)

Problem 遗产 题目大意 罗马皇帝很喜欢玩杀人游戏. 他的军队里面有n个人,每个人都是一个独立的团.最近举行了一次平面几何测试,每个人都得到了一个分数. 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻.他决定玩这样一个游戏. 它可以发两种命令: 1. Merger(i, j).把i所在的团和j所在的团合并成一个团.如果i, j有一个人是死人,那么就忽略该命令. 2. Kill(i).把i所在的团里面得分最低的人杀死.如果i这个人已经死了,这条命令就忽略. 皇帝希望他每发布一条kill命令

[BZOJ]1078: [SCOI2008]斜堆

Time Limit: 10 Sec  Memory Limit: 162 MB Description 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小.但斜堆不必是平衡的,每个结点的左右儿子的大小关系也没有任何规定.在本题中,斜堆中各个元素的值均不相同. 在斜堆H中插入新元素X的过程是递归进行的:当H为空或者X小于H的根结点时X变为新的树根,而原来的树根(如果有的话)变为X的左儿子.当X大于H

bzoj1078【SCOI2008】斜堆

1078: [SCOI2008]斜堆 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 662  Solved: 380 [Submit][Status][Discuss] Description 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值 都比它父亲大.因此在整棵斜堆中,根的值最小.但斜堆不必是平衡的,每个结点的左右儿子的大小关系也没有任 何规定.在本题中,斜堆中各个元素的值均不相同

[BZOJ 1078][SCOI2008]斜堆(可并堆)

Description 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值 都比它父亲大.因此在整棵斜堆中,根的值最小.但斜堆不必是平衡的,每个结点的左右儿子的大小关系也没有任 何规定.在本题中,斜堆中各个元素的值均不相同. 在斜堆H中插入新元素X的过程是递归进行的:当H为空或者X 小于H的根结点时X变为新的树根,而原来的树根(如果有的话)变为X的左儿子.当X大于H的根结点时,H根结点的 两棵子树交换,而X(递归)插入到交换后的左子树中.

[bzoj1078][SCOI2008][斜堆] (贪心)

Description 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小.但斜堆不必是平衡的,每个结点的左右儿子的大小关系也没有任何规定.在本题中,斜堆中各个元素的值均不相同. 在斜堆H中插入新元素X的过程是递归进行的:当H为空或者X小于H的根结点时X变为新的树根,而原来的树根(如果有的话)变为X的左儿子.当X大于H的根结点时,H根结点的两棵子树交换,而X(递归)插入到交换后的左子树中. 给出一棵

【BZOJ 1078】 [SCOI2008]斜堆

1078: [SCOI2008]斜堆 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 432  Solved: 250 [Submit][Status] Description 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小.但斜堆不必是平衡的,每个结点的左右儿子的大小关系也没有任何规定.在本题中,斜堆中各个元素的值均不相同. 在斜堆H中插入新元

结构之美——优先队列基本结构(四)——二叉堆、d堆、左式堆、斜堆

实现优先队列结构主要是通过堆完成,主要有:二叉堆.d堆.左式堆.斜堆.二项堆.斐波那契堆.pairing 堆等. 1. 二叉堆 1.1. 定义 完全二叉树,根最小. 存储时使用层序. 1.2. 操作 (1). insert(上滤) 插入末尾 26,不断向上比较,大于26则交换位置,小于则停止. (2). deleteMin(下滤) 提取末尾元素,放在堆顶,不断下滤: (3). 其他操作: 都是基于insert(上滤)与deleteMin(下滤)的操作. 减小元素:减小节点的值,上滤调整堆. 增大