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个数的更多相关文章

  1. 算法练习:寻找最小的k个数

    参考July的文章:http://blog.csdn.net/v_JULY_v/article/details/6370650 寻找最小的k个数题目描述:查找最小的k个元素题目:输入n个整数,输出其中 ...

  2. 03寻找最小的k个数

    题目描述:查找最小的k个元素         题目:输入n个整数,输出其中最小的k个.         例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 1:最简单 ...

  3. 算法笔记_035:寻找最小的k个数(Java)

    目录 1 问题描述 2 解决方案 2.1 全部排序法 2.2 部分排序法 2.3 用堆代替数组法 2.4线性选择算法   1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 ...

  4. 编程之法:面试和算法心得(寻找最小的k个数)

    内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java 题目描述 输入n个整数,输出其中最小的k个. 分析与解法 解法一 要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个 ...

  5. 寻找最小的k个数(四种方法)

    1 使用从大到小的优先队列保存最小的K个数,每次取出K个数之后的其余数和堆顶元素比较,如果比堆顶元素小,则将堆顶元素删除,将该元素插入 void topK(int arr[],int n,int k) ...

  6. 算法练习-寻找最小的k个数

    练习问题来源 https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/02.01.html 要求 输入n个整数, ...

  7. 寻找最小的k个数

    1. 能想到的最直接的办法,就是对数组进行排序,最好的排序算法的时间复杂性为O(n*logn),这一个方法请参照各种排序算法. 2. 另外申请一个k空间数组,依次更改里面的最大值,每做一次最多要扫描一 ...

  8. java实现——030最小的k个数

    1.O(nlogk)海量数据 import java.util.TreeSet; public class T030 { public static void main(String[] args){ ...

  9. 每日一题 - 剑指 Offer 40. 最小的k个数

    题目信息 时间: 2019-06-30 题目链接:Leetcode tag: 快排 难易程度:中等 题目描述: 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3. ...

随机推荐

  1. android 数据库是否该关闭

    关于android多线程数据读写请看博客: android 多线程数据库读写 常常纠结于获取了SQLiteDatabase每次操作完是否要关闭的问题,每次关闭又怕影响性能,这里记录下SQLiteOpe ...

  2. Spring全家桶之springMVC(二)

    spring mvc中url-pattern的写法 1.设置url-pattern为*.do 之前我们在web.xml文件中配置DispatcherServlet的时候,将url-pattern配置为 ...

  3. 关于Nginx参数路径问题的问题

    本文转载自:https://pureage.info/2013/10/31/130.html 由于工作需要,开始分析nginx的proxy模块,在分析之前,当然要先会用了.于是开始熟悉该模块的一些指令 ...

  4. 黑马程序员_毕向东_Java基础视频教程——转义字符(随笔)

    转义字符 转义字符 通过 \ 来转变后面的字母或符号的含义 \n :换行 \b :退格.相当于 backspace \r : 相当于回车键. Windows系统中,回车是由两个字符来表示 \r \n. ...

  5. ql的python学习之路-day6

    字节编码: 这一节主要学习的是各种编码模式的相互转换,另外插两句话,今天的心情不是特别好,又没控制好自己的情绪,以后要心存阳光,好好的对待生活和身边的人. 废话不多说了直接贴码: #!/usr/bin ...

  6. git的日常使用

    首次上传项目到github上 在项目上右击——>选择  Git Bash Here  直接进入到存放项目文件的地址 git init     在当前项目的目录中生成本地的git管理 git ad ...

  7. DRF版本组件

    1.DRF版本组件配置类局部使用 局部配置: 方式一:直接配置versioning_class=QueryParameterVersioning 方式二:直接配置versioning_class=UR ...

  8. Django认证系统之自带auth_user表操作

    Django自带auth_user表操作: views.py from django.contrib  import  auth#引入auth模块 from django.contrib.auth.m ...

  9. 关于Slow HTTP Denial of Service Attack slowhttptest的几种慢攻击DOS原理

    关于Slow HTTP Denial of Service Attack  slowhttptest的几种慢攻击DOS原理 http://www.myhack58.com/Article/60/sor ...

  10. apache slowloris mod_antiloris for Apache httpd 2.2 / 2.4

    http://www.apachelounge.com/viewtopic.php?t=4222