快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
算法步骤:
1 从数列中挑出一个元素,称为 “基准”(pivot)
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
实现代码:包含的一次改进为随机选取的标杆数
/***************************************************************************
* @file main.cpp
* @author MISAYAONE
* @date 25 March 2017
* @remark 25 March 2017
* @theme Fast Sort
***************************************************************************/
#include <iostream>
#include <vector>
using namespace std;
//交换操作
void Swap(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
//标准的快排方法
int partion(int a[],int left,int right)//首先进行分区操作
{
int flag = a[left];//将首元素当作标杆,选择最右边元素最为标杆时代码会有略微不同
int j = left;
for (int i = left+1; i <= right; ++i)//首先忽略首元素
{
if (a[i] < flag)
{
j++;
if ( j != i)
{
Swap(a[i],a[j]);//将标杆数一直往右移动,遇到比标杆数小的就交换到从头开始的位置上
}
}
}
Swap(a[j],a[left]);//j位置的元素和首元素互换,即可保证标杆元素左边为小于等于,右边为大于等于
return j;
}
//取区间内随机数的函数
int Rand(int low, int high)
{
int size = high - low + 1;
return low + rand()%size;
}
int partion1(int a[],int left,int right)//首先进行分区操作
{
Swap(a[Rand(left,right)],a[left]);//随机选择区间的一个数,将其与首元素进行交换
int flag = a[left];//将首元素当作标杆
int j = left;
for (int i = left+1; i <= right; ++i)//首先忽略首元素
{
if (a[i] < flag)
{
j++;
if ( j != i)
{
Swap(a[i],a[j]);//将标杆数一直往右移动,遇到比标杆数小的就交换到从头开始的位置上
}
}
}
Swap(a[j],a[left]);//j位置的元素和首元素互换,即可保证标杆元素左边为小于等于,右边为大于等于
return j;
}
//标准递归版本的快速排序
void Fast_sort(int a[],int left,int right)
{
if (left < right)
{
int index = partion1(a,left,right);//partion也可以
Fast_sort(a,left,index-1);
Fast_sort(a,index+1,right);
}
}
int main(int argc, char **argv)
{
int a[10] = {12,45,748,12,56,3,89,4,48,2};
Fast_sort(a,0,9);
for (size_t i = 0; i != 10; ++i)
{
cout<<a[i]<<" ";
}
cin.get();
return 0;
}
改进方案:改进选取枢轴的方法
1、选取随机数作为枢轴。
但是随机数的生成本身是一种代价,根本减少不了算法其余部分的平均运行时间。
2、使用左端,右端和中心的中值做为枢轴元。
经验得知,选取左端,右端,中心元素的中值会减少了快排大约 14%的比较。
3、每次选取数据集中的中位数做枢轴。
选取中位数的可以在 O(n)时间内完成。
复杂度分析:
最坏运行时间:当输入数组已排序时,时间为O(n^2),当然可以通过随机化来改进(shuffle array 或者 randomized select pivot),使得期望运行时间为O(nlogn)。
最佳运行时间:O(nlogn)
当输入数组的所有元素都一样时,不管是快速排序还是随机化快速排序的复杂度都为O(n^2),而在算法导论第三版的思考题7-2中通过改变Partition函数,从而改进复杂度为O(n)。
特点分析:不稳定算法(unstable sort)、In-place sort
面试:徒手写代码!试试吧!