Top K算法

Top K问题:顾名思义,就是给你多个数,让你找出最大的或者最小的K个数。

分析:通常情况下,数量级都是千万级别的,数据量特别大,所以肯定不能先排序,然后再遍历取出K个数。

我们以求出最小的K个数为例进行分析。既然一个大顶堆的顶是最大的元素,那我们要找最小的K个元素,可以先建立一个包含K个元素的大顶堆,然后遍历集合,如果集合的元素比堆顶元素小(说明它目前应该在K个最小之列),那就用该元素来替换堆顶元素,同时维护该堆的性质,那在遍历结束的时候,堆中包含的K个元素就是我们要找的最小的K个元素。

算法实现如下:

public class TopK {//最小的k个元素
	public static int[] topK(int[] nums, int k){
		int[] res=new int[k];
		for(int i=0; i<k; i++){
			res[i]=nums[i];
		}
		for(int i=(k-1)/2; i>=0; i--){//建一个初始堆
			sift(res, i);
		}
		//从nums中第k项开始,与大顶堆的根节点依次比较,并进行调整
		for(int i=k; i<nums.length; i++){
			if(nums[i]<res[0]){
				res[0]=nums[i];
				sift(res, 0);
			}
		}
		return res;
	}
	
	private static void sift(int[] nums, int index){//针对某个节点进行调整,使之符合大顶堆
		int i=index;
		int j=2*(i+1)-1;
		while(j<nums.length){
			if(j+1<nums.length && nums[j]<nums[j+1]){
				j++;//j指向较大的子节点
			}
			if(nums[i]<nums[j]){//调整节点
				int temp=nums[i];
				nums[i]=nums[j];
				nums[j]=temp;
				i=j;//追踪调整
				j=2*(i+1)-1;				
			}
			else{
				break;
			}
		}
	}
}

堆排序做TopK算法有如下几个特点:

  1、不会改变数据的输入顺序;

  2、不会占用太多的内存空间(事实上,一次只读入一个数,内存只要求能容纳前K个数即可);

  3、由于2,决定了它特别适合处理海量数据。

此外,还有一点要注意:要找出最小的K个元素使用大顶堆,求最大的K个元素使用小顶堆。小对大,大对小,很好记。


附:求最大的K个元素的算法实现(和前面的代码一毛一样)

public class TopK2 {//最大的k个元素
	public static int[] topK2(int[] nums, int k){
		int[] res=new int[k];
		for(int i=0; i<k; i++){
			res[i]=nums[i];
		}
		for(int i=(k-1)/2; i>=0; i--){
			sift(res, i);
		}
		
		for(int i=k; i<nums.length; i++){
			if(nums[i]>res[0]){
				res[0]=nums[i];
				sift(res, 0);
			}
		}
		
		return res;
	}
	
	private static void sift(int[] nums, int index){
		int i=index;
		int j=2*(i+1)-1;
		while(j<nums.length){
			if(j+1<nums.length && nums[j]>nums[j+1]){
				j++;
			}
			if(nums[i]>nums[j]){
				int temp=nums[i];
				nums[i]=nums[j];
				nums[j]=temp;
				i=j;
				j=2*(i+1)-1;
			}
			else{
				break;
			}
		}
	}

}

猜你喜欢

转载自blog.csdn.net/karute/article/details/79903273
今日推荐