最大堆、最小堆Java实现,解决TOP K问题

一、基础知识

1.1 什么是最大(小)堆

最大堆,最小堆类似,以下以最小堆为例进行讲解。
最小堆是满足以下条件的数据结构:

  1. 它是一棵完全二叉树
  2. 所有父节点的值小于或等于两个子节点的值

1.2 什么是完全二叉树

除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐。

1.3 什么是TOP K问题

Top K指的是从n(很大)个数据中,选取最大(小)的k个数据。例如学校要从全校学生中找到成绩最高的500名学生,再例如某搜索引擎要统计每天的100条搜索次数最多的关键词。

二、堆排序解决TOP K

对于TOPK问题,解决方法有很多:

方法一:对源数据中所有数据进行排序,取出前K个数据,就是TopK。

但是当数据量很大时,只需要k个最大的数,整体排序很耗时,效率不高。

方法二:维护一个K长度的数组a[],先读取源数据中的前K个放入数组,对该数组进行升序排序,再依次读取源数据第K个以后的数据,和数组中最小的元素(a[0])比较,如果小于a[0]直接pass,大于的话,就丢弃最小的元素a[0],利用二分法找到其位置,然后该位置前的数组元素整体向前移位,直到源数据读取结束。

这比方法一效率会有很大的提高,但是当K的值较大的时候,长度为K的数据整体移位,也是非常耗时的。

对于这种问题,效率比较高的解决方法是使用最小堆。

2.1 最小堆思路

最小堆(小根堆)是一种数据结构,它首先是一棵完全二叉树,并且,它所有父节点的值小于或等于两个子节点的值。

最小堆的存储结构(物理结构)实际上是一个数组。如下图:
在这里插入图片描述
堆有几个重要操作:

BuildHeap:将普通数组转换成堆,转换完成后,数组就符合堆的特性:所有父节点的值小于或等于两个子节点的值。

Heapify(int i):当元素i的左右子树都是小根堆时,通过Heapify让i元素下降到适当的位置,以符合堆的性质。

回到上面的取TopK问题上,用最小堆的解决方法就是:先去源数据中的K个元素放到一个长度为K的数组中去,再把数组转换成最小堆。再依次取源数据中的K个之后的数据和堆的根节点(数组的第一个元素)比较,根据最小堆的性质,根节点一定是堆中最小的元素,如果小于它,则直接pass,大于的话,就替换掉跟元素,并对根元素进行Heapify,直到源数据遍历结束。

2.2 最小堆解决TOPK

最小堆的实现:

public class MinHeap
{// 堆的存储结构 - 数组private int[] data;// 将一个数组传入构造方法,并转换成一个小根堆public MinHeap(int[] data){this.data = data;buildHeap();}// 将数组转换成最小堆private void buildHeap(){// 完全二叉树只有数组下标小于或等于 (data.length) / 2 - 1 的元素有孩子结点,遍历这些结点。// *比如上面的图中,数组有10个元素, (data.length) / 2 - 1的值为4,a[4]有孩子结点,但a[5]没有*for (int i = (data.length) / 2 - 1; i >= 0; i--) {// 对有孩子结点的元素heapifyheapify(i);}}private void heapify(int i){// 获取左右结点的数组下标int l = left(i);  int r = right(i);// 这是一个临时变量,表示 跟结点、左结点、右结点中最小的值的结点的下标int smallest = i;// 存在左结点,且左结点的值小于根结点的值if (l < data.length && data[l] < data[i])  smallest = l;  // 存在右结点,且右结点的值小于以上比较的较小值if (r < data.length && data[r] < data[smallest])  smallest = r;  // 左右结点的值都大于根节点,直接return,不做任何操作if (i == smallest)  return;  // 交换根节点和左右结点中最小的那个值,把根节点的值替换下去swap(i, smallest);// 由于替换后左右子树会被影响,所以要对受影响的子树再进行heapifyheapify(smallest);}// 获取右结点的数组下标private int right(int i){  return (i + 1) << 1;  }   // 获取左结点的数组下标private int left(int i) {  return ((i + 1) << 1) - 1;  }// 交换元素位置private void swap(int i, int j) {  int tmp = data[i];  data[i] = data[j];  data[j] = tmp;  }// 获取对中的最小的元素,根元素public int getRoot(){return data[0];}// 替换根元素,并重新heapifypublic void setRoot(int root){data[0] = root;heapify(0);}
}

利用最小堆获取TopK:

