左倾堆(一种可高效合并的优先队列)

左倾堆(Leftist Heap)是一个便于merge操作的数据结构,通过左倾树(Leftist Tree)实现。左倾树是一种特殊的二叉树,树中结点除了满足普通二叉堆的key大小规定外,还要求每一个结点X的左子树的Null Path Length(NPL)值不小于右子树的NPL值,因此这也是与普通二叉堆的区别:虽然普通二叉堆也满足左倾树的条件,左倾树往往不是一棵完全二叉树(而且通常不平衡),从而左倾树不能用数组表示了。上面提到的NPL指的是某个结点到NULL结点(总共有n+1个NULL结点)的最短路径长度,规定NULL结点本身的NPL等于-1,叶子结点的NPL等于0,非叶结点的NPL等于它的两个孩子结点的NPL最小值加1。按照左倾树的定义,它的左右子树都是左倾树, 而且有一个重要属性:一个具有n个结点的左倾堆最右边路径长度(即相当于root或树的NPL值)最多为floor(log(n+1)),利用这个特性可以在右路高效地实现下面的merge操作。

普通的二叉堆合并复杂度为O(n),而左倾堆的合并复杂度只有O(logn),(n是合并后的左倾堆的结点个数),因此左倾堆的最大优势是高效的合并操作。最小左倾堆的merge操作的算法是,输入两个左倾堆h1和h2,假设h1的根结点key值大于h2的根结点key值(如果不是这样,交换h1和h2),则将h2的根作为合并后的root结点,同时递归调用merge(h1.root,h2.root.right),返回的结果作为h2.root的右子树,由于merge操作保证了返回的树满足左倾树条件,因此返回的结果(现在是h2.root的右子树)也已经是一颗左倾树了。如果h2.root.right的NPL值大于h2.root.left的NPL值,需要交换两棵子树h2.root.left和h2.root.right,最后更新h2.root的NPL。

左倾堆的Extract-Min操作先删除root结点,然后调用merge操作将左右子树合并为一颗新的左倾树;Insert操作将要插入的元素看作一个只包含root结点的左倾堆,然后调用merge操作将两个左倾堆合并。

构造左倾堆:如果还是采用Insert操作,则复杂度为O(nlogn);如果使用普通二叉堆的构造方法,则复杂度为O(n),但是这并不是最好的左倾堆,原因是一方面构造的左倾堆是完全二叉树,但是另一方面又不能按照数组对它进行操作。采用FIFO队列结构可以达到O(n)复杂度,同时构造出来的左倾堆左倾效果最好,做法如下:将每个元素作为左倾堆(允许任何两个元素值相同)依次enqueue,然后每次从队列中dequeue两个左倾堆,用merge算法合并,并将合并的左倾堆enqueue到队尾,直到队列中只剩一个左倾堆,这个左倾堆就是最终结果。

实现:

import java.util.LinkedList;

 
/**
 * 
 * Leftist Heap   
 *  
 * Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)
 * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php) 
 * 
 * @author ljs
 * 2011-08-20
 *
 */
public class LeftistMinHeap {
	static class LeftistHeapNode{
		int key;
		LeftistHeapNode left;
		LeftistHeapNode right;
		int npl; //null-path-length
		
		public LeftistHeapNode(int key){
			this.key = key;
			this.npl = 0;
		}
		
		public String toString(){
			return this.key + "(npl=" + this.npl + ")";
		}
		
		public LeftistHeapNode merge(LeftistHeapNode rhsRoot){
			if(rhsRoot==this || rhsRoot == null) return this;
										
			LeftistHeapNode root1 = null; //the root of the merged tree
			LeftistHeapNode root2 = null; 
			if(rhsRoot.key<this.key){
				//merge this with rhsRoot's right child
				root1 = rhsRoot;
				root2 = this;
			}else{
				//merge rhsRoot with this's right child
				root1 = this;
				root2 = rhsRoot;				
			}
			
			LeftistHeapNode tmpRoot = root2.merge(root1.right);
			root1.right = tmpRoot;
			if(root1.left == null){ //left can not be null unless right is null
				root1.right = null;
				root1.left = tmpRoot;
				root1.npl = 0;
			}else{
				if(root1.right.npl>root1.left.npl){
					//swap left and right child						
					root1.right = root1.left;
					root1.left = tmpRoot;					
				}
				//at this time, the right child has the shortest null-path
				root1.npl = root1.right.npl + 1;
			}
			
			return root1;
		}
	}
	
	private LeftistHeapNode root;
	
	public LeftistMinHeap(LeftistHeapNode root){
		this.root = root;
	}
	
	private static LeftistHeapNode merge(LeftistHeapNode root1,LeftistHeapNode root2){
  		if(root1 == null) return root2;
		if(root2 == null) return root1;
		return root1.merge(root2); 
	}      
	public static LeftistMinHeap merge(LeftistMinHeap h1,LeftistMinHeap h2){
		LeftistHeapNode rootNode = merge(h1.root,h2.root);
		return new LeftistMinHeap(rootNode);
	}      
	
