【算法】Top-K选择

说明: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);
   		
	}	
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值