堆 ---- TopK 问题

首先,什么是 TopK 问题?
就是给定一个集合(元素个数为N),找到前 K 个最大和最小的元素

解决方法

· 方法一:针对整个集合,建立一个大小为 N 的大堆,循环取 K 次堆顶元素;
· 方法二:建立一个大小为 K 的小堆(求前K大的值),令堆顶元素为守门员,循环遍历 N 个元素中的每个元素,当元素大于堆顶元素时,将守门员替换成此元素,并进行向下调整,得到新的守门员,当所有元素遍历完后,堆中剩下的就是前 K 大的值;得到的前 K 个元素是无序的,想要知道谁是第几大,得重新排序

两种方法的使用场景
当 N >>K 时,第一种方法的时间效率和空间效率远低第二种方法,所以方法二更优
当 N 很大时,N个元素无法同时加到内存中,也只能使用方法二
当 N 和 K 差不多大时,两种方法都可以,差别不大

方法二程序代码如下:

public class HeapTopK {
    private int[] KSmallNum(int[] array,int k) {
        if(k <= 0 || k > array.length) {
            return array;
        }
       //PriorityQueue<Integer> queue = new PriorityQueue<>();
        int[] ret = new int[k];
        // 先将 array 前 K 个元素加到 ret中
        for(int i = 0; i < k; i++ ) {
            ret[i] = array[i];
        }
        // 建小堆
        for(int i = (k-1-1)/2; i >= 0; i--) {
            shiftDown(ret, k, i);
        }

        // 循环拿堆顶元素与array中的其他元素进行比较
        while (k < array.length) {
            if(ret[0] < array[k]) {
                ret[0] = array[k];
                shiftDown(ret,ret.length,0);
            }
            k = k+1;
        }
        return ret;
    }

    private void shiftDown(int[] array, int size, int index) {
        int parent = index;
        int child = 2*parent + 1;
        while(child < size) {
            if(child+1 < size && array[child+1] < array[child]) {
                child = child + 1;
            }
            if(array[child] < array[parent]) {
                int tmp = array[child];
                array[child] = array[parent];
                array[parent] = tmp;
            }else {
                break;
            }
            parent = child;
            child = 2*parent + 1;
        }
    }

    public static void main(String[] args) {
        HeapTopK heapTopK = new HeapTopK();
        int[] array = {9,6,5,2,1,4,7,8};
        int[] ret = heapTopK.KSmallNum(array, 4);
        // 打印出来的结果是无序的,是前K大的小堆层序遍历的结果
        System.out.println(Arrays.toString(ret));
        // 想要得到是第几大的值,就得重新排序
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值