[平衡树]Tree(BST) + Heap = Treap

xiaoxiao2021-02-28  140

引入

首先其实平衡树就是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; }
转载请注明原文地址: https://www.6miu.com/read-34810.html

最新回复(0)