选择算法:在一个未排序的的序列中,选取第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