说明:random文件里面含有一系列正整数。要求选择其中最大的k(k=0)个数,并显示。
目的:考察top-K选择算法的效率。
程序流程:
1、读文件到内存数组A;
2、记录程序开始时间;
3、循环1百次
4、 执行top-K选择算法;
(要求不能改变数组A中的内容,对数组A只能读,不能写)
5、循环1百次结束
6、记录程序结束时间;
7、显示1百万次循环的执行时间;
8、显示选择的top-K个数。
实现方法一: | 将n个数排序之后,取出最大的k个,即为所得。 |
时间复杂度: | O(n*lg(n)) |
主要代码块: | Arrays.sort(arr); for(int i = arr.length-1;i>arr.length-10-1;i--){ System.out.println(arr[i]); } |
该方法特点: | 需要对全部元素进行排序 |
输出结果: | Run Time:119(s) |
实现方法二: | 扫描数组,找出最大的、第2大、...、第k大的数,并分别标记 |
时间复杂度: | O(k*(n-k)) |
主要代码块: | ① for(int i = 0;i < 10;i ++) { int maxValueIndex = getMaxValueIndex(arr, 10); if(arr[maxValueIndex] > arr[i]) { int temp = arr[maxValueIndex]; arr[maxValueIndex] = arr[i]; arr[i] = temp; } } ② public static int getMaxValueIndex(int[] arr, int k) { int maxValueIndex = k; for(int i = k + 1;i < arr.length;i ++) { if(arr[i] > arr[maxValueIndex]) { maxValueIndex = i; } } return maxValueIndex; } |
该方法特点: | 只需要对前K个元素排序,不需要对N-K个元素进行排序 |
输出结果: | Run Time:220(s) |
实现方法三: | 把数组的前k个数先放入优先级队列(底层实现是小根堆),从第k+1个数开始扫描: (1) 如果当前数比优先级队列的队列头(小根堆的堆顶)大,说明当前数需要放入优先级队列,这时先弹出优先级队列的队列头,然后将当前数插入优先级队列。 (2) 如果当前数比优先级队列的队列头(小根堆的堆顶)小,说明当前数不会是top-k中的数,直接滤过,处理下一个数。 |
时间复杂度: | O(nlogk) |
主要代码块: | PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>(); for(int i=0;i<10;i++){ priorityQueue.add(arr[i]);} for(int i=0;i<arr.length;i++){ int minInQueue = priorityQueue.peek(); if(minInQueue < arr[i]){ priorityQueue.poll(); priorityQueue.add(arr[i]); } } |
该方法特点: | 尽可能少的遍历所有数据,适合海量数据存储 |
输出结果: | Run Time:50(s) |
package program;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Scanner;
import org.junit.Test;
public class Top_K {
/*
* 方法一:对n个数由大到小排序,然后选择前k个最大的数。时间复杂度:O(nlogn)
* 时间复杂度:O(nlogn)
* */
private static void method1(int[] arr) {
System.out.println("********************方法一:********************");
long start = System.currentTimeMillis();
Arrays.sort(arr);
for(int i = arr.length-1;i>arr.length-10-1;i--){
System.out.println(arr[i]);
}
long end = System.currentTimeMillis();
System.out.println("start time:" + start+ "; end time:" + end+ "; Run Time:" + (end - start)*1000 + "(s)");
}
/*
* 方法二:
* (1)扫描数组A, 选择最大的数,并做标记;
* (2) 扫描数组A, 选择第二大的数,并做标记;
* ...
* (k) 扫描数组A, 选择第K大的数,并做标记;
* 时间复杂度:O(k*(n-k))
* */
private static void method2(int[] arr) {
System.out.println("********************方法二:********************");
long start = System.currentTimeMillis();
/*
* 前k个元素 和除此之外的最大数即arr[maxValueIndex] 比较,
* 如果 第 i(i<10)个元素 大于arr[maxValueIndex] --> 交换这两个数 --> i++ -->对剩余的n-k个数再求最大值;继续该过程
* 如果 第 i(i<10)个元素 小于arr[maxValueIndex] --> i++ --> 对剩余的n-k个数再求最大值;继续该过程
* 循环结束后前k个即为最大的k个数
*/
for(int i = 0;i < 10;i ++) {
int maxValueIndex = getMaxValueIndex(arr, 10);
if(arr[maxValueIndex] > arr[i]) {
int temp = arr[maxValueIndex];
arr[maxValueIndex] = arr[i];
arr[i] = temp;
}
}
for(int i = 0;i < 10;i ++) {
System.out.println(arr[i]);;
}
long end = System.currentTimeMillis();
System.out.println("start time:" + start+ "; end time:" + end+ "; Run Time:" + (end - start)*1000 + "(s)");
}
/*
* 该方法用来寻找除了前K个元素以外的最大值
* */
public static int getMaxValueIndex(int[] arr, int k) {
int maxValueIndex = k;
for(int i = k + 1;i < arr.length;i ++) {
if(arr[i] > arr[maxValueIndex]) {
maxValueIndex = i;
}
}
return maxValueIndex;
}
/*
* 方法三:
* 把数组的前k个数先放入优先级队列(底层实现是小根堆), 从第k+1个数开始扫描。
* (1) 如果当前数比优先级队列的队列头(小根堆的堆顶)大,说明当前数需要放入优先级队列,这时先弹出优先级队列的队列头,然后将当前数插入优先级队列。
* (2) 如果当前数比优先级队列的队列头(小根堆的堆顶)小,说明当前数不会是top-k中的数,直接滤过,处理下一个数。
* 时间复杂度:O(nlogk)
* */
private static void method3(int[] arr) {
System.out.println("********************方法三********************");
long start = System.currentTimeMillis();
//创建一个PriorityQueue,(默认情况下是按照最小堆来排序的)
PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
for(int i=0;i<10;i++){
priorityQueue.add(arr[i]);//把数组的前k个数先放入优先级队列
}
for(int i=0;i<arr.length;i++){
int minInQueue = priorityQueue.peek(); //把小根堆的堆顶设为最小值
if(minInQueue < arr[i]){ //如果当前数比优先级队列的队列头(小根堆的堆顶)大,需要替换堆顶元素,
priorityQueue.poll(); //先弹出优先级队列的队列头
priorityQueue.add(arr[i]); //将当前数插入优先级队列
}
}
int[] arr2 = new int[10]; //创建一个新数组用来接收优先队列的元素
for(int i=0;i<10;i++){
arr2[i] = priorityQueue.poll(); //依次弹出优先级队列的元素,并赋值给新数组
System.out.println(arr2[i]);
}
long end = System.currentTimeMillis();
System.out.println("start time:" + start+ "; end time:" + end+ "; Run Time:" + (end - start)*1000 + "(s)");
}
public static void main(String[] args) throws FileNotFoundException {
//1.从文本文件读取数据,并将数据存到数组集合中
Scanner in = new Scanner(new File("src/program/random.txt"));
//2.用集合来接收文件
ArrayList<Integer> list = new ArrayList<>();
while(in.hasNext()){
list.add(in.nextInt());
}
//3.将集合转为数组
int[] arr = new int[list.size()];
for(int i = 0;i<list.size();i++){
arr[i] = list.get(i);
}
//4.关闭文件
in.close();
//5.调用方法
method1(arr);
method2(arr);
method3(arr);
}
}