LeetCode动态图解---面试题 17.14. 最小K个数

面试题 17.14. 最小K个数

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

最常规也是最容易想到的思路就是,先对数组进行排序,再遍历输出前四个元素即可。

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

但是这样的时间复杂度实在是太高了O(nlogn),一般在面试的过程中都会要求时间复杂度。一般这种TOPK问题都可以使用优先级队列解决。

这里有一个小技巧,经验之谈。取大用小,取小用大。若需要取出前k个最大元素构造最小堆,
若需要服出前k个最小元素构造最大推。

思路:

1.首先取出前四个最小元素,我们需要构建一个大小为4个最大堆

2.若此时队列当中元素个数小于k值,则直接添加

3.若此时队列当中元素个数等于K值,

        a.新扫描的元素值val大于等于堆顶元素,则该值一定大于堆中所有元素,这个val一定不是所求元素,直接跳过。

        b.新扫描val小于堆顶元素,就把堆顶元素出队,将新元素val添加到队列中。

        c.重复上述过程,直到整个集合被我们扫描完毕,队列中恰好就保存了前k个最小值。

4.随着堆顶元素不断交换,会把堆顶元素不断变小,最终队列扫描结束就存放了最小的k个数。
此时的时间复杂度为O(nlogk) 。n是遍历数组的时间复杂度,logk是堆中有K个元素。当k<<n是时间复杂度就很明显啦。

 动态图解:

 代码实现:

因为JDK内置的优先级队列是一个最小堆,许我们这里使用的是最大堆,所以要借用Comparator接口改造一下。

class IntegerRevese implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }

    public int[] smallestK(int[] arr, int k) {

        int[] ret = new int[k];
        //边界判空
        if (arr.length == 0 || k == 0) {
            return ret;
        }
        //因为JDK内置的优先级队列是一个最小堆,许我们这里使用的是最大堆,所以要借用Comparator接口改造一下
        Queue<Integer> queue = new PriorityQueue<>(new IntegerRevese());
        for (int i = 0; i < arr.length; i++) {
            //若此时队列当中元素个数小于k值,则直接添加
            if (queue.size() < k) {
                queue.offer(arr[i]);
            } else {
                //若此时队列当中元素个数大于K值
                int max = queue.peek();
                //新扫描的元素值val小于堆顶元素。
                if (arr[i] < max) {
                    queue.poll();
                    queue.offer(arr[i]);
                }
            }
        }
        //当前队列中保存前k个最小元素。
        int i = 0;
        while (!queue.isEmpty()) {
            ret[i++] = queue.poll();
        }
        return ret;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值