題意詳解:有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
================歡迎補充,提問,交流,賜教…………=====================