選擇算法:在一個未排序的的序列中,選取第K小的元素。算法
static int random_select(int begin, int end, int k) { assert(begin < end); assert(k >= 0); assert(k < end-begin); int rand = randint(begin, end); int left, right; partition(begin, end, rand, &left, &right); int idx = k+begin; if (idx < left) return random_select(begin, left, k); else if (idx >=left && idx<=right) return idx; else return random_select(right+1, end, idx-right-1); } static void partition(int begin, int end, int pivot_index, int *ret_left, int *ret_right) { #if 0 printf("***********in partition*****************\n"); print_array(begin, end); printf("pivot_index = %d\n", pivot_index); #endif int pivot = A[pivot_index]; swap(begin, pivot_index); int left = begin; int right = begin; int i; for (i=begin+1; i<end; i++) { if (A[i] > pivot) ; else if (A[i] == pivot) { swap(right+1, i); right += 1; } else { swap(left, i); left += 1; swap(right+1, i); right += 1; } } *ret_left = left; *ret_right = right; #if 0 print_array(begin, end); printf("left = %d, right = %d \n", left, right); printf("***********end partition*****************\n"); #endif }
用pseudo code寫完,我會用了不到5分鐘,而實際轉換成代碼調試,卻花費了我半個多小時。如下是幾點緣由。數組
1. 日常寫pseudo code時,是和人的正常思惟一直,數組開始於1,最小的第k個元素就是最小的第k個元素。而實際寫代碼是,數組時從0開始的。這一點,很容易混淆。個人感受是,定義一套本身的符號和規則,使得pseudo code和程序代碼的約定一致。好比,排名能夠從0開始,第0小,第1小,..,第k小。好比,集合統一採起半開半閉的表示方法,全部有關數組的調用傳入的參數(begin,end)都是半開半閉的.A[begin:end)dom
2. 用divide-and-conque方法解決問題時,邊界條件很容易搞錯。我認爲,在寫邊界條件時,最好用簡單示例看看對不對。不然,等程序跑起來,崩潰了,就悲劇了。ide
3. 函數的定義要明確,其表示的含義必定要明確。傳入的參數的意義,返回的值的意義。我此次有一個bug就是由於random_select函數一開始定義返回的值的含義沒有明確。函數
磨刀不誤砍柴工,定義明確了再動手。spa