主要应用
首先要知道左偏树是用来干什么的。如果给我们两个优先序列,然后让我把这两个优先队列合并成一个优先队列。如果直接用堆,就是将一个队列里面的数不断弹出然后扔到另一个队列里。复杂度是(O(n))n为队列中数的个数。但是用左偏树就可以做到(log(n_1 + n_2))。
PS:为了便于讨论,本文所有的左偏树均已小根树为例。
两个定义
外节点: 至少一个儿子为空的节点称为外节点。
距离: 某个节点的距离定义为这个节点到他的子树中离他最近的那个外节点的距离。这个的作用读完全文可能就明白了。
左偏树的性质
性质1:任何一个节点的儿子一定比这个节点的数小。
性质2:任何一个节点的左儿子的距离一定大于等于右儿子的距离
性质3:任何一个节点的距离等于他的右儿子的距离+1。(空节点的距离为-1)
性质4:一棵n个节点的左偏树中,距离最大的节点的距离不超过(log(n + 1) -1)
合并
继续考虑一开始那个合并两个优先队列的问题。首先我们肯定不想一个一个的加入队列。而是最好可以通过改变一些节点的儿子,来使得左偏树的性质得到维护。
那么左偏树是怎么实现的呢?
如图
既然叫左偏树,那么就比较偏向左边嘛。所以每次加入的时候都只想改变右孩子。
我们现在要合并上面两颗左偏树。
1:先比较两棵树的根节点。发现第一棵树比较小,所以就考虑将第二棵树加到第一棵树的右子树上去。所以就递归的合并第二棵子树和第一个子树的右子树。并且合并后的根作为1号节点的右儿子。
2:发现2号节点比4号节点小,所以1号节点的右儿子变为2,然后继续合并2号节点的右子树和以4为根节点的子树。
3:发现4比5小,所以把2号节点的右儿子变为4,然后合并4号节点的右儿子和5号节点。
4:发现4号节点右儿子为空,返回
5:维护左偏树的性质,更新每个结点的距离。并且如果左儿子的距离比右儿子要小,那么就交换两个儿子。
最终得到一棵这样的左偏树
为什么每次都是与右儿子合并?
这时就要说到距离的用处了。既然有性质2,那么与右儿子合并肯定比与左儿子合并更早结束递归(因为结束递归的时刻是当发现要合并的两个节点是外节点的时候)。
操作
合并操作
上面已经说了
删除操作
将根的左右儿子都变为零,然后将左右子树合并起来就行了。
插入操作
插入一个几点就相当于插入一个只有一个节点的左偏树
例题
一言
时间再拉长一点,让我有时间收拾一下心情。 ——火影忍者