斐波那契堆的实现和比较(相对二项堆)

Fibonacci Heap(简称F-Heap)是一种基于二项堆的非常灵活的数据结构。它与二项堆不同的地方在于:

1)root list和任何结点的child list使用双向循环链表,而且这些lists中的结点不再有先后次序(Binomial Heap中root list的根结点按degree从小到大顺序,child list的结点按degree从大到小顺序);

2)二项堆中任何一颗Binomial Tree中根结点的degree是最大的,而F-Heap中由于decrease-key操作(cut和cascading cut)的缘故,并不能保证根结点的degree最大;

3) 二项堆中任何结点(degree等于k的)为根的子树中,结点总数为2^k;F-Heap中相应的结点总数下界为F{k+2},上界为2^k(如果没有 Extract-Min和Delete两类操作的话)。其中F{k+2}表示Fibonacci数列(即0,1,1,2,3,5,8,11...)中第 k+2个Fibonacci数,第0个Fibonacci数为0,第1个Fibonacci数为1。注意不像二项堆由二项树组成那样,F-Heap的 root list中的每棵树并不是Fibonacci树(Fibonacci树属于AVL树),而F-Heap名称的由来只是因为Fibonacci数是结点个数 的一个下界值。

4)基于上面的区别,若F-Heap中结点总数为n,那么其中任何结点(包括非根结点)的degree最大值不超过 D(n) = floor(lgn/lg1.618),这里1.618表示黄金分割率(goldren ratio),即方程x^2=x+1的一个解。所以在Extract-Min的consolidate操作之后,root list中的结点最多有D(n)+1。而二项堆中degree最大值不超过floor(lgn),从而root list中最多有floor(lgn)+1颗二项树。

5)另外一个与二项堆的最大不同之处在于:F-Heap是一种具有平摊意义上的高性能 数据结构。除了Extract-Min和Delete两类操作具有平摊复杂度O(lgn),其他的操作(insert,union,find- min,decrease-key)的平摊复杂度都是常数级。因此如果有一系列的操作,其中Extract-min和delete操作个数为p,其他操作 个数为q,p<q,那么总的平摊复杂度为O(p + q.lgn)。达到这个复杂度的原因有以下几点,第一,root list和任何结点的child list中使用了双向循环链表;第二,union和insert操作的延迟合并,从而在所有的可合并堆中,F-heap的合并开销O(1)最小的;第 三,decrease-key中cut和cascading cut的巧妙处理(即任何非根结点最多失去一个孩子)。

以下是基于CLRS第三版的伪代码的Fibonacci Heap的实现。以下几点值得注意一下:

1)Decrease-Key操作中通过添加变量cascade消除CLRS中Cascading Cut函数的tail recursion;

2)Extract-Min的consolidate函数中每处理一个root结点就将该结点从root list中删除,然后在寻找新的min结点时按照degree从小到大次序(实际上在F-Heap中不用关心节点之间的相对顺序)恢复root list;

3)consolidate函数中的link操作和二项堆中的操作类似,只不过这里不用考虑child list中结点的顺序;

4)Extract- Min中涉及mark属性的代码:在Extract-Min中将min的所有孩子结点添加到root list时不需要清除mark属性,等到consolidate的link操作以及寻找新的min结点时再分别设置:linked child的mark = false,root的mark=false。

/**
 * 
 * Fibonacci Heap   
 *  
 * Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)
 * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php) 
 * 
 * @author ljs
 * 2011-09-05
 *
 */
public class FibonacciHeap {
	static class Node{
		private int key;
		private Node parent=null;
		
		private int degree=0;
		private Node child=null;
		
		private Node left;
		private Node sibling;
		
		private boolean mark=false;
		 
		public Node(int key){
			this.key = key;
		}
		public String toString(){
			return this.key + "(degree=" + this.degree + ",mark=" + this.mark + ")";
		}
	}
	//the head of root list
	private Node min;
	private int n;
	
	//insert a single node
	public void insert(Node node){
		addToRootList(node);
		n++;	
	}
	
