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

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

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

二分查找与快速排序算法

1 /**********二分查找*****************/ 2 int half_find(int *num,int size, int a) 3 { 4 int i=0; 5 int low=0; 6 int high=size-1; 7 int mid;//记录中间位置 8 while(low<=high) 9 { 10 mid = (low+high)/2; 11 if(num[mid] == a) 12 return mid;//返回所在位置 13 if(num[mid] >

Java中常用的查找算法——顺序查找和二分查找

Java中常用的查找算法——顺序查找和二分查找 一.顺序查找: a) 原理:顺序查找就是按顺序从头到尾依次往下查找,找到数据,则提前结束查找,找不到便一直查找下去,直到数据最后一位. b) 图例说明: 原始数据:int[] a={4,6,2,8,1,9,0,3}; 要查找数字:8 代码演示: import java.util.Scanner; /* * 顺序查找 */ public class SequelSearch { public static void main(String[] arg

详解Redis源码中的部分快速排序算法(pqsort.c)

看标题,你可能会疑惑:咦?你这家伙,怎么不讲解完整的快排,只讲一部分快排---.- 哎,冤枉."部分快排"是算法的名字,实际上本文相当详细呢.本文几乎与普通快排无异.看懂了本文,你对普通的快排也会有更深的认识了. 快速排序算法(qsort)的原理我们大都应该了解.本文介绍的是部分快速排序算法.其实其算法本质是一样的,只不过限定了排序的左右区间,也就是只对一个数字序列的一部分进行排序,故称为"部分快速排序算法",简称:pqsort Redis项目中的pqsort.c

面试常见算法-排序查找算法

算法是程序员必被的一个技能,在面试中常常出现,下面总结了面试中出现的常见算法,这些算法程序员应该牢记在心中,要非常熟练. 插入排序算法 原理:将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序. 要点:设立哨兵,作为临时存储和判断数组边界之用. public class InsertSort { private static void insertSort(int[] a) { int j; int tmp; for

排序算法和查找算法

示例:分别用冒泡排序,快速排序,选择排序,插入排序将数组中的值从小到大的顺序排序 $array = (9,5,1,3,6,4,8,7,2); 1.冒泡排序算法 //思路:两两比较待排序数据元素的大小,发现两个数据元素的次序相反即进行交换,直到没有反序的数据元素为止 function bubbleSort($array){         $lg = count($array);         if($lg <=1){             return $array;         }  

快速排序算法学习总结

排序算法有很多种,如冒泡排序.堆排序.快速排序等: 冒泡排序是大学的初学算法,也被很多人所熟知,这里不详细讨论,实现方式如下: 快速排序使用分治法,将一个list分为两个子list: 算法步骤:1 从数列中挑出一个元素,作为基准,2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边).在这个分区退出之后,该基准就处于数列的中间位置.这个称为分区操作.3 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序.递归的最底部情形,是数列的

快速选择排序算法

快速排序是对冒泡法排序的一种改进. 1  排序思想: 通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小:再分别对这两部分记录进行下一趟分割排序,以达到整个序列有序,重复执行以上的划分操作,直 到所有要进行排序的数据变为有序为止. 可能仅根据基本思想对快速排序的认识并不深,接下来以对n个无序数列A[0], A[1]-, A[n-1]采用快速排序方法进行升序排列为例进行讲解. (1)定义两个变量low和high,将low.high分别设置为要进行排序的序

关于快速排序算法(一个90%的人都不懂其原理、99.9%的人都不能正常写出来的算法.)

一.奇怪的现象 研究快速排序很久了,发现一个古怪的实情:这算法描述起来很简单,写一个正确的出来实在不容易.写一个优秀的快速排序算法更是难上加难. 也难怪该算法提出来过了很久才有人写出一个正确的算法,过了很久才优秀的版本出来. 二.原理描述 从数列中挑出一个元素,称为 "基准"(pivot), 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边).在这个分区退出之后,该基准就处于数列的中间位置.这个称为分区(partition)操作

排序算法--快速排序算法解析

一.快速排序算法思路 ①.取待排序列表的第一个元素p,通过归位算法,挪移他到列表中正确的位置. ②.列表被元素p分成两部分,左边都比元素p小,右边都比元素p大. ③.通过递归,在两部分,重复1.2步骤,直至列表有序. 归位算法动画演示: 二.快速排序算法代码示例 1 def partition(l,left,right):#归位算法 2 temp = l[left] #保存归位元素 3 while left <right: 4 while l[right]>=temp and left<