斜堆(Skew Heap)

斜堆(Skew Heap)基于左倾堆的概念,也是一个用于快速合并的堆结构,但它可自我调整(self-adjusting),每一个merge操作的平摊成本仍为 O(logN),其中N为结点数,而且和左倾堆相比,每个结点没有npl属性,从而节省了空间。斜堆并不能保证左倾,但是每一个合并操作(也是采取右路合 并)同时需要无条件交换(而左倾堆中只是根据左右子树的npl值不符合要求时才交换)左右子树,让新合并的右子树变成左子树,原来的左子树变成新的右子树 (这有点类似于Splay Tree中的做法),从而可以达到平摊意义上的左倾效果。注意:一颗子树r和NULL子树合并时并不需要交换r子树的左右子树。

由于斜堆并不是严格的左倾堆,最坏的情况下右路长度可能为N,因此采用递归调用merge的风险是出现stack overflow。尽管如此,在下面的实现中还是提供了递归和非递归的两个版本。测试时使用的是非递归版本。

斜堆中除merge之外的其他操作跟左倾堆类似,具体情况参考以下代码,在此不再赘述。

实现:

import java.util.LinkedList;
import java.util.Stack;

 

/**
 * 
 * Skew 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-25
 *
 */
public class SkewHeap {
	static class Node{
		int key;
		Node left;
		Node right;
		
		public Node(int key){
			this.key = key; 
		}
		
		public String toString(){
			return String.valueOf(this.key);
		}
		
		public Node recursiveMerge(Node rhsRoot){
			if(rhsRoot==this || rhsRoot == null) {
				//Note: no need to swap the left and right children of this tree
				//Node tmp = this.left;
				//this.left = this.right;
				//this.right = tmp;
				return this;
			}
			
			Node root1 = null; //the root of the merged tree
			Node 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;				
			}
			
			Node tmpRoot = root2.recursiveMerge(root1.right);
			root1.right = root1.left;
			root1.left = tmpRoot;
			//or equivalently: we may first merge recursively, then swap left and right children.
			//Node right = root1.right;
			//root1.right = root1.left;			 
			//root1.left = root2.merge(right);			
						
			return root1;
		}
	}
	private Node root;
	
	public SkewHeap(Node root){
		this.root = root;
	}
	
	//version 1: recursive merge: not recommended because skew heap is 
	//unlike leftist heap to be deterministically leftist, so it can cause 
	//stack overflow in extreme cases.
	private static Node recursiveMerge(Node root1,Node root2){
  		if(root1 == null) return root2;
		if(root2 == null) return root1;
		return root1.recursiveMerge(root2); 
	}   

	//version 2: non-recursive merge
	private static Node iterativeMerge(Node root1,Node root2){
		if(root1 == null) return root2;
		if(root2 == null) return root1;
		
		Stack<Node> stack = new Stack<Node>();
		Node r1 = root1;
		Node r2 = root2;
		while(r1 != null && r2 != null){
			if(r1.key<r2.key){
				stack.push(r1);
				r1 = r1.right;
			}else{
				stack.push(r2);
				r2 = r2.right;
			}			
		}
		//at this point, exactly one of r1 and r2 is null
		//Again we don't need to swap the left and right children of r
		Node r = (r1 != null)?r1:r2;
		
		//merge
		while(!stack.isEmpty()){
			Node node = stack.pop();			
			node.right = node.left;
			node.left = r;
			r = node;
		}
		return r;
	}
	
	public static SkewHeap merge(SkewHeap h1,SkewHeap h2){
		Node rootNode = iterativeMerge(h1.root,h2.root);
		return new SkewHeap(rootNode);
	}      
	
	public static SkewHeap buildHeap(int[] A){
		LinkedList<Node> queue = new LinkedList<Node>();
		int n = A.length;
		//init: queue all elements as a single-node tree
		for(int i=0;i<n;i++){
			Node node = new Node(A[i]);
			queue.add(node);
		}
		//merge adjacent heaps and enqueue the merged heap afterward
		while(queue.size()>1){
			Node root1 = queue.remove(); //dequeue
			Node root2 = queue.remove();
			Node rootNode = iterativeMerge(root1,root2);
			queue.add(rootNode);
		}
		Node rootNode = queue.remove();
		return new SkewHeap(rootNode);
	}
	public void insert(int x){
		this.root = SkewHeap.iterativeMerge(new Node(x), this.root);
	}
	
	public Integer extractMin(){
		if(this.root == null) return null;
		
		int min = this.root.key;
		this.root = SkewHeap.iterativeMerge(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};
		 
		 SkewHeap heap = SkewHeap.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};
		 
		 
		 Node a0 = new Node(A[0]);
		 Node a1 = new Node(A[1]);
		 Node a2 = new Node(A[2]);
		 Node a3 = new Node(A[3]);
		 Node a4 = new Node(A[4]);
		 Node a5 = new Node(A[5]);
		 Node a6 = new Node(A[6]);
		 Node a7 = new Node(A[7]);		 
		 a0.left = a1;   
		 a0.right = a2; 
		 a1.left = a3;   
		 a1.right = a4;
		 a4.left = a6;
		 a2.left = a5;
		 a5.left = a7;
		 SkewHeap h1 = new SkewHeap(a0);
		 
		 int[] B = new int[]{6,12,7,18,24,37,18,33};
		 Node b0 = new Node(B[0]);
		 Node b1 = new Node(B[1]);
		 Node b2 = new Node(B[2]);
		 Node b3 = new Node(B[3]);
		 Node b4 = new Node(B[4]);
		 Node b5 = new Node(B[5]);
		 Node b6 = new Node(B[6]);
		 Node b7 = new Node(B[7]);
		 b0.left = b1;  
		 b0.right = b2;
		 b1.left = b3;  
		 b1.right = b4; 
		 b4.left = b7;
		 b2.left = b5;  
		 b2.right = b6;
		 SkewHeap h2 = new SkewHeap(b0);
		 
		 heap = SkewHeap.merge(h1,h2);
		 while((min = heap.extractMin()) != null){
			 System.out.format(" %d", min);
		 }
		 System.out.println(); 
		 
		 System.out.println("********************");
		 A = new int[]{1,4,23,63,24,44,28};
		 a0 = new Node(A[0]);
		 a1 = new Node(A[1]);
		 a2 = new Node(A[2]);
		 a3 = new Node(A[3]);
		 a4 = new Node(A[4]);
		 a5 = new Node(A[5]);
		 a6 = new Node(A[6]); 
		 a0.left = a1;   
		 a0.right = a2;
		 a1.left = a3;   
		 a1.right = a4;
		 a2.left = a5;  
		 a2.right = a6;
		 h1 = new SkewHeap(a0);
		 
		 B = new int[]{13,17,99,57,49,105,201};
		 b0 = new Node(B[0]);
		 b1 = new Node(B[1]);
		 b2 = new Node(B[2]);
		 b3 = new Node(B[3]);
		 b4 = new Node(B[4]);
		 b5 = new Node(B[5]);
		 b6 = new Node(B[6]);
		 b0.left = b1;
		 b0.right = b2;
		 b1.left = b3;
		 b1.right = b4;
		 b2.left = b5;
		 b2.right = b6;
		 h2 = new SkewHeap(b0);
		 
		 heap = SkewHeap.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 4 13 17 23 24 28 44 49 57 63 99 105 201

参考资料:

Sleator & Tarjan (1986). "Self-Adjusting Heaps"

posted @ 2011-08-25 00:14 ljsspace 阅读(...) 评论(...) 编辑 收藏