	private void addToRootList(Node node){			
		if(min==null){					
			node.left=node.sibling=node;
			
			min = node;
		}else{
			//insert node in root list as node's left neighbor
			Node prev = min.left;
			
			prev.sibling = node;
			node.sibling = min;
			
			min.left = node;
			node.left = prev;
			
			if(node.key<min.key)
				min = node;
		}			
	}
	
	public Node findMin(){
		return this.min;
	}
	
	//rhs is the min node of H'
	public void union(FibonacciHeap h){
		Node rhs = h.min;
		if(rhs==null) return;
		
		//concatenate the root list of both heaps: insert rhs as the right neighbor of min
		if(this.min == null){
			this.min =  rhs;
		}else{
			Node nextH1 = min.sibling;
			Node lastH2 = rhs.left;
			
			min.sibling = rhs;			
			lastH2.sibling = nextH1;
			
			nextH1.left = lastH2;
			rhs.left = min;
			
			if(this.min.key>rhs.key){
				min = rhs;
			}
		}
		n += h.n;
	}
	
	public Node extractMin(){
		Node z = this.min;
		if(z!=null){
			//insert z's children in the root list
			Node x = z.child;			
			while(x!=null){
				Node next = x.sibling;
				this.addToRootList(x);
				x.parent = null;
				//x.mark = false; //delay updating mark until linking and finding new min
				x = (next != z.child)?next:null;
			}
			
			//remove z from root list
			n--;
			
			Node prev = z.left;			
			if(prev==z){
				//z is the only node in root list
				min = null;
			}else{
				Node next = z.sibling;
				prev.sibling = next;
				next.left = prev;
				//set min to a temporary value as an entry pointer of the heap, 
				//later we will update it
				this.min = next;
				consolidate();
			}			
		}
		return z;
	}
	
	private void consolidate(){
		double goldratio = (Math.sqrt(5)+1)/2.0;
		int maxdegree = (int)(Math.log(n) / Math.log(goldratio));
		
		Node[] A = new Node[maxdegree+1];
		//iterate all nodes in root list
		Node w = this.min;
		while(w!=null){
			Node prev = w.left;
			Node next = w.sibling;
			
			Node x = w;
			int d = x.degree;
			while(A[d] != null){
				Node y = A[d];
				//link x and y
				if(x.key<y.key){ //y is added as a child of x
					x = link(x,y);					
				}else{//x is added as a child of y					
					x = link(y,x);
				}				
				A[d] = null;
				d++; //when two trees with same degree are linked, the degree increases by 1.
			}
			A[d] = x;
			
			//remove w from root list
			if(prev == w){
				//w is the only node in root list
				w = null; //set condition to exit while
			}else{				
				prev.sibling = next;
				next.left = prev;
				w = next;
			}
		}
		min = null;
		
		//reconstruct root list from A[]
		for(int d=0;d<=maxdegree;d++){
			if(A[d] != null){
				A[d].mark = false; //root node is unmarked
				addToRootList(A[d]);				
			}
		}
	}
	private Node link(Node small,Node large){
		Node child = small.child;
		if(child==null){
			small.child = large;	
			large.left = large.sibling = large;
		}else{
			//make large node as a child of small node: insert large as a left neighbor of small.child			
			Node childPrev = child.left;		 
			
			childPrev.sibling = large;
			large.sibling = child;
			
			child.left = large;
			large.left = childPrev;			
		}
		small.degree++;
		large.parent = small;		
		large.mark = false; //large is a child of small, so its mark is reset
		return small;
	}
	

	public void decreaseKey(Node x,int k) throws Exception{
		if(x.key<k) throw new Exception("key is not decreased!");
		
		x.key = k;
		Node p = x.parent;
		boolean cascade = false;
		while(p!=null){
			if(cascade || x.key<p.key){
				cut(x,p);				
				if(p.mark){ //cascading cut 
					x = p;
					p = x.parent;
					cascade = true;
				}else{
					if(p.parent != null){ //p is not root
						p.mark = true;
					}
					break;
				}
			}
		}
		//if p is null, then x is root. 
		//Root node doesn't change when decreased key(thanks to the above exception checking).
		
		//update min: only x's key is changed, other's roots created via cascading cut has no effect on min
		if(k<this.min.key){
			min = x;
		}
	}

