前言
您需要写一种数据结构,来维护一些数,其中需要提供一下的操作:
1.插入数值x
2.删除数值x(如果有多个,只删除一个)
3.查询数值x的排名(如有多个相同的数,应输出最小的排名)
4.查询排名为x的数值
5. 求数值x的前驱(小于x最大的数)
6.求数值x的后继(大于x最小的数)
首先我们引入一种数据结构叫二叉查找树(BST)。该树满足一下性质:
对于树的任意一个节点
1.该节点的关键码不小于它的左子树中任意节点的关键码
2.该节点的关键码不大于它的右子树中任意节点的关键码
通俗一点来讲就是,一个节点的左边都比他小,右边都比他大QWQ
显然,二叉查找树的中序遍历是一个单调递增的节点序列……
那么我们来考虑解决上面的普通平衡树(因为不是主角就不重点讲啦!!)
1.插入:从根节点开始访问,小了向左走,大了向右走,找不到就新建点啦
2.删除:同插入过程啦啦啦啦
3.查询排名:因为左子树的点都比此节点小,那抹排名就是左子树的size(大小)+1啦啦啦,求size应该都会吧……
4.查询数值:如果左子树的大小比要求排名大,就往左走,否则往右走,直到一样……
5.前驱:首先我们先找到这个数,因为左子树的点都比这个数小,所以向左儿子走一步,然后一直向右边走,走到头就是啦
6.后继:同理啦,向右儿子走一步,然后一直向左走
是不是感觉BST超简单的,没错就是这么简单……但是你没发现什么问题嘛??
不不不
现在我按顺序插入1,2,3,4,5
树就变成了这样
我要是按顺序插入1,2,3……10^5
这特么不就变成链表了么
实际上就算不这么极端,也能分分钟把这种最单纯的2x查找树搞死
很显然这样是过不了普通平衡树的……
是不是感觉刚才看的都白看了,不不不,下面要将的treap就是基于2x进行优化的……
那么该怎么优化呢???
我们发现2x查找树的不足之处就在于它太深了(太深了……咳咳咳),因此我们就想减少它的深度,通俗的来讲就是把它拍扁。那么如何实现呢……
旋转(不转不是中国人……)
右旋,是让某个节点的左儿子站到自己原来的位置,然后自己成为原来左儿子的右儿子(左旋同理啦!!请自行脑补)可以看下面的动来动去的图
合理的旋转操作可以是BST变得更扁(you)平(xiu)。那么问题来了,怎么样才算合理呢???根据研究发现,在随机的数据下,普通的BST可以虐杀其他一切高级的树。Treap的思想就是利用“随机”来创造平衡的条件。因为在旋转的过程中必须维持BST的性质,所以Treap就把“随机”作用在堆性质上。
其实Treap就是Tree和Heap的合成词啦。Treap在插入每个新节点的时候,就给该节点随机生成一个额外的权值。然后像二叉堆的插入过程一样,自底向上依次检查,当某个节点不满足大根堆性质