Java实现寻找最小的k个数
1 问题描述
有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低。
2 解决方案
2.1 全部排序法
先对这n个整数进行快速排序,在依次输出前k个数。
package com.liuzhen.array_2;
public class SearchMinK {
//方法1:全部排序
public void quickSort(int[] A,int start,int end){
if(end > start){
int k = LomutoPartition(A,start,end);
quickSort(A,start,k-1);
quickSort(A,k+1,end);
}
}
//返回数值result,满足: 左边部分< A[result] <=右边部分
public int LomutoPartition(int[] A,int start,int end){
if(start >= end)
return start;
int begin = A[start];
int result = start;
for(int i = start + 1;i <= end;i++){
if(A[i] < begin){
result++;
swap(A,i,result);
}
}
swap(A,start,result);
return result;
}
//交换数组m位置和n位置上的值
public void swap(int[] arrayA,int m,int n){
int temp = arrayA[m];
arrayA[m] = arrayA[n];
arrayA[n] = temp;
}
//输出数组前k个元素
public void printArrayK(int[] array,int k){
for(int i = 0;i < k;i++){
System.out.print(array[i]+" ");
}
}
public static void main(String[] args){
SearchMinK test = new SearchMinK();
int[] A = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.quickSort(A, 0, A.length-1);
System.out.println("对数组进行排序后结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n"+"输出数组最小的5个数:");
test.printArrayK(A, 5);
}
}
运行结果:
对数组进行排序后结果:
1 1 2 2 3 3 3 4 4 4 5 5 6 6 7 8 9 12 32 34
输出数组最小的5个数:
1 1 2 2 3
2.2 部分排序法
具体操作步骤如下:
(1)遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设他们就是最小的k个数;
(2)利用选择排序或交换排序找到这k个元素中的最大值kmax;
(3)继续遍历剩余的n-k个数。假设每次遍历到的新元素的值为x,把x与kmax进行比较:如果x<kmax,则用x替换kmax,并回到第2步重新找出k个元素的数组中新的最大元素kmax;如果x>=kmax,则继续遍历,不更新数组。
具体代码如下:
package com.liuzhen.array_2;
public class SearchMinK {
//方法2:部分排序
public void getArrayMinK(int[] A,int k){
if(k > A.length)
return;
while(true){
int max = getMaxArrayK(A,k); //当前数组前k个元素中的最大值
int count = 0;
for(int i = k;i < A.length;i++){
if(A[max] > A[i])
swap(A,max,i);
else
count++;
}
if(count == A.length-k)
break;
}
System.out.println("\n"+"使用方法2进行部分排序后的结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n部分排序选出数组中最小的"+k+"个数:");
for(int i = 0;i < k;i++)
System.out.print(A[i]+" ");
}
//获取数组前k个元素的最大值的数组下标
public int getMaxArrayK(int[] A,int k){
int result = 0;
if(k > A.length)
return 0;
for(int i = 0;i < k;i++){
if(A[i] > A[result])
result = i;
}
return result;
}
//交换数组m位置和n位置上的值
public void swap(int[] arrayA,int m,int n){
int temp = arrayA[m];
arrayA[m] = arrayA[n];
arrayA[n] = temp;
}
public static void main(String[] args){
SearchMinK test = new SearchMinK();
int[] B = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.getArrayMinK(B, 5);
}
运行结果:
使用方法2进行部分排序后的结果:
1 1 2 2 3 9 8 7 6 5 4 5 12 32 4 3 3 4 6 34
部分排序选出数组中最小的5个数:
1 1 2 2 3
2.3 用堆代替数组法
此处思想和2.2中一致,唯一区别就是在寻找kmax时,是使用堆排序的思想。
具体代码如下:
package com.liuzhen.array_2;
public class SearchMinK {
//方法3:用堆来代替数组
/*
* 函数功能:对数组A前k个元素进行堆排序
*/
public void heapBottomUp(int[] A,int k){
for(int i = (k-1)/2;i >= 0;i--){
int temp = i;
int tempV = A[temp];
boolean heap = false;
while(!heap && 2*temp < k-1){
int j = 2*temp + 1;
if(j < k-1){
if(A[j] < A[j+1])
j = j + 1;
}
if(tempV >= A[j])
heap = true;
else{
A[temp] = A[j];
temp = j;
}
}
A[temp] = tempV;
}
}
public void getArrayMinK2(int[] A,int k){
heapBottomUp(A,k);
while(true){
int count = 0;
for(int i = k;i < A.length;i++){
if(A[i] < A[0]){
swap(A,i,0);
heapBottomUp(A,k);
}
else
count++;
}
if(count == A.length-k)
break;
}
System.out.println("\n"+"使用方法3进行部分堆排序后的结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n部分排序选出数组中最小的"+k+"个数:");
for(int i = 0;i < k;i++)
System.out.print(A[i]+" ");
}
//交换数组m位置和n位置上的值
public void swap(int[] arrayA,int m,int n){
int temp = arrayA[m];
arrayA[m] = arrayA[n];
arrayA[n] = temp;
}
public static void main(String[] args){
SearchMinK test = new SearchMinK();
int[] D = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.getArrayMinK2(D, 5);
}
}
运行结果:
使用方法3进行部分堆排序后的结果:
3 2 2 1 1 9 8 7 6 5 4 5 12 32 4 3 3 4 6 34
部分排序选出数组中最小的5个数:
3 2 2 1 1
2.4线性选择算法
看具体代码即可理解其中蕴含的思想。
package com.liuzhen.array_2;
public class SearchMinK {
//返回数值result,满足: 左边部分< A[result] <=右边部分
public int LomutoPartition(int[] A,int start,int end){
if(start >= end)
return start;
int begin = A[start];
int result = start;
for(int i = start + 1;i <= end;i++){
if(A[i] < begin){
result++;
swap(A,i,result);
}
}
swap(A,start,result);
return result;
}
//方法4:线性选择法
public void getArrayMinK3(int[] A,int k){
int start = 0;
int end = A.length - 1;
int tempK = LomutoPartition(A,start,end);
while(tempK != k){
if(tempK > k){
end = tempK - 1;
tempK = LomutoPartition(A,start,end);
}
if(tempK < k){
start = tempK + 1;
tempK = LomutoPartition(A,start,end);
}
}
System.out.println("\n"+"使用方法4进行快速选择排序后的结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n部分排序选出数组中最小的"+k+"个数:");
for(int i = 0;i < k;i++)
System.out.print(A[i]+" ");
}
public static void main(String[] args){
SearchMinK test = new SearchMinK();
int[] E = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.getArrayMinK3(E, 5);
}
}
运行结果:
使用方法4进行快速选择排序后的结果:
1 2 2 1 3 3 3 4 5 5 4 4 6 8 6 7 9 32 12 34
部分排序选出数组中最小的5个数:
1 2 2 1 3
此处附上四种方法的完整代码
package com.liuzhen.array_2;
public class SearchMinK {
//方法1:全部排序
public void quickSort(int[] A,int start,int end){
if(end > start){
int k = LomutoPartition(A,start,end);
quickSort(A,start,k-1);
quickSort(A,k+1,end);
}
}
//返回数值result,满足: 左边部分< A[result] <=右边部分
public int LomutoPartition(int[] A,int start,int end){
if(start >= end)
return start;
int begin = A[start];
int result = start;
for(int i = start + 1;i <= end;i++){
if(A[i] < begin){
result++;
swap(A,i,result);
}
}
swap(A,start,result);
return result;
}
//交换数组m位置和n位置上的值
public void swap(int[] arrayA,int m,int n){
int temp = arrayA[m];
arrayA[m] = arrayA[n];
arrayA[n] = temp;
}
//输出数组前k个元素
public void printArrayK(int[] array,int k){
for(int i = 0;i < k;i++){
System.out.print(array[i]+" ");
}
}
//方法2:部分排序
public void getArrayMinK(int[] A,int k){
if(k > A.length)
return;
while(true){
int max = getMaxArrayK(A,k); //当前数组前k个元素中的最大值
int count = 0;
for(int i = k;i < A.length;i++){
if(A[max] > A[i])
swap(A,max,i);
else
count++;
}
if(count == A.length-k)
break;
}
System.out.println("\n"+"使用方法2进行部分排序后的结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n部分排序选出数组中最小的"+k+"个数:");
for(int i = 0;i < k;i++)
System.out.print(A[i]+" ");
}
//获取数组前k个元素的最大值的数组下标
public int getMaxArrayK(int[] A,int k){
int result = 0;
if(k > A.length)
return 0;
for(int i = 0;i < k;i++){
if(A[i] > A[result])
result = i;
}
return result;
}
//方法3:用堆来代替数组
/*
* 函数功能:对数组A前k个元素进行堆排序
*/
public void heapBottomUp(int[] A,int k){
for(int i = (k-1)/2;i >= 0;i--){
int temp = i;
int tempV = A[temp];
boolean heap = false;
while(!heap && 2*temp < k-1){
int j = 2*temp + 1;
if(j < k-1){
if(A[j] < A[j+1])
j = j + 1;
}
if(tempV >= A[j])
heap = true;
else{
A[temp] = A[j];
temp = j;
}
}
A[temp] = tempV;
}
}
public void getArrayMinK2(int[] A,int k){
heapBottomUp(A,k);
while(true){
int count = 0;
for(int i = k;i < A.length;i++){
if(A[i] < A[0]){
swap(A,i,0);
heapBottomUp(A,k);
}
else
count++;
}
if(count == A.length-k)
break;
}
System.out.println("\n"+"使用方法3进行部分堆排序后的结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n部分排序选出数组中最小的"+k+"个数:");
for(int i = 0;i < k;i++)
System.out.print(A[i]+" ");
}
//方法4:线性选择法
public void getArrayMinK3(int[] A,int k){
int start = 0;
int end = A.length - 1;
int tempK = LomutoPartition(A,start,end);
while(tempK != k){
if(tempK > k){
end = tempK - 1;
tempK = LomutoPartition(A,start,end);
}
if(tempK < k){
start = tempK + 1;
tempK = LomutoPartition(A,start,end);
}
}
System.out.println("\n"+"使用方法4进行快速选择排序后的结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n部分排序选出数组中最小的"+k+"个数:");
for(int i = 0;i < k;i++)
System.out.print(A[i]+" ");
}
public static void main(String[] args){
SearchMinK test = new SearchMinK();
int[] A = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.quickSort(A, 0, A.length-1);
System.out.println("对数组进行排序后结果:");
for(int i = 0;i < A.length;i++)
System.out.print(A[i]+" ");
System.out.println("\n"+"输出数组最小的5个数:");
test.printArrayK(A, 5);
int[] B = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.getArrayMinK(B, 5);
int[] C = {2,9,7,6,5,8};
test.heapBottomUp(C, 6);
System.out.println("\nC数组:");
for(int i = 0;i < C.length;i++)
System.out.print(C[i]+" ");
int[] D = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.getArrayMinK2(D, 5);
int[] E = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
test.getArrayMinK3(E, 5);
}
}
完整代码
Java实现寻找最小的k个数的更多相关文章
- 算法练习:寻找最小的k个数
参考July的文章:http://blog.csdn.net/v_JULY_v/article/details/6370650 寻找最小的k个数题目描述:查找最小的k个元素题目:输入n个整数,输出其中 ...
- 03寻找最小的k个数
题目描述:查找最小的k个元素 题目:输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 1:最简单 ...
- 算法笔记_035:寻找最小的k个数(Java)
目录 1 问题描述 2 解决方案 2.1 全部排序法 2.2 部分排序法 2.3 用堆代替数组法 2.4线性选择算法 1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 ...
- 编程之法:面试和算法心得(寻找最小的k个数)
内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java 题目描述 输入n个整数,输出其中最小的k个. 分析与解法 解法一 要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个 ...
- 寻找最小的k个数(四种方法)
1 使用从大到小的优先队列保存最小的K个数,每次取出K个数之后的其余数和堆顶元素比较,如果比堆顶元素小,则将堆顶元素删除,将该元素插入 void topK(int arr[],int n,int k) ...
- 算法练习-寻找最小的k个数
练习问题来源 https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/02.01.html 要求 输入n个整数, ...
- 寻找最小的k个数
1. 能想到的最直接的办法,就是对数组进行排序,最好的排序算法的时间复杂性为O(n*logn),这一个方法请参照各种排序算法. 2. 另外申请一个k空间数组,依次更改里面的最大值,每做一次最多要扫描一 ...
- java实现——030最小的k个数
1.O(nlogk)海量数据 import java.util.TreeSet; public class T030 { public static void main(String[] args){ ...
- 每日一题 - 剑指 Offer 40. 最小的k个数
题目信息 时间: 2019-06-30 题目链接:Leetcode tag: 快排 难易程度:中等 题目描述: 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3. ...
随机推荐
- 1058 A+B in Hogwarts (20分)
1058 A+B in Hogwarts (20分) 题目: If you are a fan of Harry Potter, you would know the world of magic h ...
- 201843 2019-2020-2 《Python程序设计》实验二报告
201843 2019-2020-2 <Python程序设计>实验二报告 课程:<Python程序设计> 班级: 1843 姓名: 李新锐 学号:20184302 实验教师:王 ...
- Python中内置函数
python提供了很多的内置函数,这些内置的函数在某些情况下,可以起到很大的作用,而不需要专门去 写函数实现XX功能,直接使用内置函数就可以实现,下面分别来学习内置函数的使用和案例代码. abs(), ...
- SpringBoot + react app 项目,解决跨域问题的配置(跳坑含泪总结,亲测有效)
方法一: 对某一接口配置,可以在方法上添加 @CrossOrigin 注解 @CrossOrigin(origins = {"http://localhost:8110", &qu ...
- python控制台实现打印带颜色的字体
控制台颜色分类: 数值表示的参数含义: 显示方式: 0(默认值).1(高亮).22(非粗体).4(下划线).24(非下划线). 5(闪烁).25(非闪烁).7(反显).27(非反显)前景色: 30(黑 ...
- Java内存区域与内存溢出异常——深入理解Java虚拟机 笔记一
Java内存区域 对比与C和C++,Java程序员不需要时时刻刻在意对象的创建和删除过程造成的内存溢出.内存泄露等问题,Java虚拟机很好地帮助我们解决了内存管理的问题,但深入理解Java内存区域,有 ...
- linux常用命令---文件拷贝与传输
拷贝命令 文件传输
- 08-Python之路---初识函数
Python之路---初识函数️ 程序员三大美德: 懒惰 因为一直致力于减少工作的总工作量. 缺乏耐性 因为一旦让你去做本该计算机完成的事,你将会怒不可遏. 傲慢 因为被荣誉感冲晕头的你会把程序写得让 ...
- jsonp跨域封装
一.什么是同源政策? 同源策略是指在Web浏览器中,允许某个网页脚本访问另一个网页的数据,但前提是这两个网页必须有相同的URI.主机名和端口号,一旦两个网站满足上述条件,这两个网站就被认定为具有相同来 ...
- 远程快速安装redis和远程连接
一.安装redis 1.设置redis的仓库地址, 执行命令: yum install epel-release 出现下图即设置成功 2.安装redis 执行命令如下: yum insta ...