数据排序之TopK问题

前言】在大规模数据处理中,常遇到的一类问题是,在海量数据中找出出现频率最高的前K个数,或者从海量数据中找出最大的前K个数,这类问题通常称为“topK”问题

解决思路

针对topK类问题,通常比较好的方案是【分治+trie树/hash+小顶堆】,即先将数据集按照hash算法分解成多个小数据集,然后使用trie树或者hash表统计每个小数据集中的query词频,之后用小顶堆求出每个数据集中出频率最高的前K个数,最后在所有top K中求出最终的top K。

实际上,最优的解决方案应该是最符合实际设计需求的方案,在实际应用中,可能有足够大的内存,那么直接将数据扔到内存中一次性处理即可,也可能机器有多个核,这样可以采用多线程处理整个数据集。

解决】在得到数据后,如何对获得的数据进行topK排序呢。下面简单介绍几种常见方法。

【1】基于Partition(分区)解决TopK min的问题:根据数组的第K个数字来调整,使得比第K个数小的都在数组的左边,比第K个数据大的所有数字在数组的右边。

/**
	 * 最小的K个数,O(N)解法,需要修改原数组
	 * @param a
	 * @param n
	 * @param b
	 * @param k
	 */
	public void TopKmin(int[] a,int n,int[] b,int k){
		if(a==null||k>n||n<=0||k<=0)
			return;
		int left=0;
		int right=n-1;
		int index=Partition(a,left,right);
		while(index!=k-1){
			if(index>k-1)
				index=Partition(a, left, index-1);
			else
				index=Partition(a, index+1, right);		
		}
		for(int i=0;i=pivot)
				right--;
			if(left

【2】基于小顶堆来实现TopK max的问题:维护一个K个数据的小顶堆,遍历元素,若元素大于堆顶元素,则将堆顶元素移除,当前元素插入堆顶,并进行调整。

复杂度分析:先用Hash表统计每个Query出现的次数,O(N);然后第二步、采用堆数据结构找出Top 10,N*O(logK)。所以,我们最终的时间复杂度是:O(N) + *O(N1*logK)。(N为数据总数,N1为不重元素个数)。

        @Test
	public void testBuildMINHeap(){
		int[] a={49,38,65,97,76,13,49,78,34,12,64};
		int k=3;
		int[] topK=new int[k];
		for(int i=0;iroot){
				topK[0]=a[i];
				//buildMinHeap(topK, k);
				heapfiy(topK, 0, k);
			}
		}
		//buildMinHeap(a,a.length);
		
		System.out.println(Arrays.toString(topK));
	}
	public void heapfiy(int[] a,int i,int len){
		//int len=a.length;
		int left=2*i+1;
		int right=2*i+2;
		int smallest=i;
		while(true){
			if(left=0;i--){
			heapfiy(a, i,len);
		}
	}

【3】基于优先级队列来实现的TopK问题:实现Comparator接口,使得队列元素按照升序/降序排序,维护一个长度为K的数组,把队列的元素出队放到数组中即可。

              /*
                队尾    
                |1        |3
                |2        |2           
                |3        |1
                队头
                降序      升序
                */       
       public static int[] topKLargest(int[] input, int k) {//降序
		   PriorityQueue minheap = new PriorityQueue(k, new Comparator() {
			 @Override
			public int compare(Integer o1, Integer o2) {
				if(o1 < o2) {
					return 1;
				 } else if(o1 > o2) {
					return -1;
				} else {
 					return 0;
 				}
			}
		});
		
		for (int i : input) {
 			minheap.add(i);
		}

		 int[] out = new int[k];
		 for (int i = 0; i < out.length; i++) {
			out[i] = minheap.poll();
		}
		 return out;
	}
 	public static int[] topKSmallest(int[] input, int k) {//升序
		 PriorityQueue minheap = new PriorityQueue(k, new Comparator() {
			@Override
 			public int compare(Integer o1, Integer o2) {
				if(o1 < o2) {
 					return -1;
 				} else if(o1 > o2) {
 					return 1;
 				} else {
					return 0;
 				}
 			}
 		});
 		for (int i : input) {
 			minheap.add(i);
		 }

		 int[] out = new int[k];
		for (int i = 0; i < out.length; i++) {
			out[i] = minheap.poll();
		}
		return out;
 	}

	
	public static void main(String[] args) {
		int[] out = topKLargest(new int[] { 10, 23, 5, 1, 7, 8, 4, 5, 7, 12,
 				523 }, 3);
		System.out.print("Largest: ");
		for (int o : out) {
			System.out.print(o + " ");
		}
		
		out = topKSmallest(new int[] { 10, 23, 5, 1, 7, 8, 4, 5, 7, 12,
				523 }, 3);
		System.out.println();
		System.out.print("Largest: ");
		for (int o : out) {
			System.out.print(o + " ");
		}
	}

【参考资料】

《十道海量数据处理面试题与十个方法大总结》:http://blog.csdn.net/v_JULY_v/archive/2011/03/26/6279498.aspx



你可能感兴趣的:(算法)