对于要排序的区间,先定一个基准数p(基准数可随机指定,可指定区间两端,或者区间中心的数为基准数)。然后将区间中小于p的数放到p之前,大于p的数放在p之后,最后使用分治策略,分别对p之前的子区间和p之后的子区间递归并排序。
该算法平均时间复杂度O(N×logN)O(N\times log N)O(N×logN),最坏O(N2)O(N^2)O(N2)。
有多种方法可以实现上述以p为界的区间划分,下面以升序排序为例介绍一种较为常用的方法:
新建两个指针,分别指向区间的左右两端。并取区间左端的值为基准数p(此时视为将p抽离原区间,区间最左端为空)。
右指针左移,直到找到比p小的数,然后将该数填充至左指针指向位置(也就是区间最左端空置的位置)。
左指针右移,直到找到比p大的数,然后将该数填充至右指针指向位置(原来右指针的指向的值在步骤2中被移动到区间最左端,此时其指向的位置也可以视为空置)。
重复步骤2、3,直到两指针相交或越界,此时将p放置于左指针所指位置。
遍历完整个区间后,对p这单个元素的排序已完成,p此时被放置到未来数组有序时p应该处于的位置。在p前面的数都比它小,在p后面的数都比它大,此时开始分治递归p之前的区间和p之后的区间,重复1至5步。
void QuickSort(int* a, int lef, int rig){if (lef >= rig)return;int p = a[lef], i = lef, j = rig;while (i < j) { while (i=p)j--;a[i] = a[j]; while (i
上述方法可较快地实现以p为界的区间划分,但无法保证排序的稳定性。下面介绍一种稳定化的快速排序方法:
对于给定的区间,确定基准数p后,新建两个数组a1和a2,其中a1用于存储区间中所有比p小的数,a2存储所有比p大的数,对于原区间中与p相等的数,若其位置在p之前,则按位置顺序先后存入a1,若位置在p之后,则按位置顺序先后存入a2。然后按a1, p, a2的顺序将三者覆盖回原来的区间,之后同样用分治策略递归排序a1部分和a2部分。
void QuickSort(int* a, int lef, int rig){if (lef >= rig) return;int n = rig-lef+1;int a1[n], a2[n], n1 = 0, n2 = 0;int mid = lef+(rig-lef)/2;int p = a[mid];for (int i=lef; i<=rig; i++) if (i != mid) {if (a[i]p) a2[n2++] = a[i];else {if (i
一种用于从无序数组中找第k大(小)元素的算法。由于快速排序中,对区间扫描后,基准数会被放置到正确的位置,其前面的数都比它小,后面的数都比它大,基于该性质,Quick Select的算法步骤如下:(假设要找第k小的元素)
和快速排序一样,在给定区间中确定基准数p,并以同样的方式扫描区间并移动元素,令p前面的数都比它小,在p后面的数都比它大。
假设此时p前面有n1个元素,p之后有n2个元素,如果n1=k-1,说明基准数即为目标;如果n1>k-1,则说明目标元素在p之前,因此重复1、2两步,在前n1个元素中递归寻找第k小元素;如果n1
该算法平均时间复杂度O(N)O(N)O(N),最坏O(N×logN)O(N\times log N)O(N×logN)。
int QuickSelect(int* a, int lef, int rig, int k){int p = a[lef], i = lef, j = rig;while (i < j) { while (i=p)j--;a[i] = a[j]; while (i k-1)return QuickSelect(a, lef, i-1, k);return QuickSelect(a, i+1, rig, k-n1-1);
}
稳定版快速排序算法
Stable QuickSort
【算法】Quick Select