一 、霍尔法
快速排序是由冒泡排序改进而得的。在冒泡排序过程中,只对相邻的两个数据进行比较,因此每次交换两个相邻数据时只能消除一个逆序排列。如果能通过两个(不相邻)数据的一次交换,消除多个逆序排列,则会大大加快排序的速度。快速排序方法中的一次交换可能消除多个逆序排列。
算法步骤:
在待排序的n个数据中任选一个数据(通常选第一个数据)作为基准数,设该基准数位key。n个数据通过与key比较,把比key小的数据交换到前面,把比key大的数据交换到后面,最终将待排序数据分成两个子表,把key放在分界处。然后,分别对左、右子表重复上述过程,直至每个子表只有一个数据时,排序完成。
下图是一趟排序的过程:
(1)选择待排序表中的一个数据作为基准值(通常为第一个数),将基准值暂存在a[0]的位置上。
(2)从数组的最右侧位置依次向左搜索,找到第一个小于基准值的数据,记住下标right的位置(若a[right]的值大于基准值时,则执行right--)。
(3)然后从数组的左侧位置,依次向右搜索找到第一个大于基准值的数据及其下标(若a[left]小于基准值时,则执行left++)。
(4)重复步骤(2)和步骤(3),直至left==right。此时left或right的位置为基准值的最终位置,交换a[left]和基准值a[key],并且使key==left。
具体代码如下:
#include<stdio.h>
void Swap(int* a, int* b) {//交换两个数据的值
int c = *a;
*a = *b;
*b = c;
}
// 快速排序hoare版本
void PartSort1(int* a, int left, int right) {
if (left >= right) {//循环停止的条件
return;
}
int begin = left, end = right;
int randi = rand() % (right - left + 1);//产生一个在区间[left,right]中的随机数
randi += left;
Swap(&a[left], &a[randi]);
int keyi = left;//用区间[left,right]中的随机一个数作为基准值
while (left < right) {
while (left < right&&a[right] >= a[keyi]) {//left < right--》防止向左寻值时,找不到
right--; //超出区间范围
}
while (left < right&&a[left] <= a[keyi]) {
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
keyi = left;
PartSort1(a, begin, keyi - 1);//[begin,keyi-1]
PartSort1(a, keyi + 1, end);//[keyi+1,end]
}
int main()
{
int a[] = { 2,4,6,5,1,3 };
PartSort1(a, 0, sizeof(a) / sizeof(int) - 1);
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
return 0;
}
二、前后指针法
前后指针法的主要思想:
通过创建两个指针(prev和cur),prev指针指向数组的第一个数据,cur指向数组的第二个数据,让cur遍历数组去寻找比基准值a[key]小的数,若找到了,判断prev的下一个位置是否为cur,若prev的下一个位置不是cur,则++prev,交换a[prev]和a[cur]的值,然后++cur,再次遍历数组去寻找比基准值a[key]小的数;若prev的下一个位置是cur,则++prev,++cur(其本质上是先++prev,prev与cur指向同一个数,交换a[prev]和a[cur]的值就是自身与自身交换,然后++cur)。直到cur>right时,交换基准值a[key]和a[prev],使key=prev。
下图是前后指针排序中的一个过程:
key将整个区间分成[left,key-1],[key+1,right]左右两个区间,然后再将这两个子区间进行上述的操作,直至区间只有一个值,这样序列就排好序了。
代码如下:
#include<stdio.h>
void Swap(int* a, int* b) {//交换两个数的值
int c = *a;
*a = *b;
*b = c;
}
// 快速排序前后指针法
void PartSort2(int* a, int left, int right) {
if (left >= right) {//循环停止条件
return;
}
int prev = left;
int cur = left + 1;
int keyi = left;
while (cur <= right) {
if (a[cur] < a[keyi] && ++prev != cur) {
Swap(&a[prev], &a[cur]);
}
cur++;
}
Swap(&a[prev], &a[keyi]);
keyi = prev;//keyi将整个区间分成左右两个子区间
PartSort2(a, left, keyi - 1);//区间[left,keyi-1]
PartSort2(a, keyi + 1, right);//区间[keyi+1,right]
}
int main()
{
int a[] = { 2,4,6,5,1,3 };
PartSort2(a, 0, sizeof(a) / sizeof(int) - 1);
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
return 0;
}
代码运行结果: