【Java】 剑指offer(40) 最小的k个数
2024-10-12 22:23:30
本文参考自《剑指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<Integer> GetLeastNumbers_Solution1(int [] input, int k) {
ArrayList<Integer> leastNumbers = new ArrayList<Integer>();
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<k-1){
start=index+1;
index=partition(input,start,end);
}else{
end=index-1;
index=partition(input,start,end);
}
}
for(int i=0;i<k;i++){
leastNumbers.add(input[i]);
}
return leastNumbers;
} private int partition(int[] arr, int start,int end){
int pivotKey=arr[start];
while(start<end){
while(start<end && arr[end]>=pivotKey)
end--;
swap(arr,start,end);
while(start<end && arr[start]<=pivotKey)
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<Integer> GetLeastNumbers_Solution2(int [] input, int k) {
ArrayList<Integer> leastNumbers = new ArrayList<Integer>();
while(input==null || k<=0 || k>input.length)
return leastNumbers;
int[] numbers=new int[k]; //用于放最小的k个数
for(int i=0;i<k;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<input.length;i++){
if(input[i]<numbers[0]){ //存在更小的数字时
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]<temp)
break;
arr[start]=arr[child];
start=child;
child=child*2+1;
}
arr[start]=temp;
}
}
大顶堆可以用PriorityQueue实现,所以方法二可利用API实现如下:
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> list = new ArrayList<>();
if(input==null || input.length<k || k<=0)
return list;
PriorityQueue<Integer> heap = new PriorityQueue<>(k,(i1, i2)->(i2-i1));
for(int i=0; i<input.length; i++){
if(heap.size()<k){
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()
最新文章
- Config文件
- Java并发工具类Semaphore应用实例
- 编写Music类
- maven项目依赖小试牛刀
- HDU 4633 Who&#39;s Aunt Zhang(polay计数)
- Atmel Studio 6.0 重新安装
- SQL Server 查看数据页面
- Centos6.7安装mysql 5.6简单教程
- Java中从键盘输入的三种方法
- http/https协议
- Python程序员常用的IDE和其它开发工具
- C++ 使用LockWorkStation()的过程遇到的问题
- Xamarin Essentials教程实现数据的传输功能实例
- npm的packagejson文件
- pacakge-info.java
- logback logback.xml常用配置详解(二)<;appender>;
- css 基础1
- Google ProtocolBuffers2.4.1应用说明(一)
- unbound域名解析
- 如何在前端项目中引用bootstrap less?
热门文章
- node版本管理工具nvm安装以及使用
- 【TensorFlow】tf.nn.softmax_cross_entropy_with_logits的用法
- AutoML相关论文
- GPIO接口解析【转】
- kafka系列四、kafka架构原理、高可靠性存储分析及配置优化
- python模块介绍- binascii:二进制和ASCII互转以及其他进制转换
- 『转载』hadoop 1.X到2.X的变化
- mysql字符串 转 int-double CAST与CONVERT 函数的用法
- 安装installshield问题
- webpack文件笔记