1、算法思想
选定一个元素作为支点(pivot),以支点为基准将整个序列划分为两个子序列r1 … ri-1和ri+1 … rn,前一个子序列中记录的值均小于或等于支点,后一个子序列中元素的值均大于或等于支点,这个过程称作一趟快速排序(如下图所示)。然后只要分别对这两部分继续进行递归地排序,就可以使整个序列有序。
一趟快速排序的具体做法:
1)、设置两个指针low和high,刚开始分别指向序列的第一个、最后一个的元素。初始化支点(pivot)也为序列的第一个元素。(这是为了方便编程。这里有种错觉:好像pivot只能为第一个元素。其实是可以取序列中的任意一个元素的,只要将选定的元素和第一个元素交换一下就可以了。)
2)、首先,从high所指定位置起向前搜索找到第一个小于pivot的元素和low所指向的元素(即pivot)互相交换,然后从low所指位置起向后搜索,找到第一个大于pivot的元素和high所指向的元素(即pivot)交换。
3)、重复2)中的两步,直到low=high
2、基本算法实现
int partition(int data[], int low, int high) //返回pivot所在的位置
{
int pivot = data[low];
while (low < high)
{
while (low < high && data[high] >= pivot) high--;
swap(data[low], data[high]); //high指向的值比支点值小,换到前面去。和low指向的值交换也就是和pivot交换
while (low < high && data[low] <= pivot) low++;
swap(data[low], data[high]); //low指向的值比支点值大,换到后面去。此时,high指向的值等于pivot
//和high交换也就是和pivot交换
}
return low; //此时low == high。可以发现:pivot其实就是不停地在被“丢来丢去”,不是在high处就是在low处。
//所以这里返回high也可以。这也解释了为什么low == high时,算法就停止了,因为low和high指向的value已经是pivot了。
}
void QSort(int data[], int low, int high) //完整快速排序
{
if (low < high)
{
int pivotloc = partition(data, low, high);
QSort(data, low, pivotloc - 1); //递归
QSort(data, pivotloc + 1, high); //递归
}
}
int main(int argc, char *argv[])
{
int data[20] = { 1, 7, 3, 50, 43, 34, 78, 23, 67, 90 };
QSort(data, 0, 9); //调用快排
int i = 0;
while (i < 10) //输出
{
cout << data[i] << ' ';
++i;
}
system("pause");
return 0;
}
3、改进快排算法
上面算法中,每交换一对记录需要进行3次记录移动操作(即swap函数)。从代码和注释中可以看出,其实pivot一直在被“丢来丢去”,所以没有必要一直移动pivot,也就是可以将pivot暂存起来,然后将pivot赋值到最终的位置(一共就修改了4行代码:第5、9、12、14行)。修改版代码如下://微改进快排
int partition(int data[], int low, int high) //返回pivot所在的位置
{
int pivot = data[low];
data[0] = pivot; //暂存
while (low < high)
{
while (low < high && data[high] >= pivot) high--;
data[low] = data[high]; //第一次执行时,覆盖了pivot,因为pivot已经暂存到data[0]处
while (low < high && data[low] <= pivot) low++;
data[high] = data[low]; //由于上一步的执行,data[high]已经复制到了low处
}
data[low] = data[0];
return low;
}
void QSort(int data[], int low, int high) //完整快速排序
{
if (low < high)
{
int pivotloc = partition(data, low, high);
QSort(data, low, pivotloc - 1); //递归
QSort(data, pivotloc + 1, high); //递归
}
}
int main(int argc, char *argv[])
{
int data[20] = { 0, 1, 7, 3, 50, 43, 34, 78, 23, 67, 90 }; //第0个位置用于暂存pivot
QSort(data, 1, 10); //调用快排,对第1—10个位置上的值进行排序
int i = 1;
while (i <= 10) //输出
{
cout << data[i] << ' ';
++i;
}
system("pause");
return 0;
}
下面给出一个一趟快排的例子,也就是partition函数的执行过程。其中i表示low,j表示high,最右边的49表示暂存的pivot,用于进行比较:
参考文献:
《数据结构》,严蔚敏
《算法设计与分析》,王红梅