[平衡树]Tree(BST) + Heap = Treap
来源:互联网 发布:都有哪些美食kol 知乎 编辑:程序博客网 时间:2024/06/07 12:59
引入
首先其实平衡树就是BST,即二叉查找树,然后尽量使这棵BST保持平衡。
我们知道BST容易退化,所以就有人发明了AVL树和红黑树,虽然这两种树的运行速度很快=_=,但是代码很复杂, 插入删除旋转情况有很多种,才保证了它的平衡,所以后来又出现了Treap这种平衡树。
虽然Treap比较简单,但是时间上却要稍稍差一点,而且有极小到几乎没有的可能(但是还是有)会退化,但显然在平时也已经够用了, 所以这里先来看看Treap。
数据结构的储存
首先因为这是一棵二叉查找树,所以每个节点必然要存两个儿子和它本身的值,然后在这个基础上,加上一个值,作为它的优先级,那么优先级有什么用,待会儿再说。
实现原理
通过题目可以知道,Treap就是Tree(BST)+Heap
Heap就是堆,所以一棵Treap肯定首先满足了BST的性质,然后怎么满足Heap的性质呢
那么对于BST有一个简单的优化,就是将原来的数列随机打乱后插入。那么其实Treap的思路也差不多,是在插入一个点的时候,然后给它一个随机的优先级,然后通过旋转使它保持堆性质。那么为什么旋转能在保持BST性质下,修改出Heap性质呢。先来看图。
很显然地,就是根节点在左旋的时候,根节点变成了右节点的左结点(有点绕),然后把原来右节点的左子树变成它的右子树,然后这样很显然是会保持BST性质的,右旋则反过来。
所以其实不必硬记,第一是打多了就熟悉了,第二是这是可以现场推导的,如果硬记反而麻烦←_←
那么有了旋转来看插入,只需要将一个点按照BST的插入方法插入为根节点,然后再往根节点往回递归的过程中,不停旋转,将不满足堆性质的子树变成满足的子树,最终会让整棵树依旧保持堆性质。
删除其实同理,只是将插入操作反过来运行,也就是将要删除的点通过旋转转成叶子结点,除了这个点以外使这棵BST依旧保持堆性质,然后直接删掉。
具体操作可以看代码。
最后写出来的代码应该是和set一样都具有一样功能的,但是Treap能实现的作用远远不止,还需要看后面的练习。
代码
#include <ctime>#include <cstdio>#include <cstdlib>struct Treap { struct Node { Node *ch[2]; int r; int v; int cmp(int x) const { if(x == v) return -1; return x < v ? 0 : 1; } } *root; void rotate(Node* &o, int d) { Node* k = o -> ch[d ^ 1]; o -> ch[d ^ 1] = k -> ch[d]; k -> ch[d] = o; o = k; } void insert(Node* &o, int x) { if(o == NULL) { o = new Node(); o -> ch[0] = o -> ch[1] = NULL; o -> v = x; o -> r = rand(); } else { int d = o -> cmp(x); if(o -> ch[d], x); if(o -> ch[d] -> r > o -> r) { rotate(o, d ^ 1); } } } void remove(Node* &o, int x) { int d = o -> cmp(x); if(d == -1) { if(o -> ch[0] == NULL) { o = o -> ch[1]; } else { if(o -> ch[1] == NULL) { o = o -> ch[0]; } else { int d2 = (o -> ch[0] -> r > o -> ch[1] -> r ? 1 : 0); rotate(o, d2); remove(o -> ch[d2], x); } } } else { remove(o -> ch[d], x); } } int find(Node* o, int x) { while(o != NULL) { int d = o -> cmp(x); if(d == -1) return 1; else o = o -> ch[d]; } return 0; }} treap;int main(void) { srand(time(0)); int n, m; scanf("%d%d", &n, &m); for(int i = 0; i < n; ++i) { int x; scanf("%d", &x); treap.insert(treap.root, x); } for(int i = 0; i < m; ++i) { int a; scanf("%d", &a); int b; if(a == 0) { scanf("%d", &b); treap.remove(treap.root, b); } else { scanf("%d", &b); printf("%s\n", treap.find(treap.root, b) ? "existent" : "nonexistent"); } } return 0;}
- [平衡树]Tree(BST) + Heap = Treap
- Treap=Tree+Heap! 各种BST大PK(模板)
- Java基础 - 树堆(Treap = tree+heap)
- [平衡树]Treap实现Rank Tree
- 各种平衡树Treap/SBT/Avl/Splay tree
- 【noip】开车旅行 平衡树 倍增 treap tree
- TREAP平衡树代码
- Treap平衡树
- Treap平衡树
- Treap平衡树
- Treap平衡树
- TREAP平衡树
- 平衡树Treap模版
- 【平衡树】Treap
- 平衡树Treap
- 平衡树之treap
- 普通平衡树 treap
- 【学习】Treap平衡树
- winlogbeat监听windows日志到kafka、hdfs,不同层级取数据
- Packet for query is too large (1029 > 1024). You can change this value on the server by setting the
- Postgresql中的类型转换&&pg_cast.md
- echarts 市级地图注意事项
- (37)接口实现和覆盖时的异常特点
- [平衡树]Tree(BST) + Heap = Treap
- CSS 控制单行文本和多行文本的显示区域不够存放时使用省略号...来替代的做法
- 无根树任意根深度加强版
- JavaScript设计模式总结
- JAVA 生产者与消费者模式
- git 命令大全
- vb.net 教程 4-2 目录操作 DirectoryInfo 5
- 接口与抽象的区别
- 在Ubuntu系统下装Win7并引导双系统,重装windows后修复grub2引导