減治法在查詢演算法中的應用(JAVA)--快速查詢
阿新 • • 發佈:2019-01-30
減治法在查詢演算法中的應用
快速查詢:選擇問題是求一個n個數列表的第k個最小元素的問題,這個數k被稱為順序統計量。對於k=1或k=n來說,這並沒有什麼意義,我們通常會要找出這樣的元素:該元素比列表中一半元素大,比另一半元素小,這樣的元素被稱為中值。我們當然可以對列表進行排序,之後找出對應下標的值,但是!!!這樣一個查詢問題,反而要對整個列表排序,是不是有點多餘了呢?
這裡引入劃分的概念我們可以標定一個樞軸(任意元素,一般為首個元素),使得左半部分元素均小於樞軸,右半部分均大於樞軸。劃分的方法由兩種,Lomuto劃分和Hoare劃分。這裡僅介紹Lomuto。
我們假設有一個數組a[0, n-1],其子陣列為a[l, r](0 <= l <= r <=
n-1),假定首個元素為樞軸p,將該陣列分為三段,順序放在p之後,依次為,第一段[元素小於p],第二段[元素大於等於p],第三段[尚未處理元素]。演算法開始時前兩段均為空。
從i = l+1開始,從左到右掃描子陣列a[l, r],將第三段的首個元素與p比較,若a[i]>=p,執行i+1,這就相當於將a[i]劃入了第二段,同時縮小了第三段;若a[i]<p,需要將s+1(s始終指向第一段的末位元素),同時交換a[i]與a[s],之後i+1。直到第三段為空,交換a[p]與a[s]。
下圖為Lomuto劃分示意圖:
熟悉快速排序的讀者估計看出來了,這就是快速排序中的一部分函式,只不過沒有接觸過Lomuto這種叫法而已。
當然,我們這裡使用的方法就是快速選擇(“快速”這一方法一開始並非用於排序,而是查詢),下面給出查詢第k小元素的程式碼:
public class Main {
static int[] a= {89, 45, 68, 90, 29, 34, 17};
static int k = 2;
public static void main(String[] args) {
System.out.println(fastsort(0, a.length-1, k));
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
private static int Lomuto(int l, int r) {
int p = a[l];
int s = l;
for (int i = l+1; i <= r; i++) {
if (a[i] < p) {
s = s+1;
int temp = a[s];
a[s] = a[i];
a[i] = temp;
}
}
int temp = a[l];
a[l] = a[s];
a[s] = temp;
return s;
}
private static int fastsort(int l, int r, int k) {
int s = Lomuto(l, r);
/**
* s在劃分之後變成了樞軸所在的位置下標,如果s=k,輸出a[s]
* 這裡要寫成l+k-1,如果劃分到右側,只寫k會出問題
* */
if (s == l + k - 1) {
return a[s];
}else if (s > l + k - 1){
return fastsort(l, s-1, k);
} else {
return fastsort(s+1, r, l+k-1-s);
}
}
}
不幸的是,這樣的演算法時間複雜度為O(n^2),比之前基於排序的方法實際上更糟糕,但是分析表明,這種方法的平均情況下效率是線性的。而且基於劃分的演算法不僅可以查詢第k小的元素,還可以給出列表中k個最小元素和n-k個最大元素。