排序算法——快速排序

来源:互联网 发布:oracle 数据库高可用 编辑:程序博客网 时间:2024/05/01 22:57

快速排序说白了就是一个不断填坑的过程!其基本思想是基于分治法的:在待排序表A[1...n]中任取一个元素pivot作为基准,通过一趟排序将待排序的表划分为独立的两部分A[1...k-1]和A[k+1...n],使得L[1...k-1]中所有元素都小于pivot,而相应地A[k+1...n]中的所有元素都大于等于pivot,并将pivot放置于A[k]处,这个过程就称之为一趟快速排序。然后递归地对两个子表重复上述过程,直到每部分内只有一个元素或空为止!

简单说来就是:

1.先从数组中找一个基准元素,一般选第一个或者最后一个,也可以随机选一个;

2.进行划分,将比基准数小的放到它左边,不小于基准的全放到它右边;

3.对左右俩子区间继续之前的方法进行划分,直到子区间只有一个元素或者为空;


其主要过程如下:

template <typename T> //此处用模板函数纯粹属于装X...大家可以直接写个int数组void QuickSort(T A[], int low, int high){if (low < high) {int pivotPos = Partition(A, low, high);QuickSort(A, low, pivotPos-1);QuickSort(A, pivotPos+1, high);}}

划分过程常用的有两种思路:

1)俩索引分别从首尾向中间扫描:标准的填坑思路!易于理解:

template <typename T>int Partition(T A[], int low, int high){T pivot = A[low];//先挖最低位的坑作为基准元素while (low < high) {while (low < high && A[high] >= pivot) --high;A[low] = A[high];//填补低位留下的坑while(low < high && A[low] < pivot) ++low;A[high] = A[low];//填补高位留下的坑}A[low] = pivot;//最终划分处的坑,填完收工!return low;}


来看看为什么是个填坑过程吧:

假设有如下初始列:

0 1 2 3 4 5 6 7   //索引号

3 8 7 1 2 5 6 4   //第一步:令基准pivot = A[0],这不相当于在A[0]处挖了个坑吗,不过要把挖出来的保存到pivot中
2 8 7 1 2 5 6 4  //第二步:从右往左数找到第一个小于3(即pivot)的数即A[4],用它来填上一步A[0]留下的坑
2 8 7 1 8 5 6 4  //第三步:从左往右数找到第一个不小于3(即pivot)的数即A[1],用它来填上一步A[4]留下的坑
以此类推...自己在纸上画画...懒得写了
2 1 3 7 8 5 6 4   //最终结果!最后一步,用pivot保存的东东来填A[2]处留下的坑

填坑结束,打完收工!


2)俩索引一前一后逐步向后扫描:优点在于可以使序列的其中一部分保持相对顺序不变,这在某些特定应用中是有用的

template <typename T>int Partition(T A[], int low, int high){T pivot = A[high]; //以最后一个元素,A[high]为主元int i = low - 1;for (int j = low; j <= high-1; ++j) {if (A[j] < pivot) {++i;swap(A[i], A[j]);//即保证索引i及i之前的所有元素都是小于基准元素(pivot)的!//注意通过此种方式,划分之后,pivot左边的元素相对顺序是不变的!}}swap(A[i+1], A[high]);return i+1;}

一次划分过程如下:仍然用上面的那个数组

3 8 7 1 2 5 6 4  //选择最后的一个元素4作为基准,3与3交换,不用移动元素,比较了1次
3 1 7 8 2 5 6 4  //8与1交换,比较了3次
3 1 2 8 7 5 6 4  //7与2交换,比较了1次
3 1 2 4 7 5 6 8  //8与4交换,比较了2次
注意看着哦,经过一次划分后,基准元素4左边的元素(即2 1 3)的相对顺序是保持不变的!



快排的算法性能:

(1).空间效率:由于上述过程是递归的,所以需要一递归工作栈来保存递归调用信息。平均情况下,栈的深度为

(2).时间复杂度:快排运行时间与划分是否对称有关。当初始序列基本有序或者基本逆序时,此时得到的划分极不对称,就得到最坏情况下的时间复杂度,就这样竟然叫做快速排序。。而当每次划分都很对称时,就得到最好的情况,此时复杂度为;不幸中的万幸就是,快排平均情况下的运行时间与其最佳状况的运行时间是很接近的,故它的平均时间复杂度也为

这里提几个改进快排的思路:

1)当递归过程中得到的子序列规模较小时就不在继续递归调用快排了,而是直接采用插入排序来进行后续的工作;

2)尽量选取一个可以将数据中分的基准元素;比如说可以在序列头尾和中间取3个元素,然后用他们的中位值来作为基准;当然也可以随机从序列中选取基准元素,这样做可以使得最坏情况在实际排序中发生的概率极低;


(3).稳定性:显而易见,快排是不稳定的,不过上面所讲的第二种划分方法得到了部分顺序保持的序列,还是值得注意的~



0 0
原创粉丝点击