3. Treap的操作
同其他树形结构一样,treap的基本操作有:查找,插入,删除等。
3.1 查找
同其他二叉树一样,treap的查找过程就是二分查找的过程,复杂度为O(lg n)。
3.2 插入
在Treap 中插入元素,与在BST 中插入方法相似。首先找到合适的插入位置,然后建立新的节点,存储元素。但是要注意新的节点会有一个优先级属性,该值可能会破坏堆序,因此我们要根据需要进行恰当的旋转。具体方法如下:
1. 从根节点开始插入;
2. 如果要插入的值小于等于当前节点的值,在当前节点的左子树中插入,插入后如果左子节点的优先级小于当前节点的优先级,对当前节点进行右旋;
3. 如果要插入的值大于当前节点的值,在当前节点的右子树中插入,插入后如果右子节点的优先级小于当前节点的优先级,对当前节点进行左旋;
4. 如果当前节点为空节点,在此建立新的节点,该节点的值为要插入的值,左右子树为空,插入成功。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
Treap_Node *root;
void
Treap_Insert(Treap_Node *&P,
int
value)
//节点指针一定要传递引用 p为要插入树的根节点
{
if
(!P)
//
1
当P为空时
{
P=
new
Treap_Node;
P->value=value;
P->fix=
rand
();
//生成随机的修正值 fix为节点的优先值
}
else
if
(value <= P->value)
{
Treap_Insert(P->left,r);
if
(P->left->fix < P->fix)
Treap_Right_Rotate(P);
//左子节点优先级值小于当前节点优先级值,右旋当前节点
}
else
{
Treap_Insert(P->right,r);
if
(P->right->fix < P->fix)
Treap_Left_Rotate(P);
//右子节点修正值小于当前节点修正值,左旋当前节点
}
}
|
3.3:删除
跟普通的二叉查找树一样,删除结点存在三种情况。
①:叶子结点
跟普通查找树一样,直接释放本节点即可。
②:单孩子结点
跟普通查找树一样操作。
③:满孩子结点
其实在treap中删除满孩子结点有两种方式。
第一种:跟普通的二叉查找树一样,找到“右子树”的最左结点(15),拷贝元素的值,但不拷贝元素的优先级,然后在右子树中
删除“结点15”即可,最终效果如下图。
第二种:将”结点下旋“,直到该节点不是”满孩子的情况“,该赋null的赋null,该将孩子结点顶上的就顶上,如下图:
当然从理论上来说,第二种删除方法更合理,这里我写的就是第二种情况的代码。
1 #region 删除当前树中的节点 2 /// <summary> 3 /// 删除当前树中的节点 4 /// </summary> 5 /// <param name="key"></param> 6 /// <returns></returns> 7 public void Remove(K key, V value) 8 { 9 node = Remove(key, value, node); 10 } 11 #endregion 12 13 #region 删除当前树中的节点 14 /// <summary> 15 /// 删除当前树中的节点 16 /// </summary> 17 /// <param name="key"></param> 18 /// <param name="tree"></param> 19 /// <returns></returns> 20 public TreapNode<K, V> Remove(K key, V value, TreapNode<K, V> tree) 21 { 22 if (tree == null) 23 return null; 24 25 //左子树 26 if (key.CompareTo(tree.key) < 0) 27 { 28 tree.left = Remove(key, value, tree.left); 29 } 30 //右子树 31 if (key.CompareTo(tree.key) > 0) 32 { 33 tree.right = Remove(key, value, tree.right); 34 } 35 /*相等的情况*/ 36 if (key.CompareTo(tree.key) == 0) 37 { 38 //判断里面的HashSet是否有多值 39 if (tree.attach.Count > 1) 40 { 41 //实现惰性删除 42 tree.attach.Remove(value); 43 } 44 else 45 { 46 //有两个孩子的情况 47 if (tree.left != null && tree.right != null) 48 { 49 //如果左孩子的优先级低就需要“左旋” 50 if (tree.left.priority < tree.right.priority) 51 { 52 tree = RotateLL(tree); 53 } 54 else 55 { 56 //否则“右旋” 57 tree = RotateRR(tree); 58 } 59 60 //继续旋转 61 tree = Remove(key, value, tree); 62 } 63 else 64 { 65 //如果旋转后已经变成了叶子节点则直接删除 66 if (tree == null) 67 return null; 68 69 //最后就是单支树 70 tree = tree.left == null ? tree.right : tree.left; 71 } 72 } 73 } 74 75 return tree; 76 } 77 #endregion