一、概念思路
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
根据二叉树前序遍历的方式(递归),写出如下代码
void QuickSort(int* a, int begin, int end)
{
//结束条件:1.区间只有一个值 2.区间不存在
if (begin >= end)
return;
int key = PartSort(a, begin, end);
//[begin,key-1] key [key+1,end]
QuickSort(a,begin, key - 1);
QuickSort(a, key + 1,end);
}
将区间按照基准值划分为左右两半部分(单趟)的常见方式有三种,也就是上述代码PartSort函数的实现方法有三种
二、实现方法
2.1 hoare版本
1.先确定一个keyi,具体确定为哪个,没有规定
2.right向左找比key小的
3.left向右找比key大的
4.找到后交换值
5.重复3,4直到left与right相遇,交换相遇点值与key值。
最后就能实现,keyi左边的比key小,keyi右边的比key大,但不一定有序。
int PartSort1(int* a, int left, int right)
{
int keyi = left;
while (left < right)
{
// 右边找小
while (a[right] > a[keyi])
{
--right;
}
// 左边找大
while (a[left] < a[keyi])
{
++left;
}
Swap(&a[left], &a[right]);
}
Swap(&a[keyi], &a[left]);
return left;
}
但是这样的写法是有问题的,若待排数据中有多个与key相等的值,就会死循环。
所以这样改进:
int PartSort1(int* a, int left, int right)
{
int keyi = left;
while (left < right)
{
// 右边找小
while (a[right] >= a[keyi])
{
--right;
}
// 左边找大
while (a[left] <= a[keyi])
{
++left;
}
Swap(&a[left], &a[right]);
}
Swap(&a[keyi], &a[left]);
return left;
}
但这样又会带来问题,若key值本来就是最小的,right第一次向左找就会越界。
所以这样改进:
int PartSort1(int* a, int left, int right)
{
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]);
return left;
}
2.2 挖坑法
hoare版本是开辟先河的经典版本,但是里面藏着的问题太多,一不留神就可能写错,所以有人发明了挖坑法,这个方法相对好理解一些,也不容易写错。
1.先确定一个keyi,具体确定为哪个,没有规定。并把key值存起来,
2.在keyi位置确定一个坑。
3.right向左找比key小的,找到后把right值给坑位,right处成为坑位
4.left向右找比key大的,找到后把left值给坑位,left处成为坑位
5.重复3,4直到left与right相遇
6.最后把key值给坑位
最后也能实现,keyi左边的比key小,keyi右边的比key大,但不一定有序。
int PartSort2(int* a, int left, int right)
{
int key = a[left];
int hole = left;
while (left < right)
{
// 右边找小
while (left < right && a[right] >= key)
{
--right;
}
a[hole] = a[right];
hole = right;
// 左边找大
while (left < right && a[left] <= key)
{
++left;
}
a[hole] = a[left];
hole = left;
}
a[hole] = key;
return hole;
}
2.3 前后指针法
1.初始时,prev指针指向序列开头,cur指针指向prev指针的后一个位置
2.然后判断cur指针指向的数据是否小于key,
若小于,cur++,prev++
若不小于,prev++,cur指向的值 与 prev指向的值交换,然后cur++
3.重复2,直到cur越界,此时把key值与prev值交换
最后也能实现,keyi左边的比key小,keyi右边的比key大,但不一定有序。
int PartSort3(int* a, int left, int right)
{
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;
return keyi;
}