二项堆

来源:互联网 发布:剑灵数据库下载 编辑:程序博客网 时间:2024/05/21 10:26

一、二项树

二项树是一种通过递归定义的有序树,其定义如下:
  1. 度数为0的二项树只包含一个结点
  2. 度数为k的二项树有一个根结点,根结点下有 个子女,每个子女分别是度数为k-1,k-2,…,0的二项树的根
也可以按照如下定义:
  1. 度数为0的二项树只包含一个结点
  2. 度为k的二项树由两棵度为k-1的二项树通过连接而组成,其连接方式是:其中一棵树的根成为另一棵树的最左边的孩子。
 
上图中从左到右分别为度为0,1,2的二项树。
二项树Bk具有如下的性质(k表示树的度):
  1. 该树含有2^k个节点。证明很简单,因为度数为k的二项树由两棵度数为k-1的二项树组成,因而其节点数目等于2 * (度数为k-1的二项树的节点数),用数学归纳法易证
  2. 树的高度是k。这个性质有其连接方式可得,由于连接时,一棵度为k-1的二项树要成为另一棵度为k-1的二项树的子树,因而新得到的度为k的二项树的高度为度为k-1的二项树的高度+1,用数学归纳法可证该性质。
  3. 在深度d中含有C(k,d)个节点,其中d = 0, 1,2 ... , k。同样根据二项树的连接方式,度为k的二项树中深度d处节点数= 度为k-1的二项树深度为d处的节点数 +度为k-1的二项树深度为d-1处的节点数,用归纳法易证。
  4. 树的根节点的度为k,其它节点的度都小于k。

二、二项堆

二项堆是指满足以下性质的二项树的集合:
  1. 每棵二项树都满足最小堆性质,即结点关键字大于等于其父结点的关键字
  2. 不能有两棵或以上的二项树有相同度数(包括度数为0)
以上第一个性质保证了二项树的根结点包含了最小的关键字。第二个性质则说明结点数为 的二项堆最多只有logn + 1棵二项树。
二项堆中各个二项树的根被保存在一个称为根表的链表中,该链表保存了二项堆中所有二项树的根节点。
下图即为一个二项堆:
 

三、二项堆上的操作

3.1 合并度数相同的二项树

合并度数相同的二项树是二项树的基本操作也是二项堆中其它操作的基础。正如前边所说,这是通过让一棵二项树的根成为另一棵二项树最左边的孩子来实现的。 

3.2合并二项堆

合并二项堆是建立在合并二项树的基础上的,唯一需要注意的是这里的合并需要保证二项堆的性质,即保证合并后得到的二项树的根具有最小的值,这只需要比较需要合并的两棵二项树根节点的大小即可,具有较小的根节点值的二项树的根将成为新的二项树的根。下图即为一个示例:


在此基础上合并两个二项堆的算法如下:
  1. 将两个二项堆的根表按照二项树的度数大小排序的方式合并为一个新的根表
  2. 按照树的度数从小到大来来遍历新得到的根表:
a)如果当前二项树的度数上只有这一棵二项树,则接着判断处理根表中的下一个二项树
b) 如果某个度数上有三棵二项树,则将后边两棵按照度数相同二项树的合并方法合并,并得到一棵新的二项树,然后从新得到的二项树的根开始向后遍历
c) 如果某个度数上有两棵二项树,则将它们按照度数相同二项树的合并方法合并,然后从新得到的二项树的根开始向后遍历

由于合并两个二项树的的时间复杂度为O(1),因而合并二项堆的时间复杂度取决于要合并多少个二项树,由于二项堆中二项树个数为logn + 1,而每次合并两棵二项树至少能让遍历的指针前移一个位置或者从根表中删除一个节点(即减少二项树的个数),因而合并二项堆的时间复杂度为O(logn)。

从二项堆的合并算法可知,二项堆中各个二项树的根在根表中是有序的,排序的依据是各树的度数大小。

3.3 插入一个节点

基于二项树的合并操作,插入一个节点非常简单,其思想是:
  1. 创建一个只包含要插入关键字的二项堆
  2. 将此堆与原先的二项堆进行合并,即可得到插入后的堆
显然该操作的时间复杂度和合并相同,为O(logn)。

3.4 查找最小关键字所在结点

由于满足最小堆性质,只需查找二项树的的根结点即可,因为一共有logn棵子树,所以用所时间为O(logn)。

3.5 删除一个节点

同样的基于二项树的合并操作,该操作也很简单,其思想为:
  1. 找到该关键字所在节点
  2. 如果该关键字是它所在的二项树的根节点,则获得它的子树,并将它删除,然后执行第4步
  3. 否则将它和它的父节点的值交换,然后将其父节点看做是被删除的节点并判断当前节点是否是根节点,该过程一直持续,直到被删除的节点的值被交换到其所在二项树的根节点上,然后获得该二项树根节点的子树,并将根节点删除,然后执行下一步
  4. 将这些子树看作一个独立的二项堆,将此堆合并到原先的堆中即可

由于每棵树最多有logn + 1棵子树,因而这里创建新堆的时间为O(logn),又因为合并堆的时间复杂度也为O(logn),故整个操作所需时间为O(logn)。

一个删除的例子如下所示

要从其中删除节点89,第一步,先找到节点89并将其上升到其所在二项树的根节点,得到下图:

第二步,删除节点89,得到两个二项堆,得到下图:

第三步,将这两个二项堆的根表合并得到下图:

第四步,遍历并合并度相同的二项树,首先发现度为0的有两棵,将其合并可得:

然后发现度为1的有三棵,将后边两棵合并可得:

这就是最终得到的删除节点并调整后的新的二项堆。

二项堆最大的优势在于当需要合并两个二项堆时,其时间复杂度为O(lgn),具有较好的时间复杂度。

原创粉丝点击