排序算法(四)——快速排序

来源:互联网 发布:阿里云 七牛云 直播 编辑:程序博客网 时间:2024/06/01 08:23

排序

快速排序:

下面我们来研究一下快速排序,快速排序也是经常运用到的,java库中对于基本数据类型的数据就是采用快速排序。

快速排序(quick sort)是对冒泡排序的一种改进,在冒泡排序中,记录的比较和移动是在相邻位置进行,记录每次交换后只能后移一个位置,因而总的比较次数和移动次数较多。在快速排序中,记录的比较和移动是从两端向中间进行的,较大的记录一次就能从前面移动到后面,较小的记录一次就能从后面移动到前面,记录移动的距离较远,从而减少了总的比较次数和移动次数。

基本思想: 首先选一个轴值(即比较的基准),通过一趟排序将待排序记录分割成独立的两部分,前一部分记录均小于或等于轴值,后一部分记录均大于或等于轴值,然后分别对这两部分重复上述方法,直到整个序列有序。所以快速排序是一个递归的过程

选择轴值:
1.使用第一个记录的关键码;
2.选取序列中间记录的关键码;
3.比较序列中第一个记录、最后一个记录和中间记录的关键码,取关键码居中的作为轴值并调换到第一个记录的位置;
4.随机选取轴值。
轴值的选择将决定两个子序列的长度,子序列的长度最好相等。这里笔者选择第一个记录作为轴值


一次划分:对待排序列进行一次划分的过程。

设待划分的序列是r[s] ~ r[t],设参数i,j分别指向子序列左、右两端的下标s和t,令r[s]为轴值,

(1)j从后向前扫描(找小),直到r[j]<r[i],将r[j]移动到r[i]的位置,使关键码小(同轴值比较)的记录移动到前面去;

(2)i从前向后扫描(找大),直到r[i]>r[j],将r[i]移动到r[j]的位置,使关键码大(同轴值比较)的记录移动到后面去;

(3)重复上述过程,直到i=j。


一次划分如图

quickSort


看看具体过程:

quickSort


快速排序 进行划分
/*
* first end 分别指向待划分区间的最左侧记录和最右侧记录
* ① 初始化 第一个记录为轴值
* ② 右侧扫描 从end向前扫,直到扫描到小于轴值的记录后交换,此时轴值交换到了end所指的位置。
* ③ 左侧扫描 从first想后扫,知道扫描到了大于轴值的记录后交换,此时轴值交换到了first所指的位置。
* ④ 重复 ② ③ 过程直到 first 和 end 相遇,并返回相遇位置(轴值最终位置)
*/

private int partition(T[] t, int first, int end){while(first < end){ //右侧扫描 : 将轴值与end指向的记录进行比较。若end大,则end 前移一个位置,直到end小于轴值while(first < end && compare(t[first], t[end])){ end--;     }if(first < end){T tmp = t[end];    //小的记录移到前面, 交换后轴值为end指向的记录。t[end] = t[first];t[first] = tmp;first ++;}//左侧扫描 :将轴值与first指向的记录比较。若first小,则 first前移一个位置,直到first大于轴值while(first < end && compare(t[first], t[end])){   first ++;}if(first < end){T tmp = t[end];     // 大的记录移到后面,交换后轴值又为first指向的记录t[end] = t[first];t[first] = tmp;end --;}}return first;}


递归过程:快速排序是一个递归的过程,需要不断对子序列进行划分,直到每个序列都为有序序列。
private void sort(T[] t, int first, int end){if(first < end){                                //区间长度大于1,执行一次划分,否则递归结束。int center = partition(t, first, end); //一次划分sort(t, first, center -1);            // 递归地对左侧子序列进行快速排序sort(t, center +1, end);<span style="white-space:pre"></span>     //递归地对右侧子序列进行快速排序}}

快速排序主入口:
/** * 交换排序: 快速排序 */@Overridepublic void quickSort(T[] t) {if(t.length > 0){sort(t, 0, t.length -1);}System.out.println("快速排序:");print(t);}

上面的compare 、 print 方法将在讲解完全部排序介绍,这里仍然才用泛型。


性能分析:  快速排序的趟数取决于递归的深度

(1)在最好情况下,每一次划分对一个记录定位后,该记录的左侧子表与右侧子表的长度相同,时间复杂度为O(nlog2n)。对n个记录的序列进行排序的时间,每次划分后,正好把待划分区间划分成长度相等的两个子序列。

(2)在最坏情况下,待排序列为正序或者逆序。每次划分只得到一个比上一次划分少一个记录的子序列(另一个子序列为空),时间复杂度为 O(N*N)。

(3)平均情况下,由于给出的序列一般都是乱序的,很少情况出现正序或者逆序,所以平均性能的数量级也是O(nlog2n)。

稳定性:

若两个记录A和B值相等,但是排序后A、B的先后次序保持不变,则这种排序是稳定的,否则就是不稳定。快速排序是一种不稳定的排序算法。

总结: 

(1)由于快速排序是递归的,需要一个栈来存放每一层递归调用的信息,其容量与递归调用的深度一致,平均深度为:O(logN)。

(2)快速排序是一种不稳定的排序算法。

(3)快速排序适用于待排记录个数很大并且原始记录随机(这也是数据的主要情况:多、乱)。

(4)快速排序的平均性能很好。




1 0
原创粉丝点击