问题:输入n个整数,找出其中最小的k个数。数组
方案一:将输入的n个整数进行排序,输出前k个数即为所求的k个最小数。时间复杂度为O(nlogn).ui
方案二:建立一个大小为k的容器,来存储最小的k个数。遍历剩下的n-k个数字,若是大于k个数中的最大值,则替换;不然继续遍历数组的剩 下的数字。spa
在装k个最小数字的容器(使用大根堆)中,所要作的操做有如下三个:code
(1)在k个整数中找到最大的值;(时间复杂度为O(1),根节点即为最大值点)blog
(2)在这个容器中删除最大的数字;排序
(3)将可能要插入的值插入到容器中。(插入和删除的时间复杂度为O(logk))class
总共有n个节点,则总的时间复杂度为O(nlogk)。容器
package com.wyl; /** * 找到数组中的k个最小值 * @author wyl */
public class KMin { /** * 找到数组array中最下的k个数 * 思路:将数组的前K个数看成最小的k个数,存入到数组2中, * 遍历array中剩余的n-k个数,若是比数组2中的最大数字小,替换数组2中的数字; * 比其大继续遍历,最后数组2中存放的即为最小的k个数 * @param array * @param k * @return
*/
public void findKMin(int[] array, int k){ if(array.length < k){ return; } int[] newArray = new int[k]; for(int i=0;i<k;i++){ //前k个数字存入数组中
newArray[i] = array[i]; } //将数组调整成大根堆,k/2为堆的最后一个非叶节点
for(int i = k/2 - 1;i>=0;i--){ //最后一个非叶节点的下标为n/2-1
rebuildHeap(newArray, i, k); } //n-k个数和前面和k中最大数比较
for (int i =k; i < array.length; i++) { //若是堆顶大于n-k中数,则交换位置
if(newArray[0]>array[i]){ newArray[0]=array[i]; //调整堆,堆顶被替换了,加入被替换的值很是小,会一直下沉到叶子节点.
for(int j = k/2 - 1;j>=0;j--){ //最后一个非叶节点的下标为n/2-1
rebuildHeap(newArray, j, k); } } } // 输出最小的K个数
for (int i = 0; i < k; i++) { System.out.print(newArray[i] + " "); } } /** * 调整堆 * @param a * @param i * @param n */
public void rebuildHeap(int[] a, int i, int n){ if(n <= 0){ return; } int temp = a[i]; //待调整节点
boolean finish = false; //调整完成标志
int li = 2 * i + 1; //左节点下标
while(li <= n - 1 && !finish){ if(li < n - 1 && a[li] < a[li+1]){ //节点存在右子树,且右子树值大于左子树
li = li+1; } if(temp < a[li]){ //待调整节点值小于子树值,继续调整子树
a[i] = a[li]; a[li] = temp; }else{ finish = true; } } } public static void main(String[] args) { KMin kMin = new KMin(); int[] array = {4,5,1,6,3,8,7,9}; kMin.findKMin(array, 4); } }