目录
1.1 确定关键值key(作为基准值)的正确位置
目的:确保排序后数组中比key值小的数字在key之前,比key值大的数字在key之后。
如下图所示:
方法1:hoare
right先向前移动,找到比key小的位置;left再向后移动,找到比key大的位置;交换right和left指向的元素
重复上述步骤,直至left和right相遇,将此时指向的值与key交换。
int PartSort1(int* a, int left, int right) {
//用keyi表示key的下标值
int keyi = left;
while (left < right) {
//先从右开始找小
while (left < right && a[right] > a[keyi]) {
right--;
}
//再从左开始找大
while (left<right && a[left]<=a[keyi]) {
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[keyi], &a[left]);
//更新keyi
keyi = left;
return keyi;
}
运行测试:
可以看出比3小的数字均在3之前,比3大的数字均在3之后。因此3的位置正确!
方法2:挖坑法
先将key值(即 8)保存到临时变量key中,此时形成一个坑位
先将right向前移动,找到比key小的数值 ,将该数值存入坑位中,right指向的位置此时形成新的坑位。
再将left向后移动,找到比key大的数值 ,将该数值存入坑位中,left指向的位置此时形成新的坑位。
重复上述步骤,直到left与right相遇,将key值存放到此时的坑位中。
int PartSort2(int* a, int left, int right) {
//使用临时变量存放key值
int key = a[left];
//使用hole下标表示坑位位置
int hole = left;
while (left < right) {
//先从右开始找小
while (left<right && a[right]>key) {
right--;
}
a[hole] = a[right];
//更新hole
hole = right;
//再从左开始找大
while (left < right && a[left] < key) {
left++;
}
a[hole] = a[left];
hole = left;
}
a[hole] = key;
return hole;
}
运行测试:
成功!
方法三:双指针法
定义两个指针prev和cur:
初始状态,prev指向key值(即数组的第一个元素),cur指向数组的第二个元素;
当cur指向的值比key小,prev++,cur指向的值和prev指向的值进行交换,cur++;
当cur指向的值比key大,cur++
当cur指向数组的最后一个元素时,将prev指向的数值与key值交换
int PartSort3(int* a, int left, int right) {
//定义两个指针prev,cur
int prev = left;
int cur = left + 1;
int keyi = left;
//进行循环
while (cur <= right) {
while(a[cur] < a[keyi]) {
prev++;
Swap(&a[prev], &a[cur]);
cur++;
}
/*
也可以写成
if(a[cur] < a[keyi]) {
prev++;
Swap(&a[prev], &a[cur]);
}
*/
/*
优化后(防止自己与自己进行比较)
if(a[cur] < a[keyi]&&++prev!=cur) {
Swap(&a[prev], &a[cur]);
}
*/
cur++;
}
Swap(&a[prev], &a[keyi]);
//更新keyi
keyi=prev;
return keyi;
}
运行测试:
成功!
1.2 对key值的选择进行优化
方法1:三数取中
目的:在数组有序或接近有序的情况下,选择中间的数字可以使快排的效率更高
选择数组首元素、尾元素和中间位置的元素中,数值处于中间的元素。
两两进行比较,代码如下:
//得到中间数值的元素下标
int GetMidNumi(int* a, int left, int right) {
//找到中间元素的下标
int mid = (left + right) / 2;
if (a[left] < a[mid]) {
if (a[mid] < a[right]) {
return mid;
}
else if (a[left] < a[right]) {
return right;
}
else
return left;
}
else {
if (a[mid] > a[right]) {
return mid;
}
else if (a[left] < a[right]) {
return left;
}
else
return right;
}
}
方法2:随机选key
目的:防止数组元素有序或接近有序时,key值为最小值(数组的首元素),快速排序效率降低
int RandomNumi(int* a, int left, int right) {
int randi = left + rand() % (right - left);
return randi;
}
1.3 进行递归
选取每一个区间的key值,使其排序到正确的位置,直到区间中只包含一个元素或者区间不存在。
如下图所示,keyi将区间划分为两部分---[begin,keyi-1] keyi [keyi+1,end]
先递归[begin,keyi-1],再递归[keyi+1,end]
图中红色方框标记出的条件为终止条件,
我们可以将终止条件总结为 begin>=keyi-1 || keyi+1>=end
即区间左下标大于区间右下标
void QuickSort(int* a, int left, int right) {
if (left >= right) {
return;
}
int keyi = PartSort1(a,left,right);
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1,right);
}
运行测试:
int main() {
int b[] = { 8,6,7,11,5,3,9,10 };
QuickSort(b, 0, 7);
PrintArray(b, 8);
return 0;
}