文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java怎么用堆解决Top-k问题

2023-06-30 02:37

关注

本篇内容介绍了“Java怎么用堆解决Top-k问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1、什么是堆?

堆结构

堆其实就是一种二叉树,但是普通的二叉树是以链式结构进行储存数据的,而堆是以数组进行顺序存储数据的。那么什么样的二叉树才适合用顺序存储的方式呢?

我们假设一颗普通的二叉树可以用数组存储,那么就可以得到如下结构:

Java怎么用堆解决Top-k问题

我们可以看到,当二叉树中间有空值时,数组的存储空间会被浪费,那么什么情况下空间才不会被浪费呢? 那就是完全二叉树。

Java怎么用堆解决Top-k问题

从以上结构中,我们不能用链式结构的指针来访问孩子节点或者父亲节点,只能通过对应下标来访问,其实也比较简单。

例如下图:

已知 2 节点的下标是1,那么

他的左孩子下标是:2 * 2 + 1 = 3

他的右孩子下标是:2 * 2 + 2 = 4

相反,已知 1 节点的下标是3,3 节点的下标是4,那么

1 节点的父亲节点下标是:(3 - 1) / 2 = 1

3 节点的父亲节点下标是:(4 - 1) / 2 = 1

Java怎么用堆解决Top-k问题

大根堆 VS 小根堆

大根堆(最大堆)

大根堆保证,每颗二叉树的根节点都 大于 左右孩子节点

从最后一棵子树的根节点开始调整,来到每颗子树的根节点,使得每棵子树都向下调整为大根堆,最后再向下做最后调整,保证二叉树整体是大根堆(这个调整主要是为了后面的堆排序)。

Java怎么用堆解决Top-k问题

具体调整过程如下:

Java怎么用堆解决Top-k问题

Java怎么用堆解决Top-k问题

怎么用代码实现呢?

我们首先从最后一棵子树调整,那么就要拿到最后一颗子树的根节点 parent ,我们知道数组最后一个节点下标是 len - 1,而这个节点是最后一棵子树的左孩子或者右孩子,根据孩子下标就可以拿到根节点下标( parent ) ,parent-- 就可以让每颗子树都进行调整,直到来到根节点,再向下调整最后一次,便可以得到大根堆。

Java怎么用堆解决Top-k问题

// 将数组变成大根堆结构public void createHeap(int[] arr){    for (int i = 0; i < arr.length; i++) {        elem[i] = arr[i];// 放入elem[],假设不需要扩容        usedSize++;    }    // 得到根节点parent, parent--依次来到每颗子树的根节点,    for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {        // 依次向下搜索,使得每颗子树都变成大根堆        shiftDown(parent,usedSize);    }}// 向下搜索变成大根堆public void shiftDown(int parent,int len){    int child = parent*2+1;// 拿到左孩子    while (child < len){        // 如果有右孩子,比较左右孩子大小,得到较大的值和父节点比较         if (child+1 < len && (elem[child] < elem[child+1])){            child++;        }        // 比较较大的孩子和父节点,看是否要交换        int max = elem[parent] >= elem[child] ? parent : child;        if (max == parent) break;// 如果不需要调整了,说明当前子树已经是大根堆了,直接 break        swap(elem,parent,child);        parent = child;// 继续向下检测,看是否要调整        child = parent*2+1;    }}public void swap(int[] arr,int i,int j){  int temp = arr[i];    arr[i] = arr[j];    arr[j] = temp;}
小根堆(最小堆)

小根堆保证,每颗二叉树的根节点都 小于 左右孩子节点

调整过程同上。

Java怎么用堆解决Top-k问题

优先级队列(PriorityQueue)

在java中,提供了堆这种数据结构(PriorityQueue),也叫优先级队列,当我们创建一个这样的对象时,就得到了一个没有添加数据的 小根堆 ,我们可以向里面添加或者删除元素,每向里面删除或者添加一个元素,系统会整体进行一次调整,重新又调整为小根堆。

// 默认得到一个小根堆PriorityQueue<Integer> smallHeap = new PriorityQueue<>();smallHeap.offer(23);smallHeap.offer(2);smallHeap.offer(11);System.out.println(smallHeap.poll());// 弹出2,剩余最小的元素就是11,会被调整到堆顶,下一次弹出System.out.println(smallHeap.poll());// 弹出11 // 如果需要得到大根堆,在里面传一个比较器 PriorityQueue<Integer> BigHeap = new PriorityQueue<>(new Comparator<Integer>() {     @Override     public int compare(Integer o1, Integer o2) {         return o2 - o1;     } });

2、top-k问题解决思路

例:有一堆元素,让你找出前三个最小的元素。

思路一: 将数组从小到大排序,拿到数组前3个元素。但是可以发现这样时间复杂度太高,不可取。

思路二: 将元素全部放入一个堆结构中,然后弹出三个元素,每次弹出的元素都是当前堆最小的,那么弹出的三个元素就是前最小的三个元素。

这种思路可以做,但是假设我有1000000个元素,只弹出前三个最小的元素,那么就要用到大小为1000000的堆。这么做空间复杂度太高,不建议用这种方法。

思路三:

我们需要得到三个最小的元素,那么就建一个大小为3的堆,假设目前的堆结构中刚好放满了3个元素,那么这三个元素就是当前最小的三个元素。假设第四个元素是我们想要的元素之一,那么前三个至少有一个元素不是我们想要的,就需要弹出,那么弹出谁呢?

我们要得到的是前三个最小的元素,所以当前堆结构中最大的元素一定不是我们想要的,所以这里我们建一个大根堆。弹出该元素,然后放入第四个元素,直到遍历完整个数组。

Java怎么用堆解决Top-k问题

Java怎么用堆解决Top-k问题

Java怎么用堆解决Top-k问题

这样我们就得到了只含有前三个最小元素的堆,并且可以看到堆的大小一直都是3,而不是有多少数据就建多大的堆,然后再依次弹出元素就行了。

// 找前 k个最小的元素public static int[] topK(int[] arr,int k){     // 创建一个大小为 k的大根堆     PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k,new Comparator<Integer>() {         @Override         public int compare(Integer o1, Integer o2) {             return o2 - o1;         }     });     for (int i = 0; i < arr.length; i++) {         if (i < k){             // 放入前 k 个元素             maxHeap.offer(arr[i]);         }else{             // 从第 k+1个元素开始进行判断是否要入堆             if (maxHeap.peek() > arr[i]){                 maxHeap.poll();                 maxHeap.offer(arr[i]);             }         }     }     int[] ret = new int[k];     for (int i = 0; i < k; i++) {         ret[i] = maxHeap.poll();     }     return ret; }

“Java怎么用堆解决Top-k问题”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