一 可合并堆
1.1 斐波那契堆和二项堆时间复杂度比较
二 斐波那契堆结构
Java 定义堆以及结点结构如下:
public class FibHeap<T extends Comparable> {
private FibNode<T> min; // 指向最小的结点
private int size; //结点总数目
private final class FibNode<T>{
T val;
FibNode<T> right; //右兄弟结点;
FibNode<T> left; // 左兄弟结点;
FibNode<T> parent; // 父节点
FibNode<T> child; // 指向第一个孩子结点;
int degree; //具有孩子结点的个数;
boolean marked; //该标记表示该结点是否失去过一个孩子结点;
public FibNode(T val) {
this.val = val;
}
}
}
三、可合并堆操作
3.1 插入结点
伪代码如下:
Java 实现:
public void insert(T t){
FibNode<T> node = new FibNode<T>(t); //创建结点;
addNode(node);
}
public void addNode(FibNode<T> node){
if(min == null) {
min = node;
min.left = min;
min.right = min;
}
else{
node.right = min.right;
min.right.left = node;
min.right = node;
node.left = min;
}
if(node.val.compareTo(min.val) < 0)
min = node;
++size;
}
3.2 寻找最小结点
直接通过 min指针指向 即可获得:
public T getMin(){
if(min == null) return null;
return min.val;
}
3.3 合并操作
伪代码如下:
Java实现:
/**
* @param fibHeap 要合并的另一个堆, 加入到当前堆中
*/
public void union(FibHeap<T> fibHeap){
if(fibHeap == null || fibHeap.min == null) return ;
if(min == null){
min = fibHeap.min;
return;
}
FibNode<T> tmp = fibHeap.min.right;
min.right.left = fibHeap.min;
fibHeap.min.right = min.right;
min.right = tmp;
tmp.left = min;
if(min.val.compareTo(fibHeap.min.val) > 0) min = fibHeap.min;
size += fibHeap.size;
}
3.3 抽取最小结点
抽取结点过程较为复杂总体思路是先将最小结点删除并把孩子结点接入到链表中, 在合并具有相同度的结点。
详情请见《算法导论》
public T extractMin(){
if(min == null) return null;
//将每个孩子结点挂在根节点链表上;
FibNode child = min.child;
if(child!=null){
// 兄弟链表也是循环双链表, 直接拼接, 并将各个子节点的parent置为空;
min.right.left = child.left;
child.left.right = min.right;
min.right = child;
child.left = min;
FibNode p = min.right;
while(p.parent!=null){
p.parent = null;
p = p.right;
}
}
if(min == min.right){//根链表只有一个min结点, 此时堆没有元素;
min = null;
}
else{
//删除min结点
FibNode tmp = min.right;
min.left.right = min.right;
min.right.left = min.left;
min.left= null;
min.right = null;
min = tmp;
consolidate();
}
--size;
return min.val;
}
private void consolidate() {
// 结点的最大度 maxDegree <= LOGx(size) x 为 1.618...
int maxDegree = (int)Math.floor(Math.log(size)/Math.log(X));
FibNode[] map = new FibNode[maxDegree+1];
FibNode<T> p = min;
do {
while(map[p.degree]!=null){//此时存在与该结点相同degree的结点,合并结点
if(((T)map[p.degree].val).compareTo(p.val) < 0){
FibNode tmp = map[p.degree];
tmp.right = p.right;
map[p.degree] = p;
p = tmp;
}
FibNode q = map[p.degree];
map[p.degree] = null;
link(p,q);
}
map[p.degree] = p;
p = p.right;
}while(p!=min);
min = null;
//将所有map中的结点连接起来;
for(int i = 0; i <= maxDegree; ++i){
if(map[i] == null) continue;
if(min == null){
min = map[i];
min.left = min;
min.right = min;
}
else{
min.right.left = map[i];
map[i].right = min.right;
min.right = map[i];
map[i].left = min;
}
if(min.val.compareTo(map[i].val) > 0)
min = map[i];
map[i] = null;
}
}
/**
*
* @param p
* @param q
* 将 q 结点连接成 p 的孩子, 需要将q的marked标记清除;
*/
private void link(FibNode<T> p, FibNode q) {
if(p.child==null){
q.left = q;
q.right = q;
}else{
p.child.left.right = q;
q.left = p.child.left;
p.child.left = q;
q.right = p;
}
q.marked = false;
p.child = q;
++p.degree;
}
3.4 关键字值减少
public void decreaseKey(FibNode<T> node, T t){
if(node == null || t.compareTo(node.val) >= 0) return ;
node.val = t;
FibNode<T> parent = node.parent;
if(parent == null || node.val .compareTo(parent.val) >= 0){
// 如果父节点为空或者说没有违反最小堆序;
return ;
}
cut(node); //剪断该结点与父节点联系;
cascadingCut(parent);
}
private void cascadingCut(FibNode<T> node) {
FibNode<T> parent = node.parent;
if(parent == null){
return ;
}
if(parent.marked == false){ //如果父亲结点没有失去过孩子
parent.marked = true;
return ;
}
//失去过一个孩子,要将该结点剪断;
cut(node);
cascadingCut(parent);
}
/**
*
* @param node 待剪断的结点
* 将结点从其父节点链表中移除;
*/
private void cut(FibNode<T> node) {
FibNode<T> parent = node.parent;
if(node == node.parent.child){
//如果是父节点child指针所指
parent.child = parent.child.right;
parent.child.left = node.left;
node.left.right = parent.child;
}else{
node.right.left = node.left;
node.left.right = node.right;
}
if(parent.child == node) //如果父亲链表中一开始只有一个元素;
parent.child = null;
//将该节点加入到根链表中;
node.parent = null;
addNode(node);
--parent.degree;
}
全部代码:
public class FibHeap<T extends Comparable> {
private FibNode<T> min; // 指向最小的结点
private int size; //结点总数目
public final double X = (Math.sqrt(5) + 1) / 2;
private final class FibNode<T>{
T val;
FibNode<T> right; //右兄弟结点;
FibNode<T> left; // 左兄弟结点;
FibNode<T> parent; // 父节点
FibNode<T> child; // 指向第一个孩子结点;
int degree; //具有孩子结点的个数;
boolean marked; //该标记表示该结点是否失去过一个孩子结点;
public FibNode(T val) {
this.val = val;
}
}
public void insert(T t){
FibNode<T> node = new FibNode<T>(t); //创建结点;
addNode(node);
}
public T getMin(){
return min.val;
}
/**
* @param fibHeap 要合并的另一个堆, 加入到当前堆中
*/
public void union(FibHeap<T> fibHeap){
if(fibHeap == null || fibHeap.min == null) return ;
if(min == null){
min = fibHeap.min;
return;
}
//连接两个循环链表;
fibHeap.min.right.right = min.right;
min.right.left = fibHeap.min.right;
min.right = fibHeap.min;
fibHeap.min.left = min;
if(min.val.compareTo(fibHeap.min.val) > 0) min = fibHeap.min;
size += fibHeap.size;
}
public T extractMin(){
if(min == null) return null;
//将每个孩子结点挂在根节点链表上;
FibNode child = min.child;
if(child!=null){
// 兄弟链表也是循环双链表, 直接拼接, 并将各个子节点的parent置为空;
min.right.left = child.left;
child.left.right = min.right;
min.right = child;
child.left = min;
FibNode p = min.right;
while(p.parent!=null){
p.parent = null;
p = p.right;
}
}
if(min == min.right){//根链表只有一个min结点, 此时堆没有元素;
min = null;
}
else{
//删除min结点
FibNode tmp = min.right;
min.left.right = min.right;
min.right.left = min.left;
min.left= null;
min.right = null;
min = tmp;
consolidate();
}
--size;
return min.val;
}
private void consolidate() {
// 结点的最大度 maxDegree <= LOGx(size) x 为 1.618...
int maxDegree = (int)Math.floor(Math.log(size)/Math.log(X));
FibNode[] map = new FibNode[maxDegree+1];
FibNode<T> p = min;
do {
while(map[p.degree]!=null){//此时存在与该结点相同degree的结点,合并结点
if(((T)map[p.degree].val).compareTo(p.val) < 0){
FibNode tmp = map[p.degree];
tmp.right = p.right;
map[p.degree] = p;
p = tmp;
}
FibNode q = map[p.degree];
map[p.degree] = null;
link(p,q);
}
map[p.degree] = p;
p = p.right;
}while(p!=min);
min = null;
//将所有map中的结点连接起来;
for(int i = 0; i <= maxDegree; ++i){
if(map[i] == null) continue;
if(min == null){
min = map[i];
min.left = min;
min.right = min;
}
else{
min.right.left = map[i];
map[i].right = min.right;
min.right = map[i];
map[i].left = min;
}
if(min.val.compareTo(map[i].val) > 0)
min = map[i];
map[i] = null;
}
}
/**
*
* @param p
* @param q
* 将 q 结点连接成 p 的孩子, 需要将q的marked标记清除;
*/
private void link(FibNode<T> p, FibNode q) {
if(p.child==null){
q.left = q;
q.right = q;
}else{
p.child.left.right = q;
q.left = p.child.left;
p.child.left = q;
q.right = p;
}
q.marked = false;
p.child = q;
++p.degree;
}
public void decreaseKey(FibNode<T> node, T t){
if(node == null || t.compareTo(node.val) >= 0) return ;
node.val = t;
FibNode<T> parent = node.parent;
if(parent == null || node.val .compareTo(parent.val) >= 0){
// 如果父节点为空或者说没有违反最小堆序;
return ;
}
cut(node); //剪断该结点与父节点联系;
cascadingCut(parent);
}
private void cascadingCut(FibNode<T> node) {
FibNode<T> parent = node.parent;
if(parent == null){
return ;
}
if(parent.marked == false){ //如果父亲结点没有失去过孩子
parent.marked = true;
return ;
}
//失去过一个孩子,要将该结点剪断;
cut(node);
cascadingCut(parent);
}
/**
*
* @param node 待剪断的结点
* 将结点从其父节点链表中移除;
*/
private void cut(FibNode<T> node) {
FibNode<T> parent = node.parent;
if(node == node.parent.child){
//如果是父节点child指针所指
parent.child = parent.child.right;
parent.child.left = node.left;
node.left.right = parent.child;
}else{
node.right.left = node.left;
node.left.right = node.right;
}
if(parent.child == node) //如果父亲链表中一开始只有一个元素;
parent.child = null;
//将该节点加入到根链表中;
node.parent = null;
addNode(node);
--parent.degree;
}
public void addNode(FibNode<T> node){
if(min == null) {
min = node;
min.left = min;
min.right = min;
}
else{
node.right = min.right;
min.right.left = node;
min.right = node;
node.left = min;
}
if(node.val.compareTo(min.val) < 0)
min = node;
++size;
}
}
addNode(node);
--parent.degree;
}
public void addNode(FibNode<T> node){
if(min == null) {
min = node;
min.left = min;
min.right = min;
}
else{
node.right = min.right;
min.right.left = node;
min.right = node;
node.left = min;
}
if(node.val.compareTo(min.val) < 0)
min = node;
++size;
}
}