百度的一道笔试题:N个从大到小排好序的整型队列,求top M元素



题意详解:有N个队列,其中的元素均已经从大到小排序,求出最大的M个元素。

分析:
很容易想到,top elements问题的通用解法是堆(优先队列),但是N和M的大小关系不确实,所以不好处理。
这里,我们分2种情况来考虑。
(我们假设数据输入规则是:第一行输入N和M;接下来N行,每行先输入一个数num表示此行也即是此队列的元素个数,然后紧跟num个从大到小的整数。)
第一种情况(N <= M):
2 4
5  5 4 3 2 1
4  9 8 7 6

2 4
5  9 7 3 2 1
4  8 6 5 4

2 3
2  9 8 
2  7 6
这种情况,容易处理,方法是:
(1)访问N个队列的首元素,建一个含有N个元素的最大堆(即最大元素优先的优先队列);
(2)取出堆头元素并输出,然后将此元素所在队列的下一个元素(如果有)放入堆中;
(3)直到取出M个元素为止(即步骤(2)执行M次结束)。
此方法的时间复杂度:
建堆的复杂度是O(N*lgN),取元素并维护的复杂度是O(M*lgN)。
所以总的时间复杂度是max(O(N*lgN), O(M*lgN))==O(M*lgN)。

第二种情况(N > M):
10 3
1  1
1  1
3  5 4 3
1  1
1  1
3  9 8 7
1  1
1  1
3  8 7 6
1  1
怎么处理呢?
像上面那样?
行是行,只是复杂度太高。
到底有多高呢?
max(O(N*lgN), O(M*lgN))==O(N*lgN)
此复杂度不优么?
是的,不优!
比如:N=1000000, M=50(设想一下,我只需要50个最大的元素,却需要建1000000个元素的堆并维护,这是多么**的事情)。
那么,如何改进呢?
可以这样想,既然有N个队列,那么我们至少得遍历每个队列的第一个元素(也是最大元素)吧,那么O(N)这个复杂度是去不掉的;既然这样,是否可以降低后面的O(lgN)呢;猜想,能不能将其降低到O(lgM)呢?
如何才能降低到O(lgM)呢?
自然容易想到,建M个元素的堆。
最大堆,不对呀?
再想想,应该是最小堆。
具体做法:
(1)将前M个队列的首元素取出,建成含有M个元素的最小堆(即小元素优先的优先队列);
(2)访问剩余的N-M个队列的首元素,维护最小堆(即是说,若这些队列的首元素大于堆中的最小元素,则将其替换);
(3)建一个最大堆,初值是此最小堆中的所有元素;
(4)同第一种情况中的(2)(3)。
时间复杂度:
建堆并维护最小堆的复杂度是O(N*lgM),取元素并维护最大堆的复杂度是O(M*lgM)。
所以总的时间复杂度是max(O(N*lgM), O(M*lgM))==O(N*lgM)。

综合以上2种情况,此题的时间复杂度是MAX(N,M)*lgMIN(N,M)。


import java.util.Comparator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;
import java.util.Vector;


public class TopMelements {
	
	private static int n;
	private static int m;
	
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		while(in.hasNext()) {
			Vector<Queue<Integer>> vec = new Vector<Queue<Integer>>();
			n = in.nextInt();
			m = in.nextInt();
			for(int i=0; i < n; i++) {
				int num =  in.nextInt();
				Queue<Integer> que = new LinkedList<Integer>();
				while(num-- > 0) {
					que.add(in.nextInt());
				}
				vec.add(que);
			}
			Queue<Info> bigHeap = new PriorityQueue<Info>(1, new BigHeapCmp());
			if(n <= m) {
				for(int i=0; i < n; i++) {
					bigHeap.add(new Info(vec.elementAt(i).remove(), i));
				}
			}
			else { // n > m
				Queue<Info> litterHeap = new PriorityQueue<Info>(1, new LitterHeapCmp());
				int i = 0;
				for( ; i < m; i++) {
					litterHeap.add(new Info(vec.elementAt(i).remove(), i));
				}
				for( ; i < n; i++) {
					int x = vec.elementAt(i).remove(); 
					if(x > litterHeap.element().val) {
						litterHeap.remove();
						litterHeap.add(new Info(x, i));
					}
				}
				for(i=0; i < m; i++) {
					bigHeap.add(litterHeap.remove());
				}
			}
			System.out.printf("The Top M Elements: ");
			while(m-- > 0) {
				Info res = bigHeap.remove();
				System.out.printf("%d ", res.val);
				if(false == vec.elementAt(res.queueId).isEmpty()) {
					bigHeap.add(new Info(vec.elementAt(res.queueId).remove(), res.queueId));
				}
			}
			System.out.printf("\n");
		}
	}
	
	private static class LitterHeapCmp implements Comparator<Info> {
		@Override
		public int compare(Info o1, Info o2) {
			return o1.val < o2.val ? -1 : 1;
		}		
	} 
	
	private static class BigHeapCmp implements Comparator<Info> {
		@Override
		public int compare(Info o1, Info o2) {
			return o1.val < o2.val ? 1 : -1;
		}		
	}
	
	private static class Info {
		private int val;
		private int queueId;
		
		Info(int val, int queueId) {
			this.val = val;
			this.queueId = queueId;
		}
	}
	
}


测试数据:

10 3
1  1
1  1
3  5 4 3
1  1
1  1
3  9 8 7
1  1
1  1
3  8 7 6
1  1

5 4
5  5 4 3 2 1
5  5 4 3 2 1
5  20 9 8 7 6
5  15 14 13 12 11
5  20 9 8 7 6

7 6
1  1
1  1
5  5 4 3 2 1
5  5 4 3 2 1
5  20 9 8 7 6
5  15 14 3 2 1
5  20 9 8 7 6

2 4
5  5 4 3 2 1
4  9 8 7 6

2 4
5  9 7 3 2 1
4  8 6 5 4 

2 3
2  9 8 
2  7 6

输出结果如下:
The Top M Elements: 9 8 8 
The Top M Elements: 20 20 15 14 
The Top M Elements: 20 20 15 14 9 9 
The Top M Elements: 9 8 7 6 
The Top M Elements: 9 8 7 6 
The Top M Elements: 9 8 7 


================欢迎补充,提问,交流,赐教…………=====================

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值