快速排序(QuickSort)是一种很高效的排序算法,但实际操作过程中不容易写出十分正确具有健壮性的代码,所以我想讲清这个问题,也能使自己理解更加深刻
算法思想
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法的代码我参考了阿哈磊的代码:http://bbs.ahalei.com/thread-4419-1-1.html
讲的很透彻
给定一个序列5, 2, 8, 4, 22, 31, 0, 1, 9九个数字,来对九个数字排序
第一步:找一个基准数(用来参考的数字),为了方便,就把第一个数字也就是5看成基准数
第二步:一个小人从右往左找一个比基准数大的数字,然后另一个小人从左往右找一个比基准数小的数字(注意要先从右边找,至于为什么,后面再说)
第二步完成后右边找到一个数字是1,左边找到一个数字是8,然后我们交换这两个数字
序列就变成了5, 2, 1, 4, 22, 31, 0, 8, 9然后继续找 5, 2, 1, 4, 0, 31, 22, 8, 9
当两个小人碰头,在0这个位置停下了,也就是两边查询的数组下标相等时,结束查找,我们发现0左边的数都是小于5的,右边的数都是大于5的,然后我们把碰头位置数字与基准数交换,分成两个序列
左序列0,2,1,4,5
右序列31,22,8,9
然后继续上面相同的操作(这里是一个递归过程)
递归到最后,就能得到一个有序的序列
排序前序列:5 2 8 4 22 31 0 1 9
5 2 1 4 0 31 22 8 9
改变基准数:0 2 1 4 5 31 22 8 9
0 2 1 4 5 31 22 8 9
改变基准数:0 2 1 4 5 31 22 8 9
0 2 1 4 5 31 22 8 9
改变基准数:0 1 2 4 5 31 22 8 9
0 1 2 4 5 31 22 8 9
改变基准数:0 1 2 4 5 31 22 8 9
0 1 2 4 5 31 22 8 9
改变基准数:0 1 2 4 5 31 22 8 9
0 1 2 4 5 31 22 8 9
改变基准数:0 1 2 4 5 9 22 8 31
0 1 2 4 5 9 8 22 31
改变基准数:0 1 2 4 5 8 9 22 31
0 1 2 4 5 8 9 22 31
改变基准数:0 1 2 4 5 8 9 22 31
0 1 2 4 5 8 9 22 31
改变基准数:0 1 2 4 5 8 9 22 31
排序后序列:0 1 2 4 5 8 9 22 31
/* 快速排序算法: */
#include<cstdio>
#include<iostream>
using namespace std;
int a[10] = {5, 2, 8, 4, 22, 31, 0, 1, 9};
void print() {
for(int i = 0; i < 9; i++)
cout << a[i] << " ";
cout << "\n";
}
void QuickSort(int left, int right) {
int temp = a[left], i, j;
i = left;
j = right;
if(i > j) return ;
while(i != j) {//循环到两个小人碰到为止
while(a[j] >= temp && i < j) j--;//右边开始找,找到比基准数小的数
while(a[i] <= temp && i < j) i++;
if(i < j) swap(a[i], a[j]);//交换两个数
}
swap(a[left], a[i]);//改变基准数
print();
QuickSort(left, i-1); //递归过程
QuickSort(i+1, right);
}
int main() {
print();
QuickSort(0, 8);
print();
}
为什么一定要先从右开始找?
不妨想想如果从左边找
while(a[i] <= temp && i < j) i++;
while(a[j] >= temp && i < j) j–;
考虑一种情况,当i停下来的时候,找到一个大于基准数的数
接着j开始向左走,企图寻找一个小于基准的数,但是存在i < j这个条件的限制,j有可能没找到小于基准的数时就被迫停下来了,此时i==j的位置与基准数交换位置,结果就会出现错误