1. 程式人生 > >百度的一道筆試題:N個從大到小排好序的整型佇列,求top M元素

百度的一道筆試題: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 

================歡迎補充,提問,交流,賜教…………=====================