二项堆(三)之 Java的实现(一)

2014-11-23 23:13:43 · 作者: · 浏览: 4
概要
前面分别通过C和C++实现了二项堆,本章给出二项堆的Java版本。还是那句老话,三种实现的原理一样,择其一了解即可。
目录
1. 二项树的介绍
2. 二项堆的介绍
3. 二项堆的基本操作
4. 二项堆的Java实现(完整 源码)
5. 二项堆的 Java测试程序
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3656098. html
更多内容:数据结构与算法系列 目录
(01) 二项堆(一)之 图文解析 和 C语言的实现
(02) 二项堆(二)之 C++的实现
(03) 二项堆(二)之 Java的实现
二项树的介绍
二项树的定义
二项堆是二项树的集合。在了解二项堆之前,先对二项树进行介绍。
二项树是一种递归定义的有序树。它的递归定义如下:
(01) 二项树B0只有一个结点;
(02) 二项树Bk由两棵二项树B(k-1)组成的,其中一棵树是另一棵树根的最左孩子。
如下图所示:
上图的B0、B1、B2、B3、B4都是二项树。对比前面提到的二项树的定义:B0只有一个节点,B1由两个B0所组成,B2由两个B1所组成,B3由两个B2所组成,B4由两个B3所组成;而且,当两颗相同的二项树组成另一棵树时,其中一棵树是另一棵树的最左孩子。
二项树的性质
二项树有以下性质:
[性质一] Bk共有2k个节点。
如上图所示,B0有20=1节点,B1有21=2个节点,B2有22=4个节点,...
[性质二] Bk的高度为k。
如上图所示,B0的高度为0,B1的高度为1,B2的高度为2,...
[性质三] Bk在深度i处恰好有C(k,i)个节点,其中i=0,1,2,...,k。
C(k,i)是高中数学中阶乘元素,例如,C(10,3)=(10*9*8) / (3*2*1)=240
B4中深度为0的节点C(4,0)=1
B4中深度为1的节点C(4,1)= 4 / 1 = 4
B4中深度为2的节点C(4,2)= (4*3) / (2*1) = 6
B4中深度为3的节点C(4,3)= (4*3*2) / (3*2*1) = 4
B4中深度为4的节点C(4,4)= (4*3*2*1) / (4*3*2*1) = 1
合计得到B4的节点分布是(1,4,6,4,1)。
[性质四] 根的度数为k,它大于任何其它节点的度数。
节点的度数是该结点拥有的子树的数目。
注意:树的高度和深度是相同的。关于树的高度的概念,《算法导论》中只有一个节点的树的高度是0,而"维基百科"中只有一个节点的树的高度是1。本文使用了《算法导论中》"树的高度和深度"的概念。
二项堆的介绍
二项堆和之前所讲的堆(二叉堆、左倾堆、斜堆)一样,也是用于实现优先队列的。二项堆是指满足以下性质的二项树的集合:
(01) 每棵二项树都满足最小堆性质。即,父节点的关键字 <= 它的孩子的关键字。
(02) 不能有两棵或以上的二项树具有相同的度数(包括度数为0)。换句话说,具有度数k的二项树有0个或1个。
上图就是一棵二项堆,它由二项树B0、B2和B3组成。对比二项堆的定义:(01)二项树B0、B2、B3都是最小堆;(02)二项堆不包含相同度数的二项树。
二项堆的第(01)个性质保证了二项堆的最小节点就是某个二项树的根节点,第(02)个性质则说明结点数为n的二项堆最多只有log{n} + 1棵二项树。实际上,将包含n个节点的二项堆,表示成若干个2的指数和(或者转换成二进制),则每一个2个指数都对应一棵二项树。例如,13(二进制是1101)的2个指数和为13=23 + 22+ 20, 因此具有13个节点的二项堆由度数为3, 2, 0的三棵二项树组成。
二项堆的基本操作
二项堆是可合并堆,它的合并操作的复杂度是O(log n)。
1. 基本定义
复制代码
public class BinomialHeap> {
private BinomialNode mRoot; // 根结点
private class BinomialNode> {
T key; // 关键字(键值)
int degree; // 度数
BinomialNode child; // 左孩子
BinomialNode parent; // 父节点
BinomialNode next; // 兄弟节点
public BinomialNode(T key) {
this.key = key;
this.degree = 0;
this.child = null;
this.parent = null;
this.next = null;
}
public String toString() {
return "key:"+key;
}
}
...
}
复制代码
BinomialNode是二项堆的节点。它包括了关键字(key),用于比较节点大小;度数(degree),用来表示当前节点的度数;左孩子(child)、父节点(parent)以及兄弟节点(next)。
BinomialHeap是二项堆对应的类,它包括了二项堆的根节点mRoot以及二项堆的基本操作的定义。
下面是一棵二项堆的树形图和它对应的内存结构关系图。
2. 合并操作
合并操作是二项堆的重点,它的添加操作也是基于合并操作来实现的。合并两个二项堆,需要的步骤概括起来如下:
(01) 将两个二项堆的根链表合并成一个链表。合并后的新链表按照"节点的度数"单调递增排列。
(02) 将新链表中"根节点度数相同的二项树"连接起来,直到所有根节点度数都不相同。
下面,先看看合并操作的代码;然后再通过示意图对合并操作进行说明。
merge()代码(Java)
View Code
link()代码(Java)
View Code
合并操作代码(Java)
复制代码
1 /*
2 * 合并二项堆:将h1, h2合并成一个堆,并返回合并后的堆
3 */
4 private BinomialNode union(BinomialNode h1, BinomialNode h2) {
5 BinomialNode root;
6
7 // 将h1, h2中的根表合并成一个按度数递增的链表root