堆之左倾堆

左倾堆

堆(heap)被为优先队列(priority queue),但并不是队列按先后顺序来取出元素,而是按照元素的优先级。上一节的 索引堆二叉堆 也是。

这一节我们来介绍左倾堆

左倾堆的介绍

左偏树(英语:leftist tree或leftist heap),也可称为左偏堆、左倾堆,是计算机科学中的一种树,是一种优先队列实现方式 ━━> 维基百科

当优先队列需要解决“两个优先队列合并”的问题时,二叉堆的效率就比较差了,这时左倾堆等方式可以很好的解决这类问题。左倾堆合并时间复杂度为O(lgn)。左倾堆在统计问题,最值问题,模拟问题和贪心问题等问题中有广泛的应用。 左倾堆是二叉树,另外还有两个属性,键值和零距离。 ━━> 来自这里

不同于斜堆合并的平均情况复杂度,左偏堆的合并操作的最坏情况复杂度为O(log n),而完全二叉堆为O(n),所以左偏堆适合基于合并操作的情形。

由于左偏堆已经不是完全二叉树,因此不能用数组存储表示,需要用链接结构。

  • 左子树
  • 右子树
  • 键值
  • NPL

键值

用来比较节点的大小

零距离( NPL )

是 null path length 的缩写,指的是从该结点到达一个没有两个孩子的结点(一个或无孩子)的最短距离,叶节点的NPL为0, NULL的NPL为-1。

左倾堆的性质

  • 节点的优先级大于或等于它子节点的优先级
  • 任意节点左孩子的NPL >= 右孩子的NPL
  • 节点的NPL = 它的右孩子的NPL + 1

下图是 NPL 的示意图:

合并操作图解

合并操作是左倾堆的重点。合并两个左倾堆的过程如下:

  1. 如果一个空左倾堆与非空左倾堆合并,返回非空左倾堆;
  2. 如果两个左倾堆都非空,比较两个根节点,取优先级高的根节点为新的根节点。将“优先级较高堆的根节点的右孩子”和“优先级底的堆”进行合并;
  3. 如果新堆的右孩子的NPL > 左孩子的NPL,则交换左右孩子;
  4. 设置新堆的根节点的NPL = 右子堆NPL + 1;

下图将是我们合并的两个左倾堆:

第一步 将 较小堆(根为10)的右孩子较大堆(根为12) 进行合并

因为 根为 10 的堆根为 12 的堆 优先级高,根为 10 的根节点 作为新节点

第二步 将上一步得到的 根 12 的右子树根为 15 的树 进行合并

第三步 将上一步得到的 根 15 的右子树根为 22 的树 进行合并

第四步 将上一步得到的 根 22 的右子树根为 27 的树 进行合并

至此,已经成功的将两棵树合并成为一棵树了。接下来,对新生成的树进行调节。

第五步 上一步得到的 树22的右孩子的NPL > 左孩子的NPL ,因此交换左右孩子

第六步 上一步得到的 树13的右孩子的NPL > 左孩子的NPL ,因此交换左右孩子

第七步 上一步得到的 树10的右孩子的NPL > 左孩子的NPL ,因此交换左右孩子

至此,合并完毕。上面就是合并得到的左倾堆!

最后,Demo 在这里

参考

纸上谈兵: 左倾堆 (leftist heap)
左倾堆的C语言实现