堆的使用:使用小顶堆处理问题----查找几个数组中最大的k个数
2016-04-07 15:25
375 查看
原问题是:给定两个数组,求两个数组中最大的k个数
刚上来看到是两个数组以为要排序,后来发现2个数组其实也只是幌子,可以使用维持一个k个数的集合,进一个(依次)出一个(最小值)的方式来解决,最后全部过滤完留在集合里的就是最大的k个数,时间上需要考虑的就是(N-k)*time1(k)+time2(k),其中N为这两个数组的总数目,time1(k)为在集合中每次新加一个元素然后从k+1个元素中踢出最小值的时间,time2(k)为构建初始集合的时间。
影响时间的因素主要是3部分(N-k),time1(k),time2(k).
这里使用小顶堆来做为过滤使用的集合,其:
time2: 建堆最坏klogk,平均kO(1);
time1: 过滤操作:相当于删除堆顶元素,然后执行一次堆顶元素的重定位(说重排序也不准确,不知道用什么词来表达)
1. 堆的数据结构
2. 用来过滤的工具
3. 测试代码
这样总的操作时间就是O(nlogk)
在k比n小的较多的情况下是比较合适的。
最后给个使用场景:比如某网站想实时查询当前最热门(搜索次数最多)的几个关键字,就可以用这种办法实现
刚上来看到是两个数组以为要排序,后来发现2个数组其实也只是幌子,可以使用维持一个k个数的集合,进一个(依次)出一个(最小值)的方式来解决,最后全部过滤完留在集合里的就是最大的k个数,时间上需要考虑的就是(N-k)*time1(k)+time2(k),其中N为这两个数组的总数目,time1(k)为在集合中每次新加一个元素然后从k+1个元素中踢出最小值的时间,time2(k)为构建初始集合的时间。
影响时间的因素主要是3部分(N-k),time1(k),time2(k).
这里使用小顶堆来做为过滤使用的集合,其:
time2: 建堆最坏klogk,平均kO(1);
time1: 过滤操作:相当于删除堆顶元素,然后执行一次堆顶元素的重定位(说重排序也不准确,不知道用什么词来表达)
1. 堆的数据结构
package ADT.Heap; /** * Created by Lijn on 2016/4/6. * * @author Lijn */ public interface IHeap<T extends Comparable<T>> { /** * @Description: 插入 * @return */ public void insert(T x); /** * @Description: 查找最小值 * @return */ public T findMin(); /** * @Description: 删除最小值 * @return */ public T deleteMin(); /** * @Description: 是否为空 * @return */ public boolean isEmpty(); /** * @Description: 清空 */ public void makeEmpty(); }
/** * @Title: BinaryHeap.java * @Description: 小顶堆 * @date: 2014-11-3下午6:17:59 * @version: V1.0 */ package ADT.Heap; import java.lang.reflect.Array; import java.util.Arrays; /** * @Description: * @author: Lijn * @time: 2014-11-3 */ public class BinaryHeap<T extends Comparable<T>> implements IHeap<T> { /** * @Description: 默认大小 */ protected static final int DEFAULT_CAPACITY=8; /** * @Description: 存储的节点数目 */ protected int currentSize; protected T[] arrayData; /** * @Description: constructor */ public BinaryHeap() { arrayData =(T[])Array.newInstance(Comparable.class, DEFAULT_CAPACITY); } /** * @Description: constructor */ public BinaryHeap(int n) { arrayData =(T[])Array.newInstance(Comparable.class, n+1); } /** * @Description: constructor:初始数组 * @param items * @throws Exception */ public BinaryHeap(T[] items) throws Exception { currentSize=items.length; arrayData =(T[])new Comparable[(currentSize+1)*11/10]; //浅拷贝 int i=1; for(T t:items) { arrayData[i++]=t; } buildHeap(); } /** * @Description: 插入2叉堆 * 堆的平均插入次数是O(1):2.607;最大插入次数是O(log(N)) * @param x */ public void insert(T x) { //数组不够则扩充 if(currentSize == arrayData.length - 1){ enlargeArray(arrayData.length*2); } //插入空穴 int hole = ++currentSize; //上虑 for(;hole>1 && x.compareTo(arrayData[hole/2])<0;hole/=2){ arrayData[hole] = arrayData[hole/2]; } arrayData[hole] = x; } /** * @Description: 查找最小值 * @return */ public T findMin() { if(isEmpty()) { return null; }else{ return arrayData[1]; } } /** * @Description: 删除最小值 * @return * @throws Exception */ public T deleteMin() { if(isEmpty()){ throw new RuntimeException("空堆"); } T minT = findMin(); arrayData[1] = arrayData[currentSize--]; percolateDown(1); return minT; } /** * @Description: 是否为空 * @return */ public boolean isEmpty() { return currentSize==0?true:false; } /** * @Description: 清空 */ public void makeEmpty() { currentSize=0; } /** * @Description: 下虑操作 * @param hole 下移的节点的位置 */ protected void percolateDown(int hole) { int child ; T tmp = arrayData[hole]; for(;hole*2<=currentSize;hole=child) { //下虑 操作需要考虑左右子树的大小,取较小的子节点 child = hole*2; if(child!=currentSize && arrayData[child+1].compareTo(arrayData[child])<0) { child++; } //空穴下虑 if(arrayData[child].compareTo(tmp)<0) { arrayData[hole] = arrayData[child]; }else{ break; } } arrayData[hole] = tmp; } /** * @Description: 上率操作 * @param hole */ private void percolateUp(int hole) { T x = arrayData[hole]; for(;hole>1 && x.compareTo(arrayData[hole/2])<0;hole/=2){ arrayData[hole] = arrayData[hole/2]; } arrayData[hole] = x; } /** * @Description: 将传入的数组堆序 */ private void buildHeap() { for(int i=currentSize/2;i>0;i--) { percolateDown(i); } } /** * @Description: 扩充数组 * @param newSize */ private void enlargeArray(int newSize) { if(newSize < arrayData.length ) { //正好是一层叶子节点 newSize = arrayData.length*2; } arrayData = Arrays.copyOf(arrayData, newSize); } public String toString(){ StringBuilder sb = new StringBuilder(); for(int i=1;i<=currentSize;i++){ sb.append(arrayData[i]).append(";"); } return sb.toString(); } }
2. 用来过滤的工具
package Option.heapUse1; import ADT.Heap.BinaryHeap; public class MiniTopHeap<Integer extends Comparable<Integer>> extends BinaryHeap<Integer>{ public MiniTopHeap(int n){ super(n); } @Override public void insert(Integer x) { //数组不够则扩充 if(currentSize == arrayData.length - 1){ throw new RuntimeException(); } super.insert(x); } /** * @Description: 下虑操作 * @param x 新插入的数 */ public void filter(Integer x){ if(currentSize != arrayData.length-1){ throw new RuntimeException("堆尚未存满"); } if(x.compareTo(arrayData[1]) < 0){ return; }else{ arrayData[1] = x; percolateDown(1); } } }
3. 测试代码
/** * @Title: HeapUse1.java * @date: 2016-04-06下午3:38:37 * @version: V1.0 */ package Option.heapUse1; import java.util.Random; /** * @Title: HeapUse1 * @Description: 使用小顶堆来求解问题:给定几个数字数组,求这几个数组的最大的k个值 * 复杂度:Nlogk (N为总数目,k为所求数目) * @author: Lijn * @date: 2016-04-06下午3:38:37 * @version: V1.0 */ public class HeapUse1 { public static void main(String[] args){ Integer[] array1 = new Integer[500]; Integer[] array2 = new Integer[500]; Random rd = new Random(); int k = 100; MiniTopHeap<Integer> heap = new MiniTopHeap<Integer>(k); for(int i =0;i<500;i++){ array1[i]= rd.nextInt(1000); array2[i]= rd.nextInt(1000); } //平均 k 最坏 klogk for(int j=0;j<k;j++){ heap.insert(array1[j]); } //平均 (1000-k)logk 最坏(1000-k)logk for(int j=k;j<500;j++){ heap.filter(array1[j]); } for(int j=0;j<500;j++){ heap.filter(array2[j]); } System.out.println(heap.toString()); } }
这样总的操作时间就是O(nlogk)
在k比n小的较多的情况下是比较合适的。
最后给个使用场景:比如某网站想实时查询当前最热门(搜索次数最多)的几个关键字,就可以用这种办法实现
相关文章推荐
- mysql截取汉字首字母
- cookie--显示用户上次访问时间
- 6.8 发信过程中的结构体选择
- 雷达波段划分
- SQL Server Transact-SQL 编程
- div套div 里面div有浮动 外面div自适应高度
- 《算法竞赛入门经典》6.3.1二叉树-小球下落
- 文件操作-图片的拷贝
- Appium 一个测试套件多次启动android应用
- Project Lombok插件使用
- ios上的数据持久化问题概览(Data Persistence On iOS – An Overview)
- linux ftp批量上传和下载文件
- MySQL主从复制的配置
- Linux Kernel Oops异常分析
- 计算机视觉、机器学习相关领域论文和源代码集合
- pca降维的理论知识
- vector 释放内存 swap
- 顺序表
- 我的NopCommerce之旅(7): 依赖注入(IOC/DI)
- Xilinx实习一年总结