【练习】从一组数字中找出最大的几个,用堆完成
来源:互联网 发布:ssh的端口号 编辑:程序博客网 时间:2024/06/03 20:59
从一组数字中找出最大的几个,例如从n个数字中找出最大的k个,最容易想到的方法是首先对这n个数字进行从小到大的排序,然后选出前面的k个数字就行了,快速排序的平均时间复杂度是O(nlogn)。不过,在有些面试题里面会有这样一个条件:
n非常大,以至于无法将这n个数一次性全部读入内存。
这种时候可以用大顶堆来完成这个题目,如果形象的用完全二叉树来想象堆的样子的话,大顶堆中满足“任意一个结点中的值,都大于其子结点中的值”。
在进行实现的时候,也不需要真的用一个树来存放数字,因为是完全二叉树,所以可以很方便的用数组来存放(可以理解为,从树的根结点开始进行广度优先遍历,把遍历的结果以此放在数组中)。只要在这个数组arr中,对于任意一个下标i,满足arr[i*2+1]>=arr[i]和arr[i*2+2]>=arr[i],则这个数组满足堆的性质。
这个数据中的第一个元素(即arr[0])对应那棵完全二叉树的根结点中的值,也就是这个完全二叉树中最大的值(大顶堆的根结点中的值)。只要构建好了这样一个包含k个数的大顶堆,接下来就可以依次遍历那n个数,把每一个数同根结点的值进行比较:
(1)如果这个数大于大顶堆的根结点,那么这个数就不会是“最小的k个数”之一,不理会这个数;
(2)如果这个数等于大顶堆的根结点,虽然这个数是“最小的k个数”之一,但是同样不理会这个数,至于原因,举例来说,我们要找的是最小的3个数,现在大顶堆里面放的是“1,2,2”,现在又读到一个“2”,我们自然不可能把大顶堆里面的“1”换成“2”,用“2”替换“2”等于没换,所以不理会现在读到的这个“2”;
(3)如果这个数小于大顶堆的根结点,那么自然大顶堆的根结点不在“最小的k个数”之中,先用这个新来的数替换大顶堆根结点的值,然后再对大顶堆进行调整,使之保持堆的结构。
用堆来完成“用n个数中找出最小的k个数”,时间复杂度为O(nlogk),时间复杂度比使用排序法稍微好一点,不过最大的优势应该是应对那种“n个数无法一次性全部读入内存”的情况。
至于怎么对大顶堆进行调整,使其保持堆的结构,可以下面的代码,就是replace()方法中的代码。
下面是大顶堆的代码:
public class MyMaxHeap { private int size; private int[] arr; public MyMaxHeap(int size) { this.size=size; arr = new int[size]; for(int i=0; i<size; i++) { arr[i] = Integer.MAX_VALUE; } } public int peek() { return arr[0]; } public void replace(int val) { arr[0] = val; moveBigger2Top(); } public int[] getVals() { return arr; } public void moveBigger2Top() { int maxInd = size-1; int lastNonLeafInd = (maxInd-1)/2; int curNodeInd = 0; while( curNodeInd<= lastNonLeafInd ) { int leftSonInd = curNodeInd * 2+1; int rightSonInd = leftSonInd + 1; int targetNodeInd = leftSonInd; if( rightSonInd<=maxInd && arr[rightSonInd] > arr[leftSonInd] ) { targetNodeInd = rightSonInd; } if( arr[ targetNodeInd ] > arr[curNodeInd] ) { int t=arr[ targetNodeInd ]; arr[ targetNodeInd ] = arr[ curNodeInd ]; arr[ curNodeInd ] = t; } curNodeInd = targetNodeInd; } }}
下面是测试代码,与使用排序法得到的结果进行比较,观察结果是不是一样来判断代码写对了没有。
import java.util.Arrays;import java.util.Collections;public class Test { public static void main(String[] args) { // TODO Auto-generated method stub int length=2000; int heapSize=100; int[] arr1=new int[length]; int[] arr2=new int[length]; for(int i=0; i<length; i++) { arr1[i] = (int)(Math.random()*10000.0); arr2[i] = arr1[i]; } System.out.println("原始数组:"); for(int val: arr1) { System.out.print(val+" "); } MyMaxHeap heap=new MyMaxHeap(heapSize); for(int i=0; i<length; i++) { if(arr1[i] <heap.peek()) { heap.replace(arr1[i]); } } int[] minVals1=heap.getVals(); int sum1=0; System.out.println(); System.out.println("用最大堆选出来的最小"+heapSize+"个数:"); for(int val: minVals1) { sum1+=val; System.out.print(val+" "); } System.out.println(); System.out.println("其和为:"+sum1); Arrays.sort(arr2); int sum2=0; System.out.println(); System.out.println("用排序算法选出来的最小"+heapSize+"个数:"); for(int i=0;i<heapSize; i++) { sum2+=arr2[i]; System.out.print(arr2[i]+" "); } System.out.println(); System.out.println("其和为:"+sum2); System.out.println(); System.out.println("done."); }}
- 【练习】从一组数字中找出最大的几个,用堆完成
- 从一大堆数字中找出几个最大的数
- 找出一组整数中最大的那个
- Python实现:斐波那契数列、交换两个变量的值、从一组数据中找出最大最小值
- 打印一组数字中最大数及出现的次数
- 找出指定目录中最大的几个文件
- 求在一组N个的数中找出第K个最大数
- 找出一组数据中多次出现的所有数字(空间要求最低)
- 一组数据中只有一个数字出现了一次。其他所有数字都是成对出现的。请找出这个数字。
- 一组数据中只有一个数字出现一次 ;其他所有数字都是成对出现的请找出这个数字。
- 一组数据中只有一个数字出现了一次。其他所有数字都是成对出现的。 请找出这个数字。
- 一组数据中只有一个数字出现了一次。其他所有数字都是成对出现的。 请找出这个数字。
- 一组数据中只有一个数字出现了一次,其他所有数字都是成对出现的。 请找出这个数字。
- 【算法练习】找出数组中仅出现一次的数字
- 从一组字符串中取出数字
- 华为机试---找出随机数字中最大的三位数
- 找出数组中最大的数字和位置
- 100亿个数字中找出最大的10个
- C/C++学习路线图
- 在Ubutu12.04上构建apache2服务器
- rails数据交互(4)
- Eclipse中项目面板字体的修改
- uCOS II
- 【练习】从一组数字中找出最大的几个,用堆完成
- spring IOC 反射加载demo
- 18Lab02-android UI
- String对象常用方法和属性
- ARM基础 三、计算机的指令集
- mongodb 的数据备份与恢复
- 边框紧挨的变色框制作
- 一致性哈希算法的理解与实践
- 2017年年度计划