public class TopK
{public static void main(String[] args){// 源数据int[] data = {56,275,12,6,45,478,41,1236,456,12,546,45};// 获取Top5int[] top5 = topK(data, 5);for(int i=0;i<5;i++){System.out.println(top5[i]);}}// 从data数组中获取最大的k个数private static int[] topK(int[] data,int k){// 先取K个元素放入一个数组topk中int[] topk = new int[k]; for(int i = 0;i< k;i++){topk[i] = data[i];}// 转换成最小堆MinHeap heap = new MinHeap(topk);// 从k开始,遍历datafor(int i= k;i<data.length;i++){int root = heap.getRoot();// 当数据大于堆中最小的数(根节点)时,替换堆中的根节点,再转换成堆if(data[i] > root){heap.setRoot(data[i]);}}return topk;
}
}

2.3 最小堆的删除操作

前面在介绍最小堆解决TOPK问题的时候,已经涉及到建堆、添加元素的过程,接下来介绍最小堆的删除过程。

操作原理是:当删除节点的数值时,原来的位置就会出现一个孔,填充这个孔的方法就是,
把最后的叶子的值赋给该孔并下调到合适位置,最后把该叶子删除。

如图中要删除72,先用堆中最后一个元素来35替换72,再将35下沉到合适位置,最后将叶子节点删除。
  “结点下沉”

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群

在《容器化单页面应用中RESTful API的访问》以及《容器化单页面应用中Nginx反向代理与Kubernetes部署》两篇文章中&#xff0c;我介绍了一套容器化ASP.NET Core应用程序的方案&#xff0c;并对于Nginx反向代理的使用进行了介绍。在《使用Rancher在Microsoft Azure上搭建Kuberne…

微软如何利用机器学习改进Win 10更新体验

微软在 2018 年 4 月 Windows 更新时第一次大规模使用机器学习&#xff08;ML&#xff09;&#xff0c;ML 通过监测 PC 运行状况的六个核心领域&#xff08;例如总体可靠性&#xff09;&#xff0c;以确定功能更新过程是否顺利进行。而在 2019 年 5 月发布的更新推送中&#xf…

使用.NET Core创建Windows服务(一) - 使用官方推荐方式

原文&#xff1a;Creating Windows Services In .NET Core – Part 1 – The “Microsoft” Way创建Windows服务来运行批处理任务或者运行后台任务&#xff0c;是一种非常常见的模式&#xff0c;但是由于云服务&#xff08;Amazon Lambda, Azure WebJobs以及Azure Functions&am…

关于.NET HttpClient方式获取微信小程序码(二维码

随着微信小程序的火热应用&#xff0c;市面上有关小程序开发的需求也多了起来。近来分析了一项生成有关生成微信小程序码的需求——要求扫码跳转到小程序指定页面&#xff08;带参数&#xff09;&#xff1b;看了下小程序官方文档&#xff0c;以及网上的例子&#xff0c;未看到…

从零开始使用Skywalking分布式链路追踪系统

当我们用很多服务时&#xff0c;各个服务间的调用关系是怎么样的&#xff1f;各个服务单调用的顺序\时间性能怎么样?服务出错了&#xff0c;到底是哪个服务引起的&#xff1f;这些问题我们用什么方案解决呢&#xff0c;以前的方式是各个系统自己单独做日志&#xff0c;出了问题…

快速排序 (Quick Sort)(Java实现)

快速排序&#xff08;Quicksort&#xff09;是对冒泡排序的一种改进&#xff0c;借用了分治的思想&#xff0c;由C. A. R. Hoare在1962年提出。 1、基本思想 快速排序的基本思想&#xff1a;挖坑填数分治法。 首先选一个轴值(pivot&#xff0c;也有叫基准的)&#xff0c;通过…

使用.net core3.0 正式版创建Winform程序

前阵子一直期待.net core3.0正式版本的出来&#xff0c;以为这个版本出来&#xff0c;Winform程序又迎来一次新生了&#xff0c;不过9.23日出来的马上下载更新VS&#xff0c;创建新的.net core Winform项目&#xff0c;发现并没有Winform窗体设计器。而微软目前则是通过插件的方…

深入理解 ValueTask

.NET Framework 4 里面的命名空间为 System.Threading.Tasks的 Task 类。这个类以及它派生的 Task<TResult> 早已成为编程的主要部分&#xff0c;在 C#5 中的异步编程模式当作介绍了 async/await。在这篇文章里&#xff0c;我会覆盖新的类 ValueTask / ValueTask<TRes…

NET Core 3.0 AutoFac替换内置DI的新姿势

.NET Core 3.0 和 以往版本不同&#xff0c;替换AutoFac服务的方式有了一定的变化&#xff0c;在尝试着升级项目的时候出现了一些问题。原来在NET Core 2.1时候&#xff0c;AutoFac返回一个 IServiceProvider 参数注入到ConfigureServices .NET Core 服务中&#xff0c;基本大痣…

asp.net core 使用Mysql和Dapper

序曲&#xff1a;学习编程最好的方式就是敲代码&#xff0c;没有比这个更好的方法&#xff0c;哪怕你看了上百G的视频&#xff0c;都不如你自己敲几行代码更为有效。还有要记得敲完代码然后写一篇随笔来记录一下你所学所想。大家都知道&#xff0c;.netcore是微软一个具有历史意…

CSFR(跨站请求伪造)攻击与防御

一、CSRF是什么&#xff1f; CSRF&#xff08;Cross-site request forgery&#xff09;&#xff0c;中文名称&#xff1a;跨站请求伪造&#xff0c;也被称为&#xff1a;one click attack/session riding&#xff0c;缩写为&#xff1a;CSRF/XSRF。 二、CSRF可以做什么&#…

Vue 3源码公布

10 月 5 日凌晨&#xff0c;Vue.js 框架的作者兼核心开发者尤雨溪公布了尚处于 Pre-Alpha 状态的 Vue 3 源码。说学不动的童鞋抓紧剩余的假期时间撸一遍源码吧 : D作者表示&#xff0c;Vue 3 主要的架构改进、优化和新功能均已完成&#xff0c;剩下的主要任务是完成一些 Vue 2 …

在副业刚需的时代,如何掌握副业的正确姿势?

前言近期&#xff0c;伴随着“副业刚需”这个词语的流行&#xff0c;关于“职场人要不要发展副业”的话题再一次被炒得沸沸扬扬。有人认为副业是刚需&#xff0c;是抵御中年危机的锦囊妙计&#xff0c;甚至是中年人该有的自觉&#xff0c;没有副业的人不足以谈人生&#xff0c;…

使用.NET Core创建Windows服务(二) - 使用Topshelf方式

原文&#xff1a;Creating Windows Services In .NET Core – Part 2 – The “Topshelf” Way作者&#xff1a;Dotnet Core Tutorials译者&#xff1a;Lamond Lu译文&#xff1a;使用.NET Core创建Windows服务&#xff08;二&#xff09; - 使用Topshelf方式使用.NET Core创建…

常用加密算法(Java实现)总结

1、Java的安全体系架构 Java中为安全框架提供类和接口。JDK 安全 API 是 Java 编程语言的核心 API&#xff0c;位于 java.security 包&#xff08;及其子包&#xff09;&#xff0c;以及sun.securityAPI包&#xff08;及其子包&#xff09;中。设计用于帮助开发人员在程序中同…

怎样的项目才能称为“成功项目”?

作者&#xff1a;邹溪源&#xff0c;长沙资深互联网从业者&#xff0c;架构师社区合伙人&#xff01;引子这个故事讲的是一家拥有百年历史的制造业大厂的信息化转型过程中的波折。这家企业拥有超过三万名员工&#xff0c;它是某行业的领先品牌&#xff0c;但是在信息化程度上却…

彩虹表

一、简介 彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合&#xff0c;不一定是针对MD5算法的&#xff0c;各种算法的都有&#xff0c;有了它可以快速的破解各类密码。越是复杂的密码&#xff0c;需要的彩虹表就越大&#xff0c;现在主流的彩虹表都是1…

深入Dapper.NET源码

经过业界前辈、StackOverflow多年推广,「Dapper搭配Entity Framework」成为一种功能强大的组合,它满足「安全、方便、高效、好维护」需求。但目前中文网路文章,虽然有很多关于Dapper的文章但都停留在如何使用,没人系统性解说底层原理。所以有了此篇「深入Dapper源码」想带大家进…

DDoS攻击与防御

一、DDOS介绍 要了解DDOS攻击是什么&#xff0c;首先要了解DOS攻击的基本原理是至关重要的。 DoS攻击是最早出现的&#xff0c;它的攻击方法说白了就是单挑&#xff0c;是比谁的机器性能好、速度快。但是现在的科技飞速发展&#xff0c;一般的网站主机都有十几台主机&#xf…

DRDoS(memcache漏洞导致的反射型分布式拒绝服务攻击)

一、DDoS基础 见博文&#xff1a;DDoS攻击与防御 二、Memcached 反射DDOS攻击原理 反射DDOS是发送大量带有被害者IP地址的请求给反射服务器&#xff0c;反射服务器对IP地址源做出大量回应&#xff0c;形成拒绝服务攻击。CLOUDFLARE的这张图很好的解释了DDOS反射攻击过程&…