快速选择 - 快速排序算法在查找中的应用

如果要我们找出一个数组中的最小(最大)的元素,那么第一反应肯定是使用最小(最大)堆。时间复杂度等同于建堆的复杂度,这里是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 }

 

转载于:https://www.cnblogs.com/nipan/p/4065026.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值