最小的k个数 java_【Java】 剑指offer(40) 最小的k个数

本文参考自《剑指offer》一书,代码采用Java语言。

题目

输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

思路

思路一:同剑指offer(39) 数组中出现次数超过一半的数字中使用partition()方法,基于数组的第k个数字调整,使得更小的k个数字都在数组左边即可。

思路二:依次遍历n个整数,用一个容器存放最小的k个数字,每遇到比容器中最大的数字还小的数字时,将最大值替换为该数字。容器可以使用最大堆或者红黑树来实现。本文根据堆排序的原理来实现。

测试算例

1.功能测试(数组中存在/不存在重复数字)

2.边界值测试(k=1或者等于数组长度)

2.特殊测试(null、k<1、k大于数组长度)

Java代码

//题目:输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8

//这8个数字,则最小的4个数字是1、2、3、4。

public class KLeastNumbers {

/*

* 方法一:采用partition()

*/

public ArrayList GetLeastNumbers_Solution1(int [] input, int k) {

ArrayList leastNumbers = new ArrayList();

while(input==null || k<=0 || k>input.length)

return leastNumbers;

int start=0;

int end=input.length-1;

int index=partition(input,start,end);

while(index!=k-1){

if(index

start=index+1;

index=partition(input,start,end);

}else{

end=index-1;

index=partition(input,start,end);

}

}

for(int i=0;i

leastNumbers.add(input[i]);

}

return leastNumbers;

}

private int partition(int[] arr, int start,int end){

int pivotKey=arr[start];

while(start

while(start=pivotKey)

end--;

swap(arr,start,end);

while(start

start++;

swap(arr,start,end);

}

return start;

}

private void swap(int[] arr, int i,int j){

int temp=arr[i];

arr[i]=arr[j];

arr[j]=temp;

}

/*

* 方法二:基于堆的容器

*/

public ArrayList GetLeastNumbers_Solution2(int [] input, int k) {

ArrayList leastNumbers = new ArrayList();

while(input==null || k<=0 || k>input.length)

return leastNumbers;

int[] numbers=new int[k]; //用于放最小的k个数

for(int i=0;i

numbers[i]=input[i];//先放入前k个数

for(int i=k/2-1;i>=0;i--){

adjustHeap(numbers,i,k-1);//将数组构造成最大堆形式

}

for(int i=k;i

if(input[i]

numbers[0]=input[i];

adjustHeap(numbers,0,k-1);//重新调整最大堆

}

}

for(int n:numbers)

leastNumbers.add(n);

return leastNumbers;

}

//最大堆的调整方法,忘记时可以复习一下堆排序。

private void adjustHeap(int[] arr,int start,int end){

int temp=arr[start];

int child=start*2+1;

while(child<=end){

if(child+1<=end && arr[child+1]>arr[child])

child++;

if(arr[child]

break;

arr[start]=arr[child];

start=child;

child=child*2+1;

}

arr[start]=temp;

}

}

大顶堆可以用PriorityQueue实现,所以方法二可利用API实现如下:

public ArrayList GetLeastNumbers_Solution(int [] input, int k) {

ArrayList list = new ArrayList<>();

if(input==null || input.length

return list;

PriorityQueue heap = new PriorityQueue<>(k,(i1, i2)->(i2-i1));

for(int i=0; i

if(heap.size()

heap.offer(input[i]);

}else if(heap.peek()>input[i]){

heap.poll();

heap.offer(input[i]);

}

}

for(int i: heap)

list.add(i);

return list;

}

收获

1.k小于等于0的情况别忘记了

2.方法二,只需要在原始数组中进行读入操作,而所有的写操作和判断都是在容器中进行的,不用反复读取原始数组,思想非常好。

3.记得要弄清楚是否可以改变原始输入的数组。

4.partition函数:即是快速排序的基础,也可以用来查找n个数中第k大的数字。

5.当涉及到频繁查找和替换最大最小值时,二叉树是非常合适的数据结构,要能想到堆和二叉树。

6. PriorityQueue重点:

* 大顶堆与小顶堆的构建: new Comporator()

* 如何插值: heap.offer(e)

* 如何获取堆顶的值: heap.peek(), heap.poll()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值