BST:
概念:
BST(Binary Search Tree):二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
左、右子树也分别为二叉排序树。
应用:
在BST上可以进行这样的操作:
- 插入 \(x\)数
- 删除 \(x\) 数(若有多个相同的数,因只删除一个)
- 查询 \(x\) 数的排名(排名定义为比当前数小的数的个数 +1+1 。若有多个相同的数,因输出最小的排名)
- 查询排名为 $ x$ 的数
- 求 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)
- 求 \(x\) 的后继(后继定义为大于 \(x\),且最小的数)
实现:
(1.2操作略,s为子树大小)
3.求排名
1 | int rank(int x,node* o=root) |
4.求第k大
1 | int kth(int k,node* o=root) |
5.求前驱
1 | //全局变量prec |
6.求后继
1 | //全局变量succ |
平衡树
随机数据下,树期望高度为\(O(\log n)\)。所以,当树平衡时,所有这些操作时间都为\(O(\log n)\)。但树不一定平衡:按递增或递减顺序插入元素,树就会退化成一条链。在实际应用中,树几乎不会非常平衡。所以,朴素BST表现不佳。
易知,元素相同的BST可能有多种形态(朴素BST的形态与插入顺序有关)。我们我们采用一些方式来使BST的形态发生变化,但仍然为合法BST。这样树就变得平衡。
常见的BST有Splay,Treap,替罪羊树,红黑树,SBT等。
还有权值线段树、01trie、跳表等
大多数BST基于旋转操作。
旋转:
令一个节点换到它的父亲位置上。为了维护BST性质,节点的子节点的位置也会跟着发生变化。 (这里原来有图片,但是挂了)
令x为被旋转节点,y为其父亲,则:
x变到原来y的位置
y与x的位置关系与原来x与y的位置关系相反(如:x为y的左儿子,则后来y为x的右儿子)
y非x的儿子不变,x的与x.y共线的儿子不变
原来x位置的节点变成了x的与x.y不共线的儿子
代码实现大概是这个样子(建议假设x是左儿子来理解,此时k==0):
1 | //数组版: |
数组和指针操作明显不同是因为我学习时抄的模板不是一个人写的
Treap
- Treap=Tree+Heap
treap的每个节点有一个随机的优先级。对于键值而言,它是BST;对于优先级而言,它是堆(即,每棵子树中,根的优先级总是最大的)。因为优先级随机,所以树期望平衡。虽然基于随机,但不容易被卡掉,跑得也很快。
我采用刘汝佳式的指针写法,节点定义如下:
1 | struct node |
insert
insert操作基本思想为dfs找到新节点应该在的位置并新建该节点,然后回溯判断是否违反堆性质并通过旋转维护堆性质。(注意对重复元素的处理)
1 | void insert(node* &o,int v) |
remove
当要删除的节点只有一个儿子时,删除操作很简单:直接用这个儿子代替它即可。remove操作正是基于这样的理念:将要删除的节点向下转到有至少一棵子树为空,然后删除。若有子树非空,则用非空的儿子代替它。
1 | void remove(node* &o,int v) |
完整实现如下:
1 |
|