快速选择 - 快速排序算法在查找中的应用
如果要我们找出一个数组中的最小(最大)的元素,那么第一反应肯定是使用最小(最大)堆。时间复杂度等同于建堆的复杂度,这里是O(N)。
如果要我们找出一个数组中的第k个最小的元素,那么我们依然可以使用最小堆,删除掉k次的最小值,就得到了结果。复杂度是O(N + klogN)。
如果要我们找出一个数组的中值,那么我们使用最小堆,复杂度是O(NlogN)。
但是,如果我们使用快速排序的思路来做快速选择呢?步骤是这样的:
(1)如果待排元素集合S的大小为1,k也为1,则返回。
(2)合理选取枢纽pivot。
(3)以pivot为参照,将集合分为S1和S2和pivot它自己。
(4)如果pivot的位置就是k,则返回。如果k小于pivot的位置,则递归处理S1。如果k大于pivot的位置,则递归处理S2。
我们分析上述快速选择的时间复杂度,与快速排序相比,快速选择只有步骤(4)与之有异。快速排序是递归S1和S2,而快速选择是递归其中之一,节省一次递归。最坏的复杂度的情况和快速排序相仿,是O(N^2)。最好的情况的复杂度是O(N)。平均的情况的复杂度,书上说是O(N),这点我暂时没去证明。
代码如下:
1 #include <cstdio> 2 #include <cstdlib> 3 4 #define MINLENGTH 10 5 6 typedef int Item; 7 // inlineʹSwapÄÚÁªÕ¹¿ª¡£Ìá¸ß³ÌÐòЧÂÊ¡£ 8 void inline 9 Swap(Item* a, Item* b) 10 { 11 if (a != b) { 12 *a ^= *b; 13 *b ^= *a; 14 *a ^= *b; 15 } 16 } 17 // ÔÚarrÖÐÈ¡×óÖÐÓÒÈý¸öλÖ㬱Ƚϡ£pivotÈ¡ÖмäÖµ£¬½«pivot·ÅÔÚright-1µÄλÖÃÉÏ¡£ 18 // leftµÄλÖÃСÓÚpivot£¬rightµÄλÖôóÓÚpivot¡£ 19 Item 20 Median3(Item arr[], int left, int right) 21 { 22 int center = (left + right) / 2; 23 24 if (arr[left] > arr[center]) { 25 Swap(&arr[left], &arr[center]); 26 } 27 if (arr[left] > arr[right]) { 28 Swap(&arr[left], &arr[right]); 29 } 30 if (arr[center] > arr[right]) { 31 Swap(&arr[center], &arr[right]); 32 } 33 34 Swap(&arr[center], &arr[right-1]); 35 return arr[right-1]; 36 } 37 38 void 39 InsertSort(int arr[], int len) 40 { 41 for (int i = 1; i < len; i++) { 42 int tmp = arr[i]; 43 int j; 44 for (j = i; j >= 1 && arr[j-1] > tmp; j--) { 45 arr[j] = arr[j-1]; 46 } 47 arr[j] = tmp; 48 } 49 } 50 // ÕâÀïÑ¡ÓÃi£¬jÓöµ½µÈÓÚpivotµÄÇé¿öÏÂÍ£Ö¹£¨ÒòΪÈç¹û²»Í£Ö¹£¬ÄÇ»á´ïµ½O(N^2)¡££© 51 // ÁíÍ⣬³ÌÐò¿¼ÂÇ´ýÅÅÊý×Ö²»ÉÙÓÚ3¸öµÄÇé¿ö¡£ÉÙÓÚ3¸öµÄ»°£¬ÐèÒª¿¼ÂǶîÍâµÄÌØÊâÇé¿ö¡£ 52 void 53 QSelect(Item arr[], int key, int left, int right) 54 { 55 if (right - left >= MINLENGTH) { 56 int pivot = Median3(arr, left, right); 57 int i = left; 58 int j = right - 1; 59 for ( ; ; ) { 60 while (arr[++i] < pivot) {} 61 while (arr[--j] > pivot) {} 62 if (i < j) { 63 Swap(&arr[i], &arr[j]); 64 } else { 65 break; 66 } 67 } 68 Swap(&arr[i], &arr[right-1]); // ½«pivot·ÅÔÚiµÄλÖÃÉÏ¡£ 69 70 if (key == i) { 71 return; 72 } 73 if (key < i) { 74 QSelect(arr, key, left, i - 1); 75 } else if (key > i) { 76 QSelect(arr, key, i + 1, right); 77 } 78 } 79 else { 80 InsertSort(arr + left, right - left + 1); 81 } 82 } 83 84 void 85 QuickSelect(Item arr[], int key, int len) 86 { 87 QSelect(arr, key, 0, len-1); 88 } 89 90 int 91 main(int argc, char** argv) 92 { 93 Item arr[100000] = {0}; 94 95 for (int i = 0; i < 100000; i++) { 96 arr[i] = 100000/(i+1); 97 } 98 99 int key = 50000; 100 QuickSelect(arr, key, 100000); 101 102 printf("%d\n", arr[key]); 103 104 105 system("pause"); 106 107 return 0; 108 }