1. 程式人生 > >java最小堆實現優先權佇列和求最大的n個數問題

java最小堆實現優先權佇列和求最大的n個數問題

堆在實現優先權佇列和求最大最小的n個數問題上,有著莫大的優勢!

對於最大堆和最小堆的定義此處不再贅述,課參考網上文章:http://blog.csdn.net/genios/article/details/8157031

本文主要是對最小堆進行實現和應用,僅供新手參考。

優先權佇列

優先權佇列是一種非常有用的資料結構,作業系統的程序排程就有優先權佇列的應用,如果用最小值表示最高的優先權,則使用最小堆,否則使用最大堆。

top-N值為問題:

對於求最大的n個數,可以用最小堆來實現,思路是:將n個數構建成一個最小堆,如果剩下的數有大於堆頂元素的值,則替換掉堆頂元素,再調整為最小堆,直到所有數字遍歷完。

貼上原始碼:

關鍵類:

package com.algorithms;

/**
 * 最小堆,用陣列實現,解決top-N問題
 * @author "zhshl"
 * @date	2014-10-22
 * @param <T>
 */

public class Min_Heap {
	
	private int heap[];
	private int maxSize; ////最多可容納數目
	private int n;///元素數目
	
	
	/** 
	 * @param num 堆的大小
	 */
	public Min_Heap(int num){
		n=0;
		maxSize=num;
		heap=new int[maxSize];
	}
	
	
	
	/*
	*//**
	 * 初始堆是一顆任意次序的完全二叉樹,從(n-2)/2處開始向下調整
	 * @param heap
	 * @param n
	 *//*
	public void createHeap(int[] heap2,int n){
		
		heap=heap2;
		
		for(int i=(n-2)/2;i>=0;i--){
			///從(n-2)/2處開始向下調整
			adjustDown(i,n-1);
		}
	}*/

	
	/**
	 * 元素入堆
	 * @param v
	 */
	public void append(int v){
		if(isFull()){
			System.out.println("heap is full ,can't append elements !");
			return ;
		}
		////元素插在末尾
		heap[n]=v;
		n++;
		///向上調整
		adjustUp(n-1);
		
		
	}
	
	
	/**
	 * 取出堆頂元素
	 * @return
	 */
	public int serve(){
		if(isEmpty()){
			System.out.println("heap is empty!");
			return Integer.MIN_VALUE;
		}
		
		int temp=heap[0];
		////用最後一個元素取代第一個元素
		heap[0]=heap[n-1];
		n--;
		//向下調整
		adjustDown(0, n-1);
		
		return temp;
	}	
	
	
	/**
	 * 求最大的n個數據
	 * @param data
	 * @param n
	 * @return null if n is bigger than the heap size, otherwise 
	 */
	public int[] getTopN(int []data,int n){
		heap=new int[n];
		maxSize=n;
		this.n=0;
	
		///構建初始堆
		for(int i=0;i<n;i++){
			append(data[i]);
		}
		
		for(int i=n;i<data.length;i++){
			////如果比堆頂元素大,則替換堆頂元素,並調整
			if(data[i]>heap[0]){
				heap[0]=data[i];
				adjustDown(0, n-1);
			}
		}
		
		return heap;
	}
	
	
	/**
	 * 對元素i進行向下調整,用於刪除元素時調整
	 * @param i
	 * @param j
	 */
	private void adjustDown(int i, int j) {
		// TODO Auto-generated method stub
		int child=2*i+1;
		int temp=heap[i];///記錄待調整的節點的值	
		while(child<j){
			////在範圍內進行遍歷調整
			
			if(heap[child]> heap[child+1]){
				///如果左孩子比右孩子大, 則指向較小的右孩子
				child++;
			}
			
			if(heap[child]>temp){
				///如果較小的孩子都比自己大,則退出
				break;
			}
			
			heap[(child-1)/2]=heap[child];
			child=child*2+1;
		}
		////迴圈結束,child指向最終位置的節點的左孩子
		heap[(child-1)/2]=temp;
		
	}
	
	
	
	
	/**
	 * 將i處的元素向上調整為堆,用於插入時候
	 * @param i
	 */
	private void adjustUp(int i){
		int temp=heap[i];
		while(i>0&&heap[(i-1)/2]> temp){
			///當父節點的值比temp大的時候,交換值
			heap[i]=heap[(i-1)/2];
			i=(i-1)/2;
		}
		heap[i]=temp;
	}
	
	
	/**
	 * 堆是否滿了
	 * @return
	 */
	public boolean isFull(){
		return n>=maxSize;
	}
	
	/**
	 * 是否為空堆
	 * @return
	 */
	public boolean isEmpty(){
		return 0==n;
	}
}


測試類:

package com.algorithm.test;

import com.algorithms.Min_Heap;

/**
 * 最小堆測試類
 * @author "zhshl"
 * @date	2014-10-22
 *
 */
public class Min_Heap_Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Min_Heap heap=new Min_Heap(10);
		heap.append(2);
		heap.append(2);
		heap.append(13);
		heap.append(21);
		heap.append(2);
		heap.append(2);
		heap.append(53);
		heap.append(6);
		
		int temp;
		while(!heap.isEmpty()){
			temp=heap.serve();
			System.out.println(temp);
		}
		
		
		
		
		
		
		System.out.println("\n\n 求top-N問題:");
		int data[]={4,51,52,12,123,52,7643,234,123,33,44,2};
		
		data=heap.getTopN(data, 5);
		for(int i:data){
			System.out.println(i);
		}
	}

}