【leetcode】用3个方法解决面试题17.14.最小k个数(TopK问题)

文章介绍了三种在Java中找出数组中最小k个数的算法:1)使用归并排序对数组排序后返回前k个;2)将数组元素放入最小堆中,出队k次;3)建立最大堆,仅保留最小的k个数。第三种方法的时间复杂度更优,为O(n)。
摘要由CSDN通过智能技术生成

1.题目内容

设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

2.解题思路及代码(3个方法)

方法1:

相信大家都能想到这个方法,先将数组进行升序排列,然后返回前k个数即可。

代码:

    public int[] smallestK(int[] arr, int k) {
         Arrays.sort(arr);
         int[] ar=new int[k];
         for(int i=0;i<k;i++){
             ar[i]=arr[i];
         }
         return ar;
    }

在这个代码中Arrays.sort()使用了归并排序,它的时间复杂度为nlogn。

方法2:

将数组的所有元素添加到最小堆里面,依次出队k次。(出的是堆顶元素,即每次出的都是最小值)

补充:JDK的优先级队列默认是最小堆,堆顶存放了最小值

    public int[] smallestK(int[] arr, int k) {
         Queue<Integer>queue=new PriorityQueue<>();
         for(int i:arr){
             queue.offer(i);
         }
         int[] ar=new int[k];
         for(int n=0;n<k;n++){
             ar[n]=queue.poll();
         }
         return ar;
    }

建堆的时间复杂度为n,整体的代码时间复杂度为nlogn,那么有没有时间复杂度小于nlogn的方法呢?这就用到了方法3的思路。

方法3:

本题中我们需要的结果只是最小的k个数,所有只需要建立一个只保存k个元素的最大堆。

步骤:

1.当最大堆的元素个数小于k时,直接入堆

2.当最大堆元素的个数大于等于k时,将堆顶元素和入堆元素比较,如果入堆元素比堆顶元素还大,则这个元素一定不是本题需要的元素

3.入堆元素小于堆顶时,交换两个元素,继续使堆为最大堆

4.重复上述步骤

(换句话说,本题需要的是最小的k个数,建立最大堆是为了得到k个数的最大值,如果入堆的值比这个最大值还大,则一定不是本题需要的值)

对于这种的TopK问题,用堆解决时,可以遵循下面两句话:取大用小,取小用大

即:要求最大的k个数,就用最小堆,求最小的k个数,就用最大堆。

代码:

public int[] smallestK(int[] arr, int k) {
         Queue<Integer>queue=new PriorityQueue<>(new Comparator<Integer>(){
             public int compare(Integer o1,Integer o2){
                 return o2-o1;
             }
         });
         for(int j:arr){
             queue.offer(j);
             if(queue.size()>k){
                 queue.poll();
         }        
    }
    //此时队列中保存了最小的k个数
         int ar[]=new int[k];
         for(int i=0;i<k;i++){
             ar[i]=queue.poll();
         }
    return ar;
}

补充:方法2中的建堆及建堆的时间复杂度这里就不细说了,详情可参考我的另一篇博客http://t.csdn.cn/tP0my

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值