堆排序获取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 + " ");
        }
    }
}

 

posted @ 2017-10-02 21:15  花弄影  阅读(1729)  评论(0编辑  收藏  举报