在一個無序序列中找出第k個元素,對於k很小或者很大時能夠採起特殊的方法,好比用堆排序來實現 。可是對於與序列長度N成正比的k來講,就不是一件容易的事了,可能最容易想到的就是先將無序序列排序再遍歷便可找出第k個元素。因爲任何基於比較的排序算法不可能用少於Θ(N lgN)次比較來實現將全部元素排序,因此採用排序的方法的時間複雜度是線性對數級別的。算法
咱們能夠借鑑快速排序中將序列劃分的思想來實現平均狀況下線性級別的算法,算法實現以下:數組
1 public class KthElement { 2 3 private static void exch(Comparable[] a, int i, int j) 4 { 5 Comparable swap = a[i]; 6 a[i] = a[j]; 7 a[j] = swap; 8 } 9 10 private static boolean less(Comparable a, Comparable b) 11 { 12 return a.compareTo(b) < 0; 13 } 14 15 private static int partition(Comparable[] a, int lo, int hi) 16 { 17 int i = lo; 18 int j = hi + 1; 19 Comparable v = a[lo]; 20 while(true) 21 { 22 while(less(a[++i], v)) if(i == hi) break; 23 while(less(v, a[--j])); 24 if(i >= j) break; 25 exch(a, i, j); 26 } 27 exch(a, lo, j); 28 return j; 29 } 30 31 public static Comparable select(Comparable[] a, int k) 32 { 33 int lo = 0; 34 int hi = a.length - 1; 35 while(hi > lo) 36 { 37 int j = partition(a, lo, hi); 38 if(j == k) break; 39 else if(j > k) hi = j - 1; 40 else if(j < k) lo = j + 1; 41 } 42 return a[k]; 43 } 44 public static void main(String[] args) { 45 Integer[] ints = {5, 3, 1, 4, 2}; 46 int find = (int) select(ints, 2); 47 System.out.println(find); 48 } 49 50 }
在select方法中,使用partition方法將序列劃分。若是k = j,問題就已經解決了; 若是k < j,就繼續切分左字數組(令 hi = j - 1);若是k > j,就繼續切分右子數組(令lo = j + 1)。該循環保證了lo左邊的元素都小於等於a[lo...hi], 而hi右邊的元素都大於等於a[lo...hi],咱們不斷切分直到數組中只剩下第k個元素。爲什麼這個的時間複雜度是線性級別的,證實很複雜,在此給出理想狀況下的簡易證實。假設每次切分都從中間切分,則全部的比較次數爲(N + N/2 + N/4 + N/8 +...)直到找到k,很顯然這個和小於2N。平均狀況下的複雜度爲Θ( 2N + 2Kln(N/K) + 2(N - K)ln(N/(N-K)) )。當K = N/2時,複雜度爲Θ((2 + 2ln2)N)。less