题意详解:有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
================欢迎补充,提问,交流,赐教…………=====================