• 能带你起飞的【数据结构】成王第十二篇:堆2


    前言

     

    堆的其他应用

    TopK 问题

    TopK 问题:给你100万个数据,让你找到前10个最大的元素.

    思路一:对整体进行排序,输出前10个最大的元素

    基于比较的排序最快的时间复杂度是N*log以2为底的N.

    对整体进行排序肯定不是一个非常好的思路

    (4条消息) 拜托,面试别再问我TopK了!!!_架构师之路_的博客-CSDN博客

    另外一种算法的链接,大家可以看一下

    思路二:用堆排序

    这不是Top k的做法,是堆

    思路三:

     假设有这么一组数据,还是要找前三个最大的值

    我们取这五个数据演示一下

    问题:找到前3个最大的元素.

     1.先把前3(也就是前K)个元素创建为小根堆

     当前的堆为什么是小根堆,因为堆顶的元素,一定是当前k个元素当中最小的一个元素.

    如果,有元素x比堆顶元素大,那么x这个元素,可能就是topk中的一个

    相反,如果是大根堆,那就不一定了

    如果堆顶元素小,那么出堆顶元素,然后入当前比堆顶大的元素,然后再次调整为小根堆

    此时我们就找到了前k个最大的元素

    此时堆的大小只有k这么大

    Topk时间复杂度:首先遍历整个元素O(N)

    调整这个堆,也就是树的高度:log以2为底的k

    所以时间复杂度就是:O(n*logk),如果k的值非常小就是O(n)

    总结:

    1.如果求前k个最大的元素,要建一个小根堆

    2.如果求前k个最小的元素,要建一个大根堆

    3.第k大的元素,建一个小堆,堆顶元素就是第k大的元素

    4.第k小的元素

    堆排序 

    借助堆如何对整体元素进行排序

    对这一组数据进行从小到大的排序 

    问题:借助大根堆还是小根堆排序?

    大根堆

    1.调整为大根堆

    2.0下标和最后1个未排序的元素进行交换即可

    3.end--

    4.end为0之后也就有序了

    此时一定能够保证65是最大的, 因为本身如果对整个数组进行排序之后,65也一定是在数组的最后一个元素,所以0下标元素和最后一个元素交换,交换完之后只有0下标这个棵树不是大根堆,其他树并没有动过,那么就调整0下标这棵树,调整的话肯定不能包含65,相当于把65删掉了

     调整过后又变成了大根堆

    下一次依次类推往后走

    也就是说一开始有个end下标,保存的是待排序排序序列未排序的最后一个元素

    此时65已经有序了,end--

     此时再把49和此时end下标的值15进行交换

    换完之后49就有序了

    调整之后end--

    然后让37和18进行交换 

    这时37就有序了

     堆排序代码

    1. public void heapSort(){
    2. int end = this.usedSize - 1;
    3. while(end > 0){
    4. int tmp = elem[0];
    5. elem[0] = elem[end];
    6. elem[end] = tmp;
    7. shiftDown(0,end);
    8. end--;
    9. }
    10. }

    java对象的比较 

    优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够 进行比较

    offer(null)之后就报空指针异常了

      为什么要能够进行比较呢

    因为PriorityQueue默认底下是一个小根堆

    存了第一个元素,再存第二个元素就得跟第一个元素进行比较

    第二个元素和第一个元素进行比较,假设都是两张牌

     根据什么比较呢?并没有指定

    放的是整数可以自己比较大小,但是放的是自定义的两张牌怎么比较,到底谁大谁小

    关于对象的比较来说:

    1.equals(); 比较两个对象相不相同,返回值是一个->true或者false

    2.比较大小:comparable,comeparTo

    我们需要

     实现compareTo,同时传一个Card,来设置怎么比较

    比如根据数字比较:

    此时我们在打印一下priorityQueue

    打印结果:

     这个时候就不会报错了

    这种方式有个不好的地方,

    一旦写好了根据哪种规则比较,那么就不能轻易进行修改了

    另一种方式:

    我们不给这个类实现接口,我们来写一些比较器

    此时来比较

    打印结果 :

     既然可以用比较器,那么我们的优先级队列是不是也可以改一改

    这样我们的运行结果还是报错

     此时我们给队列传一个比较器

     此时打印结果

    另一种写法 :

    匿名内部类

     在一种写法:

     可读性非常差

    用代码实现TopK问题

    1. public class TopK {
    2. /**
    3. * 求数组当中前K个最小的元素
    4. * @param array
    5. * @param k
    6. * @return
    7. */
    8. public static int[] topK(int[] array,int k){
    9. //1.创建一个大小为k的大根堆
    10. PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, new Comparator<Integer>() {
    11. @Override
    12. public int compare(Integer o1, Integer o2) {
    13. return o2 - o1;
    14. }
    15. });
    16. //2.遍历数组当中的元素,前K个元素放到队列当中
    17. for (int i = 0; i < array.length ; i++) {
    18. if(maxHeap.size() < k){
    19. maxHeap.offer(array[i]);
    20. }else {
    21. //3.从第k+1个元素开始,每个元素和堆顶元素进行比较
    22. int top = maxHeap.peek();
    23. if(top > array[i]){
    24. //3.1先弹出
    25. maxHeap.poll();
    26. //3.2后存入
    27. maxHeap.offer(array[i]);
    28. }
    29. }
    30. }
    31. int[] tmp = new int[k];
    32. for (int i = 0; i < k; i++) {
    33. tmp[i] = maxHeap.poll();
    34. }
    35. return tmp;
    36. }
    37. public static void main(String[] args) {
    38. int[] array = {18,21,8,10,34,12};
    39. }
    40. }

    373. 查找和最小的 K 对数字 - 力扣(LeetCode)
    ​​​​​​​

    1. public static List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
    2. PriorityQueue <List<Integer>> maxHeap = new PriorityQueue<>(new Comparator<List<Integer>>() {
    3. @Override
    4. public int compare(List<Integer> o1, List<Integer> o2) {
    5. return (o2.get(0)+ o2.get(1)) - (o1.get(0) + o1.get(1));
    6. }
    7. });
    8. for (int i = 0; i < Math.min(nums1.length,k); i++) {
    9. for (int j = 0; j < Math.min(nums2.length,k); j++) {
    10. if(maxHeap.size() < k){
    11. List<Integer> tmpList = new ArrayList<>();
    12. tmpList.add(nums1[i]);
    13. tmpList.add(nums2[j]);
    14. maxHeap.offer(tmpList);
    15. }else{
    16. int top = maxHeap.peek().get(0) + maxHeap.peek().get(1);
    17. if(top >nums1[i] + nums2[j] ){
    18. maxHeap.poll();
    19. List<Integer> tmpList = new ArrayList<>();
    20. tmpList.add(nums1[i]);
    21. tmpList.add(nums2[j]);
    22. maxHeap.offer(tmpList);
    23. }
    24. }
    25. }
    26. }
    27. List<List<Integer>> ret = new ArrayList<>();
    28. for (int i = 0; i < k && !maxHeap.isEmpty(); i++) {
    29. ret.add(maxHeap.poll());
    30. }
    31. return ret;
    32. }
    33. public static void main(String[] args) {
    34. int[] num1 = {1,7,11};
    35. int[] num2 = {2,4,6};
    36. List<List<Integer>> ret = kSmallestPairs(num1,num2,3);
    37. System.out.println(ret);
    38. }

     

  • 相关阅读:
    极限多标签学习之-FastXML运行和评价EUR-Lex4k数据集
    使用Jenkins实现前端自动化打包部署(Linux版本)
    Mysql命令行常用基本操作
    学会这几款表白特效让你明年双十一不再是一个人
    Node.js
    android查漏补缺(6)android相关属性
    代码随想录训练营 DP
    数据结构 1.1 初学数据结构
    鸿蒙Cannot resolve symbol 'ResourceTable'
    张俊将出席用磁悬浮技术改变生活演讲
  • 原文地址:https://blog.csdn.net/m0_64397675/article/details/125584358