算法笔记_035:寻找最小的k个数(Java)
目录
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
附4种方法完整代码:
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);
}
}
完整代码
算法笔记_035:寻找最小的k个数(Java)的更多相关文章
- 算法练习:寻找最小的k个数
参考July的文章:http://blog.csdn.net/v_JULY_v/article/details/6370650 寻找最小的k个数题目描述:查找最小的k个元素题目:输入n个整数,输出其中 ...
- 编程之法:面试和算法心得(寻找最小的k个数)
内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java 题目描述 输入n个整数,输出其中最小的k个. 分析与解法 解法一 要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个 ...
- 03寻找最小的k个数
题目描述:查找最小的k个元素 题目:输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 1:最简单 ...
- 算法系列:寻找最大的 K 个数
Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...
- Java实现寻找最小的k个数
1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 解决方案 2.1 全部排序法 先对这n个整数进行快速排序,在依次输出前k个数. package com.liuzhen. ...
- 寻找最小的k个数(四种方法)
1 使用从大到小的优先队列保存最小的K个数,每次取出K个数之后的其余数和堆顶元素比较,如果比堆顶元素小,则将堆顶元素删除,将该元素插入 void topK(int arr[],int n,int k) ...
- 剑指Offer:面试题30——最小的k个数(java实现)
问题描述: 输入n个整数,找出其中最小的k个数 思路1: 先排序,再取前k个 时间复杂度O(nlogn) 下面给出快排序的代码(基于下面Partition函数的方法) public void Quic ...
- 算法练习-寻找最小的k个数
练习问题来源 https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/02.01.html 要求 输入n个整数, ...
- 寻找最小的k个数
1. 能想到的最直接的办法,就是对数组进行排序,最好的排序算法的时间复杂性为O(n*logn),这一个方法请参照各种排序算法. 2. 另外申请一个k空间数组,依次更改里面的最大值,每做一次最多要扫描一 ...
随机推荐
- 【BZOJ 4229】 4229: 选择 (线段树+树链剖分)
4229: 选择 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 67 Solved: 41 Description 现在,我想知道自己是否还有选择. ...
- -【线性基】【BZOJ 2460】【BZOJ 2115】【HDU 3949】
[把三道我做过的线性基题目放在一起总结一下,代码都挺简单,主要就是贪心思想和异或的高斯消元] [然后把网上的讲解归纳一下] 1.线性基: 若干数的线性基是一组数a1,a2,a3...an,其中ax的最 ...
- 2017-2018-1 JAVA实验站 冲刺 day01
2017-2018-1 JAVA实验站 冲刺 day01 各个成员在 Alpha 阶段认领的任务 小组成员 分工 任务量 张韵琪 写博客.后期市场推广,营销.打杂.各职能的配合 齐力锋 提供宣传用图. ...
- 斐波那契数列(python实现)
描述 一个斐波那契序列,F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2) (n>=2),根据n的值,计算斐波那契数F(n),其中0≤n≤1000. 输入 输入 ...
- 树形DP+(分组背包||二叉树,一般树,森林之间的转换)codevs 1378 选课
codevs 1378 选课 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 学校实行学分制.每门的必修课都有固定的学分 ...
- Codeforces Round #348 (VK Cup 2016 Round 2, Div. 2 Edition) A. Little Artem and Presents 水题
A. Little Artem and Presents 题目连接: http://www.codeforces.com/contest/669/problem/A Description Littl ...
- CodeM资格赛1
题目描述 美团外卖的品牌代言人袋鼠先生最近正在进行音乐研究.他有两段音频,每段音频是一个表示音高的序列.现在袋鼠先生想要在第二段音频中找出与第一段音频最相近的部分. 具体地说,就是在第二段音频中找到一 ...
- 桥接模式:探索JDBC的接口
目录概要 场景问题 假设要设计一个电脑商场管理系统的某个模块设计,电脑分为品牌和类型两个纬度,我们应该怎么解决?我们初学者最容易想到的办法就是利用继承的方式,那利用继承实现的类图又是什么样子呢?我们看 ...
- Android TextView 显示HTML加图片
TextView显示网络图片,我用android2.3的系统,可以显示图片出来,并且如果图片比较大,应用会卡的现象,肯定是因为使用主线程去获取网络图片造成的,但如果我用android4.0以上的系统运 ...
- Codeforces Round #FF (Div. 1) B. DZY Loves Modification
枚举行取了多少次,如行取了i次,列就取了k-i次,假设行列单独贪心考虑然后相加,那么有i*(k-i)个交点是多出来的:dpr[i]+dpc[k-i]-i*(k-i)*p 枚举i取最大值.... B. ...