数据结构OJ题——top-k问题:最小的K个数(Java实现)

题目链接:top-k问题:最小的K个数

1.方法一

各种排序算法(由于本文主要讲有关堆的使用,这里不做有关排序算法解决本题的介绍。对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决

2.方法二

记录数组arr[]的元素个数n,用向上调整法建一个小根堆。由于用向上调整法建立小根堆后,遍历堆中的数据时,下一个元素的值一定>= 上一个元素的值。然后删除小根堆的堆顶元素k次到存储数据的数组tmp[]中,这样tmp[]中记录的就是前k个最小的数。

代码如下

class Solution {
    public int[] smallestK(int[] arr, int k) {
        PriorityQueue<Integer> heap = new PriorityQueue<>(); 
        //n*logn  以向上插入的方法建堆
        for(int i = 0; i < arr.length;i++){
            heap.offer(arr[i]);
        }
        //k*logn   删除堆顶的K个元素到目标数组中
        int[] tmp = new int[k];//创建数组tmp[]存储前k个最小数据
        for(int i = 0;i < k;i++){
            tmp[i] = heap.poll();
        }
        return tmp;
    }
}

时间复杂度

以向上调整法建堆的过程中时间复杂度为T1(n) = (n+1)*( log2(n+1) - 2) + 2,删除堆顶k个元素的时间复杂度为T2(n) = k*log2( n+1 ),因此总的时间复杂度为:T(n)= (n+k+1) * log2(n+1);

3.方法三

思考:arr[]数组中有n个元素,我们只需要取出前k个最小的元素。在方法二中,我们建立了一个大小为n的小根堆。考虑到我们最终只需要保留k个数据,我们能否建立一个大小为k的堆,在这个堆上对数据进行操控从而降低程序的时间复杂度呢?答案是肯定的。

图示如下
在这里插入图片描述
代码如下

//实现comparator接口,为了创立比较器
class Imp implements Comparator<Integer>{
    public int compare(Integer o1,Integer o2){
        return o2.compareTo(o1);    //注意,创建大根堆,必须o2在前o1在后
    }
}
class Solution {
    public int[] smallestK(int[] arr, int k) {
        int[] tmp = new int[k];
        if(k == 0){//k==0 单独处理,防止空指针异常
            return tmp;
        }
        Imp imp = new Imp();
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(imp);
        //建立一个大小为3的大根堆
        for(int i = 0;i < k;i++){
            maxHeap.offer(arr[i]);
        } 
        for(int i = k;i < arr.length;i++){
            //arr[i]比堆顶元素小
            if(arr[i] < maxHeap.peek()){
                maxHeap.poll();
                maxHeap.offer(arr[i]);
            }
        }
        for(int i = 0;i < k;i++){
            tmp[i] = maxHeap.poll();
        }
        return tmp;
       
    }
}

时间复杂度

建堆过程:T1(n) = k*log2(k + 1)

 for(int i = 0;i < k;i++){
            maxHeap.offer(arr[i]);
        } 

删除添加元素过程:T2(n) = (n-k) * log2(k + 1);

for(int i = k;i < arr.length;i++){
            //arr[i]比堆顶元素小
            if(arr[i] < maxHeap.peek()){
                maxHeap.poll();
                maxHeap.offer(arr[i]);
            }
        }

所以总的时间复杂度T(n) = n*log2(k+1)-----(1)
对比方法二的时间复杂度T(n)= (n+k+1) * log2(n+1) -----(2)
(1)式的值更小,特别当n是一个极大的数比如100000,而k只是一个就很小的数比如10时,两个式子在对数真数部分的差值巨大,(1)式的值远小于(2)式。
当然方法二也有比较麻烦的地方,在于它需要创建一个大根堆,而Java默认创建的是小根堆,因此需要一个类实现Comparator接口并重写里面的compare()方法。这部分内容我会在其他文章做介绍,这里由于不是本文的核心暂时就忽略掉了。

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值