堆排序獲取TopN

package com.zjl.tool.sort;

/**
 * 求前面的最大K個 解決方案:小根堆 (數據量比較大(特別是大到內存不能夠容納)時,偏向於採用堆)
 * @author 張恩備
 * @date 2016-11-25 下午12:15:36
 */
public class TopNByHeap {
    /**
     * 待排序列(R1,R2,...,Rk,...Rn)看做是徹底二叉樹,經過比較、交換,父節點和孩子節點的值,
     * 使得對於任意元素Rk(k<=n/2),知足Rk>=R(2k),Rk>=R(2k+1)
     * @param arr    數組對象
     * @param start    數組的開始下標
     * @param end    數組的結束下標
     */
    private static void HeapAdjust(int[] arr, int start, int end) {
        //當下標爲start的元素有孩子元素時
        while(start <= end/2) {
            //left和right分別爲左右孩子元素的下標,max爲左右孩子中值較小的孩子的元素下標
            int left = 2 * start+1;
            int right = 2 * start+2;
            int min = 0;
            
            //若是既有左孩子,又有右孩子
            if(left < end&&right <= end) {
                //若是左孩子小於右孩子的值,max = right,不然爲max = left
                if(arr[left] <= arr[right])
                    min = left;
                else
                    min = right;
            }
            //若是隻有左孩子,沒有右孩子,max值爲left
            if(left <= end&&right > end) {
                min = left;
            }
            //若是沒有孩子,則代表到了徹底二叉樹的葉子節點
            if(left > end) {
                break;
            }
            
            //若是當前節點值小於兩孩子中的值較大者,那麼將當前節點值與max交換
            if(arr[start] > arr[min]){
                int tmp = arr[start];
                arr[start] = arr[min];
                arr[min] = tmp;
            }
            //當前節點向孩子節點迭代
            start = min;
        }
    }
    
    /**
     * 建立k個節點的小根堆
     * 
     * @param a
     * @param k
     * @return
     */
    static int[] createHeap(int a[], int k) {
        int[] result = new int[k];
        for (int i = 0; i < k; i++) {
            result[i] = a[i];
        }
        //由最後一個非葉子節點,向根節點迭代,建立最大堆,數組中的最大值將被移動到根節點
        for(int start = k-1/2;start >= 0;start--) {
            HeapAdjust(result, start, k-1);
        }
        return result;

    }

    static void insert(int a[], int value, int k) {
        //當輸入元素的值value大於堆的根元素值時,則將堆的根元素的值賦值爲輸入元素的值value
         a[0]=value;
         //將改變後的堆從新k個節點的生成小根堆
         for(int start = k-1/2;start >= 0;start--) {
                HeapAdjust(a, start, k-1);
            }
    }

    static int[] getTopKByHeap(int input[], int k) {
        int heap[] = createHeap(input, k);
        for(int i=k;i<input.length;i++){
            //當input[i]值大於堆的根元素值時,將input[i]插入到堆中
            if(input[i]>heap[0]){
                insert(heap, input[i], k);
            }
        }
        
        //將小根堆降序排列
        while(k-1 > 0){
            //交換arr[0]和arr[k-1]的值
            int tmp = heap[0];
            heap[0] = heap[k-1];
            heap[k-1] = tmp;
            
            //待排序堆的範圍變爲依次減少,最後剩下一個元素時結束
             //執行完這一步,根元素的值變爲整個待序列中的最小值
            HeapAdjust(heap, 0, k-2);
            
            k--;
        }
        return heap;
    }

    public static void main(String[] args) {
        int a[] = {40,55,49,73,12,27,98,81,64,36,78};
        //獲取top3
        int result[] = getTopKByHeap(a, 3);
        for (int temp : result) {
            System.out.print(temp + " ");
        }
    }
}
相關文章
相關標籤/搜索