求一個無序陣列中第K小問題
阿新 • • 發佈:2019-02-17
這種問題如果先排序,那麼至少時間複雜度為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());
}