您的位置:首页 > 其它

Treap树

2016-03-04 09:24 211 查看
1:定义

1     #region Treap树节点
2     /// <summary>
3     /// Treap树
4     /// </summary>
5     /// <typeparam name="K"></typeparam>
6     /// <typeparam name="V"></typeparam>
7     public class TreapNode<K, V>
8     {
9         /// <summary>
10         /// 节点元素
11         /// </summary>
12         public K key;
13
14         /// <summary>
15         /// 优先级(采用随机数)
16         /// </summary>
17         public int priority;
18
19         /// <summary>
20         /// 节点中的附加值
21         /// </summary>
22         public HashSet<V> attach = new HashSet<V>();
23
24         /// <summary>
25         /// 左节点
26         /// </summary>
27         public TreapNode<K, V> left;
28
29         /// <summary>
30         /// 右节点
31         /// </summary>
32         public TreapNode<K, V> right;
33
34         public TreapNode() { }
35
36         public TreapNode(K key, V value, TreapNode<K, V> left, TreapNode<K, V> right)
37         {
38             //KV键值对
39             this.key = key;
40             this.priority = new Random(DateTime.Now.Millisecond).Next(0,int.MaxValue);
41             this.attach.Add(value);
42
43             this.left = left;
44             this.right = right;
45         }
46     }


节点里面定义了一个priority作为“堆定义”的旋转因子,因子采用“随机数“。

 

2:添加

    首先我们知道各个节点的“优先级”是采用随机数的方法,那么就存在一个问题,当我们插入一个节点后,优先级不满足“堆定义"的

时候我们该怎么办,前辈说此时需要旋转,直到满足堆定义为止。

旋转有两种方式,如果大家玩转了AVL,那么对Treap中的旋转的理解轻而易举。

①: 左左情况旋转



从图中可以看出,当我们插入“节点12”的时候,此时“堆性质”遭到破坏,必须进行旋转,我们发现优先级是6<9,所以就要进行

左左情况旋转,最终也就形成了我们需要的结果。

 

②: 右右情况旋转



既然理解了”左左情况旋转“,右右情况也是同样的道理,优先级中发现“6<9",进行”右右旋转“最终达到我们要的效果。

1         #region 添加操作
2         /// <summary>
3         /// 添加操作
4         /// </summary>
5         /// <param name="key"></param>
6         /// <param name="value"></param>
7         public void Add(K key, V value)
8         {
9             node = Add(key, value, node);
10         }
11         #endregion
12
13         #region 添加操作
14         /// <summary>
15         /// 添加操作
16         /// </summary>
17         /// <param name="key"></param>
18         /// <param name="value"></param>
19         /// <param name="tree"></param>
20         /// <returns></returns>
21         public TreapNode<K, V> Add(K key, V value, TreapNode<K, V> tree)
22         {
23             if (tree == null)
24                 tree = new TreapNode<K, V>(key, value, null, null);
25
26             //左子树
27             if (key.CompareTo(tree.key) < 0)
28             {
29                 tree.left = Add(key, value, tree.left);
30
31                 //根据小根堆性质,需要”左左情况旋转”
32                 if (tree.left.priority < tree.priority)
33                 {
34                     tree = RotateLL(tree);
35                 }
36             }
37
38             //右子树
39             if (key.CompareTo(tree.key) > 0)
40             {
41                 tree.right = Add(key, value, tree.right);
42
43                 //根据小根堆性质,需要”右右情况旋转”
44                 if (tree.right.priority < tree.priority)
45                 {
46                     tree = RotateRR(tree);
47                 }
48             }
49
50             //将value追加到附加值中(也可对应重复元素)
51             if (key.CompareTo(tree.key) == 0)
52                 tree.attach.Add(value);
53
54             return tree;
55         }


 

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         }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: