Java最小堆解决TopK问题

转载自  Java最小堆解决TopK问题

TopK问题是指从大量数据(源数据)中获取最大(或最小)的K个数据。

TopK问题是个很常见的问题:例如学校要从全校学生中找到成绩最高的500名学生,再例如某搜索引擎要统计每天的100条搜索次数最多的关键词。

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

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

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

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

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

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

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

最小堆的存储结构(物理结构)实际上是一个数组。如下图:

堆有几个重要操作:

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

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

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

最小堆的实现:

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--)   {  // 对有孩子结点的元素heapify  heapify(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);  // 由于替换后左右子树会被影响,所以要对受影响的子树再进行heapify  heapify(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];  }  // 替换根元素,并重新heapify  public 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};  // 获取Top5  int[] 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开始,遍历data  for(int i= k;i<data.length;i++)  {  int root = heap.getRoot();  // 当数据大于堆中最小的数(根节点)时,替换堆中的根节点,再转换成堆  if(data[i] > root)  {  heap.setRoot(data[i]);  }  }  return topk;  
}  
}  

 

 

 

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

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

相关文章

Shiro-概述

一、什么是Shiro shiro是Apache旗下的一个开源框架&#xff0c;将软件系统的安全认证相关功能抽取出来&#xff0c;实现用户身份证&#xff0c;权限授权&#xff0c;加密&#xff0c;会话等功能&#xff0c;组成一个通用的安全认证框架。 二、特点 易于理解的java security …

ASP.NET Core 缓存技术 及 Nginx 缓存配置

前言 在Asp.Net Core Nginx部署一文中&#xff0c;主要是讲述的如何利用Nginx来实现应用程序的部署&#xff0c;使用Nginx来部署主要有两大好处&#xff0c;第一是利用Nginx的负载均衡功能&#xff0c;第二是使用Nginx的反向代理来降低我们后端应用程序的压力。那除了以上两点之…

localStorage、sessionStorage、Cookie的区别及用法

localStorage、sessionStorage、Cookie https://segmentfault.com/a/1190000012057010 localStorage、sessionStorage、Cookie的区别及用法 webstorage webstorage是本地存储&#xff0c;存储在客户端&#xff0c;包括localStorage和sessionStorage。 localStorage localSto…

shiro入门

一、身份认证 基本流程 流程如下&#xff1a; shiro把用户的数据封装成标识token&#xff0c;token一般封装着用户名&#xff0c;密码等信息使用Subject获取到封装着用户的数据标识tokenSubject将token交给SecurityManager&#xff0c;在SecurityManager安全中心中&#xff…

前后端分离开发模式下后端质量的保证 —— 单元测试

概述 在今天&#xff0c; 前后端分离已经是首选的一个开发模式。这对于后端团队来说其实是一个好消息&#xff0c;减轻任务并且更专注。在测试方面&#xff0c;就更加依赖于单元测试对于API以及后端业务逻辑的较验。当然单元测试并非在前后端分离流行之后才有&#xff0c;它很早…

查询近12个月的数据,没有数据的月份补零

可以有两种方法实现&#xff0c;一种是纯sql的方式&#xff0c;一种是sql和代码共用的形式 一、纯sql的形式 前半部分用于一个近12个月的表&#xff0c;然后再将查出来的数据和月份表相结合&#xff0c;就是想要的结果 select v.month,b.price price,ifnull(b.count,0) cou…

java图形验证码生成工具类

转载自 java图形验证码生成工具类生成验证码效果 ValidateCode.java 验证码生成类 package cn.dsna.util.images; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.FileOutput…

.NET Core中的认证管理解析

0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目&#xff0c;已经准备好了用户注册、登录等很多页面&#xff0c;也可以使用AuthorizeAttribute进行各种权限管理&#xff0c;看起来似乎十分方便。不过生成的代码都替…

散列算法,Remal使用散列算法

一、散列算法 散列算法让其保证不可逆&#xff0c;安全。这里举一个例子sh1的摘要算法。上代码 /*** 散列算法* author Administrator*/ public class HashRsaUtil {/*** 加密方式*/public static final String SHA1"SHA-1";/*** 加密次数*/public static final In…

简化得最没道理的6个汉字,让人大跌眼镜

文章来源于网络&#xff0c;侵删&#xff01;&#xff01;&#xff01; 1、“進”被简化为“进”&#xff0c;“進”字是让人“越走越佳”。 简化字却把它改成了“进”字&#xff0c;让你越走越走到“井”里去了&#xff0c;井底之蛙自生自灭。 2、“廠”被简化为“厂”字&…

c++

#include <iostream> #include<cmath> using namespace std; int main() { int x; double a; double b; while(cin >> x ){//注意while处理多个case for(int i 0 ;i < x ; i ){ cin>>a; …

在docker中运行ASP.NET Core Web API应用程序

本文是一篇指导快速演练的文章&#xff0c;将介绍在docker中运行一个ASP.NET Core Web API应用程序的基本步骤&#xff0c;在介绍的过程中&#xff0c;也会对docker的使用进行一些简单的描述。对于.NET Core以及docker的基本概念&#xff0c;网上已经有很多文章对其进行介绍了&…

谈谈 Java 的克隆

转载自 谈谈 Java 的克隆为什么要克隆对象 做开发很少用到克隆的。我能想得到的是用于调用方法时作为参数传递&#xff0c;为了保证方法调用前后对象的内部结构不被破坏&#xff0c;可以克隆一个对象作为参数传递。 使类具有克隆能力 有人可能注意到 Object 类中有一个 native…

android启调支付宝

网上找了一个可以起吊支付宝的appdemo &#xff0c;它集成了服务器端&#xff0c;我先将其分离为app和服务器端&#xff0c;保证app在接收参数后可以启调支付宝 &#xff08;保证app这边是正确的 不然出错都不知道是服务器出错还是app出错&#xff09;&#xff0c;在 找网上资…

shiro-身份授权流程、案例

一、身份授权流程 首先调用Subject.isPermitted/hasRole接口&#xff0c;委托给SecurityManager.SecurityManager接着会委托给内部组件Authorizer.Authorizer再将其请求委托给我们的Realm去做&#xff0c;Realm才是真正干活的.realm将用户请求的参数封装成权限对象&#xff0c…

对Java的URL类支持的协议进行扩展的方法

转载自 对Java的URL类支持的协议进行扩展的方法JAVA默认提供了对file,ftp,gopher,http,https,jar,mailto,netdoc协议的支持。当我们要利用这些协议来创建应用时&#xff0c;主要会涉及到如下几个类&#xff1a;java.net.URL、java.net.URLConnection、InputStream。URL类默认…

在.Net项目中使用Redis作为缓存服务

最近由于项目需要&#xff0c;在系统缓存服务部分上了redis&#xff0c;终于有机会在实际开发中玩一下&#xff0c;之前都是自己随便看看写写&#xff0c;很零碎也没沉淀下来什么&#xff0c;这次算是一个系统学习和实践过程的总结。 和Redis有关的基础知识 Redis是一个开源的分…

中国的程序员培训是不是有问题?

内容来源于&#xff0c;看最下面的出处&#xff0c;侵删 中国技术开放日的出海团对日本进行了为期一周的访问。笔者随行了头两天&#xff0c;参加Slush Asia大会&#xff0c;并访问了Gungho和Deloitte两家企业。虽然已经在日本生活了四年&#xff0c;但这样的体验却甚少&#x…

后台回调支付宝

https://blog.csdn.net/u012552275/article/details/78320051 网上找了一个可以起吊支付宝的appdemo &#xff0c;它集成了服务器端&#xff0c;我先将其分离为app和服务器端&#xff0c;保证app在接收参数后可以启调支付宝 &#xff08;保证app这边是正确的 不然出错都不知道…

解决高版本SpringBoot整合swagger时启动报错:Failed to start bean ‘documentationPluginsBootstrapper‘ 问题

一、控制台的报错信息 2021-12-29 15:15:04 [main] ERROR org.springframework.boot.SpringApplication - Application run failed org.springframework.context.ApplicationContextException: Failed to start bean documentationPluginsBootstrapper; nested exception is j…