【剑指Offer系列】40-最小的K个数(优先队列实现顶堆)

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

方式一:小顶堆

public class TopKMin {
    public static void main(String[] args) {
        int[] arr  = {7,8,2,5,1,3,0,4,6,9,-1};
        int[] res = topKMin(arr, 4);
        System.out.println(Arrays.toString(res));
    }

    private static int[] topKMin(int[] arr, int k) {
        if (arr == null || k <= 0 || arr.length == 0) {
            return new int[0];
        }
        Queue<Integer> queue = new PriorityQueue<>(k);
        for (int i = 0; i < arr.length; i++) {
            queue.add(arr[i]);
        }
        int[] res = new int[k];
        int i = 0;
        while (i < k) {
            res[i++] = queue.poll();
        }
        return res;
    }
}

方式二:大顶堆

public class TopKMin {
    public static void main(String[] args) {
        int[] arr  = {7,8,2,5,1,3,0,4,6,9,-1};
        int[] res = topKMin(arr, 4);
        System.out.println(Arrays.toString(res));
    }

    private static int[] topKMin(int[] arr, int k) {
        if (arr == null || k <= 0 || arr.length == 0) {
            return new int[0];
        }
        Queue<Integer> queue = new PriorityQueue<>(k, (Integer i1, Integer i2) -> i2 - i1);
        int i = 0;
        while (i < k) {
            queue.add(arr[i]);
            i ++;
        }
        for (i = k; i < arr.length; i++) {
            Integer curMax = queue.peek();
            if (curMax > arr[i]) {
                queue.poll();
                queue.add(arr[i]);
            }
        }
        int[] res = new int[k];
        i = 0;
        while (queue.size() > 0) {
            res[i++] = queue.poll();
        }
        return res;
    }
}
public class KThFromTheBottom {
    public static void main(String[] args) {
        int[] arr = {7,8,2,5,1,3,0,4,6,9,-1};
        System.out.println(topKMin(arr, 4));
        System.out.println(topKMax(arr, 4));
    }

    private static Queue<Integer> topKMax(int[] arr, int k) {
        // 1.小顶堆
        Queue<Integer> queue = new PriorityQueue<>(k, Comparator.comparingInt(i -> i));
        for (int i = 0; i < arr.length; i++) {
            int val = arr[i];
            if (queue.size() < k) {
                queue.add(val);
            } else {
                // 2.堆已经有k个值了,先peek下堆中最小的值是否 < 待加入值
                // 是则弹出,加入当前值(否则说明待加入值太小了,比堆中目前最小值还小,没必要加入queue)
                Integer peek = queue.peek();
                if (peek!= null && val >= peek) {
                    queue.poll();
                    queue.add(val);
                }
            }
        }
        return queue;
    }

    private static Queue<Integer> topKMin(int[] array, int k) {
        // 1.大顶堆
        Queue<Integer> queue = new PriorityQueue<>(k, (i1, i2) -> i2 - i1);
        for (int i = 0; i < array.length; i++) {
            int val = array[i];
            if (queue.size() < k) {
                queue.add(val);
            } else {
                // 2.堆已经有k个值了,先peek下堆中最大的值是否 > 待加入值
                // 是则弹出,加入当前值即换血更年轻的(否则说明待加入值太大了,比堆中目前最大值还大,没必要加入queue)
                Integer peek = queue.peek();
                if (peek!= null && peek >= val) {
                    queue.poll();
                    queue.add(val);
                }
            }
        }
        return queue;
    }
}
补充topk问题
  • 一篇文章中出现频率最高的10个单词
1.通过IO流,读取此文章,用StringBuilder存读入的单词hello,world(sb.append(元素+“,”))
2.StringBuilder->String->StringTokenizer
StringBuilder sb = new StringBuilder();
        StringTokenizer tokenizer = new StringTokenizer(sb.toString());
        Map<String,Integer> map = new HashMap<>();
        while (tokenizer.hasMoreTokens()){
            String str = tokenizer.nextToken();
            Integer count = map.get(str);
            if (count == null){
                map.put(str,1);
            }else {
                map.put(str,count++);
            }
        }
        
3.遍历map,将map中的所有val存入大顶堆
4.直接从大顶堆中拿出top10个元素,即为出现频率前10的单词
  • 如果是1GB的内容,找topk问题。
1.采用归并算法的思想:先将1GB分成n份,堆每一份求出topk
2.再对这n份的topk结果,再进行一次总体topk即可
3.比如1GB分成100份,每份求出对应的top10,最终将100*top10再进行一次求top10即可
  • 总结:
1.求频率、出现次数:可以用map存储元素和该元素出现的次数
2.求topk问题,可以用顶堆
3.数据量巨大的情况下,可以分治,先分,后合
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值