1. 程式人生 > >求一個無序陣列中第K小問題

求一個無序陣列中第K小問題

這種問題如果先排序,那麼至少時間複雜度為O(nlogn)

但是有種方法可以達到O(n)

這種方法有個闕值,只有大於44的時候用這種方法才比先排序後查詢的快

首先上一段虛擬碼吧

if(N<44) then 排序A return A[K];   ---O(1)
else 
	i <- [N/5] 將陣列分成五個元素一小組,一共N/5組  ---O(N)
排序這個5個元素的陣列,找到這個N/5組中每組的中間項 組成一個集合S ---O(N)
m <- select(S,N/5,(N/5)/2) 找到這個集合中的中間項m  --T(N/5)

B = { a | a < m } 
C = { a | a = m }          ---O(N)
D = { a | a > m }  

if(k<B.length) then return select(B,B.length,k) 
else	
	if(k<B.length+C.length) return m
	if(K>B.length+C.length) return select(D,D.length,k-B.length-C.length)

分析下select演算法在最壞的情況下的時間複雜度
唯一難以判別的情況只有最後一部分程式碼,其他部分的程式碼的時間複雜度都能很輕易的看出
想確定最後一部分程式碼的時間複雜度先確定問題規模,也就是那三個集合BCD的規模
因為m為中位數,N/5個數組也排序了,可以將其分成四組+1個點 最壞的情況下,有三組是小於m數的也就是B集合
這三組是有兩組只取N/5個數組中的兩個元素,有一個取3個,那麼相對於原問題N的規模是7/10
也就是說 W(n) = w(n/5) + w(7/10) + cn 然後用遞迴樹解一下,得到O(N)

上面的文字說明有點玄乎,沒關係我還特意畫了圖

以下都是假設最壞的情況

紅色的就是我們求的那個m,也就是將陣列劃分N/5個小陣列後每個小陣列排好序,取中間項組成的集合S當作的中間項

然後紅色哪一行,從左到右是升序

這圖當中從上到下是依次減小的!!!!!

然後我們假設黃色+藍色+綠色都是比紅色小的,這種情況是存在的吧,沒毛病吧?

然後我們算下黃色+藍色+綠色的範圍(規模)是多大

為了研究問題方便,然後我們近似的認除了紅色的其他四個顏色區域列數相等,(在研究N規模問題的時候是沒有問題的,相別不是很大,只有紅色上面下面兩個數的差別可以忽略)

2r = 藍色

2r = 綠色

3r = 黃色

3r = 棕色

2r + 2r + 3r + 3r = N

5個數,不同色所在的比例不一樣,不是佔3個就是佔2個

全部顏色加起來就是N個元素

很明顯可以得到上面的結論

所以黃色+藍色+綠色是佔全部的7N/10

那麼很明顯了W(n) <<= w(n/5) + w(7n/10) + cn

得到O(N)

下面給出一個java版本的實現,簡單實現

	
	/**
	 * return Nth small integer in this array
	 * @param array an integer array
	 * @param length length of this integer array
	 * @param index Nth
	 * @return number of Nth small integer in this array
	 */
	public static int select(ArrayList<Integer> array,	int length,	int index){
		
		if(length<44){
			Collections.sort(array);
			return array.get(index-1);
		}
		
		int i = length/5;
		ArrayList<Integer> S =  new ArrayList<>(i);
		int j = 1;
		while(j<=i){
			int k = (j-1)*5;
			int[] n = new int[5];
			n[0] = array.get(k);
			n[1] = array.get(k+1);
			n[2] = array.get(k+2);
			n[3] = array.get(k+3);
			n[4] = array.get(k+4);
			Arrays.sort(n);
			S.add(n[2]);
			j++;
		}
		
		int m = select(S,i,i/2);
		
		ArrayList<Integer> less = new ArrayList<>();
		ArrayList<Integer> equal = new ArrayList<>();
		ArrayList<Integer> more = new ArrayList<>();
		
		for(int idx = 0;idx<array.size();idx++){
			if(array.get(idx)<m){
				less.add(array.get(idx));
			}else if(array.get(idx)==m){
				equal.add(array.get(idx));
			}else{
				more.add(array.get(idx));
			}
		}
	
		if(index<=less.size()){
			return select(less,less.size(),index);
		}else if(index<=less.size()+equal.size()){
			return m;
		}else
			return select(more,more.size(),index-less.size()-equal.size());
			
	}