	private void cut(Node x,Node parent){		
		if(x == x.sibling){ //x is the only child of parent
			parent.child=null;
		}else{
			Node prev = x.left;
			Node next = x.sibling;
			//remove x from child list
			prev.sibling = next;
			next.left = prev;
			
			parent.child=next;
		}
		parent.degree--;
		x.parent = null;
		x.mark = false;
		
		//add x to root list
		addToRootList(x);
	}
	public void delete(Node x) throws Exception{
		decreaseKey(x,Integer.MIN_VALUE);
		this.extractMin();
	}
	
	
	public void print(){
		System.out.format("F-Heap(n=%d):%n",this.n);
		Node h = this.min;
		while(h!=null){
			this.print(0, h);
			h = (h.sibling!=min)?h.sibling:null;
		}
	}
	
	private void print(int level, Node node){
		for (int i = 0; i < level; i++) {
            System.out.format(" ");
        }
		System.out.format("|");
        for (int i = 0; i < level; i++) {
        	System.out.format("-");
        }
        //boolean isChildLink = (node.parent!=null && node.parent.child == node);        
        System.out.format("%d%s%n", node.key,node.mark?"(x)":"");
        Node child = node.child;
        while(child!=null){        	
        	print(level + 1, child);
        	child = (child.sibling!=node.child)?child.sibling:null;
        }       	
	}
	
	public static void main(String[] args) throws Exception {
		//heap1
		FibonacciHeap fheap1 = new FibonacciHeap();
		Node node5=null,node14=null,node15=null;
		for(int i=0;i<17;i++){
			Node node = new Node(i);
			fheap1.insert(node);
			if(i==5) node5=node;
			if(i==14) node14=node;
			if(i==15) node15=node;
		}
		fheap1.extractMin();		
		//fheap1.print();		
		
		fheap1.decreaseKey(node14, 9);
		fheap1.decreaseKey(node15, 8); //cascading cut
		//fheap1.print();
				
		fheap1.delete(node5);
		fheap1.print();
		
		Node min=fheap1.findMin();
		System.out.format("min:%d%n", min.key);
		
		
		//heap2
		System.out.println("");
		FibonacciHeap fheap2 = new FibonacciHeap();
		int[] B = new int[]{40,39,20,18,41,38,52,3};
		Node node52=null,node41=null;
		for(int i=0;i<B.length;i++){
			Node node = new Node(B[i]);
			fheap2.insert(node);
			if(i==6) node52 = node;
			if(i==4) node41 = node;
		}
		fheap2.insert(new Node(0));
		fheap2.extractMin();
		//fheap2.print();
		
		fheap2.decreaseKey(node52, 7);
		//fheap2.print();
		fheap2.decreaseKey(node41, 21);
		//fheap2.print();
		
		fheap2.extractMin();
		fheap2.print();
		
		//union heap1 and heap2 
		fheap1.union(fheap2);
		fheap1.print();
		min = fheap1.extractMin();
		while(min!=null){
			System.out.format("%d ",min.key);
			min = fheap1.extractMin();		
		}
	}

}

测试输出:

F-Heap(n=15):
|1
 |-9(x)
  |--10
  |--11
   |---12
 |-2
 |-3
  |--4
|6
|7
 |-8
|8
 |-16
 |-9
  |--13
min:1

F-Heap(n=7):
|7
 |-21
 |-20
  |--39
|18
|38
 |-40(x)
F-Heap(n=22):
|1
 |-9(x)
  |--10
  |--11
   |---12
 |-2
 |-3
  |--4
|7
 |-21
 |-20
  |--39
|18
|38
 |-40(x)
|6
|7
 |-8
|8
 |-16
 |-9
  |--13
1 2 3 4 6 7 7 8 8 9 9 10 11 12 13 16 18 20 21 38 39 40

posted @ 2011-09-05 14:11  ljsspace  阅读(1471)  评论(0编辑  收藏  举报