	public static LeftistMinHeap buildHeap(int[] A){
		LinkedList<LeftistHeapNode> queue = new LinkedList<LeftistHeapNode>();
		int n = A.length;
		//init: queue all elements as a single-node tree
		for(int i=0;i<n;i++){
			LeftistHeapNode node = new LeftistHeapNode(A[i]);
			queue.add(node);
		}
		//merge adjacent heaps and enqueue the merged heap afterward
		while(queue.size()>1){
			LeftistHeapNode root1 = queue.remove(); //dequeue
			LeftistHeapNode root2 = queue.remove();
			LeftistHeapNode rootNode = merge(root1,root2);
			queue.add(rootNode);
		}
		LeftistHeapNode rootNode = queue.remove();
		return new LeftistMinHeap(rootNode);
	}
	public void insert(int x){
		this.root = LeftistMinHeap.merge(new LeftistHeapNode(x), this.root);
	}
	
	public Integer extractMin(){
		if(this.root == null) return null;
		
		int min = this.root.key;
		this.root = LeftistMinHeap.merge(this.root.left, this.root.right);
		return min;
	}
	 
	public static void main(String[] args) {
		 int[] A = new int[]{4,8,10,9,1,3,5,6,11};
		 
		 LeftistMinHeap heap = LeftistMinHeap.buildHeap(A);
		 heap.insert(7);
		 Integer min = null;
		 while((min = heap.extractMin()) != null){
			 System.out.format(" %d", min);
		 }
		 System.out.println();
		 
		 System.out.println("********************");
		 A = new int[]{3,10,8,21,14,17,23,26};
		 
		 
		 LeftistHeapNode a0 = new LeftistHeapNode(A[0]);
		 LeftistHeapNode a1 = new LeftistHeapNode(A[1]);
		 LeftistHeapNode a2 = new LeftistHeapNode(A[2]);
		 LeftistHeapNode a3 = new LeftistHeapNode(A[3]);
		 LeftistHeapNode a4 = new LeftistHeapNode(A[4]);
		 LeftistHeapNode a5 = new LeftistHeapNode(A[5]);
		 LeftistHeapNode a6 = new LeftistHeapNode(A[6]);
		 LeftistHeapNode a7 = new LeftistHeapNode(A[7]);		 
		 a0.left = a1;  a0.npl = 1;
		 a0.right = a2; 
		 a1.left = a3;  a1.npl = 1;
		 a1.right = a4;
		 a4.left = a6;
		 a2.left = a5;
		 a5.left = a7;
		 LeftistMinHeap h1 = new LeftistMinHeap(a0);
		 
		 int[] B = new int[]{6,12,7,18,24,37,18,33};
		 LeftistHeapNode b0 = new LeftistHeapNode(B[0]);
		 LeftistHeapNode b1 = new LeftistHeapNode(B[1]);
		 LeftistHeapNode b2 = new LeftistHeapNode(B[2]);
		 LeftistHeapNode b3 = new LeftistHeapNode(B[3]);
		 LeftistHeapNode b4 = new LeftistHeapNode(B[4]);
		 LeftistHeapNode b5 = new LeftistHeapNode(B[5]);
		 LeftistHeapNode b6 = new LeftistHeapNode(B[6]);
		 LeftistHeapNode b7 = new LeftistHeapNode(B[7]);
		 b0.left = b1;  b0.npl = 2;
		 b0.right = b2;
		 b1.left = b3;  b1.npl = 1;
		 b1.right = b4; 
		 b4.left = b7;
		 b2.left = b5;  b2.npl = 1;
		 b2.right = b6;
		 LeftistMinHeap h2 = new LeftistMinHeap(b0);
		 
		 heap = LeftistMinHeap.merge(h1,h2);
		 while((min = heap.extractMin()) != null){
			 System.out.format(" %d", min);
		 }
		 System.out.println(); 
		 
		 System.out.println("********************");
		 A = new int[]{1,5,7,10,15,20,25,50,99};
		 a0 = new LeftistHeapNode(A[0]);
		 a1 = new LeftistHeapNode(A[1]);
		 a2 = new LeftistHeapNode(A[2]);
		 a3 = new LeftistHeapNode(A[3]);
		 a4 = new LeftistHeapNode(A[4]);
		 a5 = new LeftistHeapNode(A[5]);
		 a6 = new LeftistHeapNode(A[6]);
		 a7 = new LeftistHeapNode(A[7]);
		 LeftistHeapNode a8 = new LeftistHeapNode(A[8]);	
		 a0.left = a1; a0.npl = 2;		 
		 a0.right = a2;
		 a1.left = a3;  a1.npl = 1;
		 a1.right = a4;
		 a2.left = a5;  a2.npl = 1;
		 a2.right = a6;
		 a5.left = a7;  a5.npl =  1;
		 a5.right = a8;
		 h1 = new LeftistMinHeap(a0);
		 
		 B = new int[]{22,75};
		 b0 = new LeftistHeapNode(B[0]);
		 b1 = new LeftistHeapNode(B[1]);
		 b0.left = b1;
		 h2 = new LeftistMinHeap(b0);
		 
		 heap = LeftistMinHeap.merge(h1,h2); 
		 while((min = heap.extractMin()) != null){
			 System.out.format(" %d", min);
		 }
		 System.out.println();
	}

}



测试输出:
 1 3 4 5 6 7 8 9 10 11
********************
 3 6 7 8 10 12 14 17 18 18 21 23 24 26 33 37
********************
 1 5 7 10 15 20 22 25 50 75 99

posted @ 2011-08-21 00:21  ljsspace  阅读(711)  评论(3编辑  收藏  举报