快速排序由C. A. R. Hoare在1962年提出。排序效率在同为O(N*logN)的排序算法中算是很高的。
它的基本思想是:
1.先从序列中选取一个数作为基准数
2.所有比基准数小的,放在基准数的左边,所有比基准数大的放在基准数的右边。
3.将基准数左右两边的序列,分别执行上述两个步骤,直至数列有序。
它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
假设有这样一个序列:
array[8] = {49,38,65,97,76,13,27,49};
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
49 | 38 | 65 | 97 | 76 | 13 | 27 | 49 |
假设我为选取第left = 0个元素 49作为我们第一次分组的基准数 pivot = 49,此处0位置处有一个”坑”。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
38 | 65 | 97 | 76 | 13 | 27 | 49 |
当左边出现”坑”时,从右边的下标位置,即位置right = 7起开始”填坑”,填坑的原则是 “小而赋值(填坑),大而移动”,即,,比基准值大的,向左移动,比基准值小的,放到坑里去。
经过分析下标为right = 7的数据为49与基准值pivot相等,继续左移right –,下标right = 6的数据为27,比基准值小,要完成赋值 。此时right = 6的位置是一个坑。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
27 | 38 | 65 | 97 | 76 | 13 | 49 |
当右边出现”坑”时,从左边的下标位置,即位置left = 0起开始”填坑”,填坑的原则是,”大而赋值(填坑),小而移动”,即,比基准值小的,向右移动,比基准值大的,放到坑里面去。
经过分析下标为left = 0的数据为27与基准值pivot小,右移一位left++,下标为left = 1的数据为38,比基准值小,继续右移left++,下标为left = 2的数据为65比基准值要大,要完成赋值 。此时left = 2的位置是一个坑。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
27 | 38 | 97 | 76 | 13 | 65 | 49 |
当左边出现”坑”时,从右边的下标位置,即位置right = 6(即上一次被填坑的位置)起开始”填坑”,原则同上,right = 6的值为65比基准值要大,左移right–,right=5的值为13比基准值要小,完成赋值。此时right = 5处的位置是一个坑。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
27 | 38 | 13 | 97 | 76 | 65 | 49 |
当右边出现”坑”时,从左边的下标位置,即位置left = 2(即上一次被填坑的位置)起开始”填坑”,原则同上,right = 2的值为13比基准值要小,右移left++,left=3的值为97比基准值要大,完成赋值。此时left = 3处的位置是一个坑。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
27 | 38 | 13 | 76 | 97 | 65 | 49 |
当左边出现”坑”时,从右边的下标位置,即位置right = 5(即上一次被填坑的位置)起开始”填坑”,原则同上,right = 6的值为65比基准值要大,左移right–,right=4的值为76比基准值要大,继续左移right–,此时left = right = 3,将基准值入到left = right相等处的坑中。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
27 | 38 | 13 | 49 | 76 | 97 | 65 | 49 |
此时即完成了,第一轮分组。 左侧值 小于 pivot; 右侧值 大于等于 pivot
依据上述规则,分别对pivot左右两侧的序列执行上述操作,直至有序。
此时我们要完成代码的编写,再次对原则进行归纳:
1.先从序列中选取一个数作为基准数
2.所有比基准数小的,放在基准数的左边,所有比基准数大的放在基准数的右边。
3.将基准数左右两边的序列,分别执行上述两个步骤,直至数列有序。
为 :
1.左右填坑,直到找到pivot所在的坑,并完成赋值。
2.依据pivot所在的位置进行分组,重复上述步骤。
完成步骤1的代码如下:
int findPivotPostion(int *arr, int left, int right)
{
int pivot = arr[left];
while(left < right)
{
while(left<right && arr[right] >= pivot) // 大而(>=)移动,
right--;
arr[left] = arr[right]; //小而赋值
while(left<right && arr[left] <= pivot) //小而(<=)移动
left++;
arr[right] = arr[left]; //大而赋值
}
arr[left] = pivot; // 为基准值找到位置
return left;
}
完成步骤2的代码如下:
void quickSort(int *arr, int left ,int right)
{
if(left < right)
{
int pos = findPivotPostion(arr,left, right);
quickSort(arr,left,pos-1);
quickSort(arr,pos+1,right);
}
}
为了更简名之,合并之:
void quickSort(int *arr, int left ,int right)
{
if(left < right)
{
int pivot = arr[left];
int low = left; int high = right;
while(left < right)
{
while(low<high && arr[hig] >= pivot) // 大而(>=)移动,
high--;
arr[low] = arr[high]; //小而赋值
while(low<high && arr[low] <= pivot) //小而(<=)移动
low++;
arr[high] = arr[low]; //大而赋值
}
arr[low] = pivot; // 为基准值找到位置
quickSort(arr,left,low-1);
quickSort(arr,low+1,right);
}
}
测试代码如下:
int main()
{
int array[8] = {49,38,65,97,76,13,27,49};
for(int i=0; i<8; i++)
printf("%5d",array[i]);
putchar(10);
quickSort(array,0,7);
for(int i=0; i<8; i++)
printf("%5d",array[i]);
return 0;
}