快速排序
| |
1. 基本思想: 首先选取一个记录作为枢轴(不失一般性,可选第一个记录),依它的关键字为基准重排其余记录,将所有关键字比它大的记录都排在它之后,而将所有关键字比它小的记录都排在它之前,由此完成一趟快速排序。之后,分别对由一趟排序分割而成的两个子序列进行快速排序。 快速排序是一个递归的算法
2.性能分析:
□最坏情况: 当待排序记录已经"有序"的情况下,排序时间最长。这时,第一次划分经过n-1次比较,将第一个记录定在原位置上;第二次递归调用,经过n-2次比较,将第二个记录定在它原来的位置上,......,这样,总的比较次数为: C = n (n-1) / 2 =O(n*n);
□最好情况: 每次划分所取的枢轴都是当前无序子序列中的"中值"记录,划分的结果是枢轴的左右两个子区的长度大致相等,这时总的比较次数为: C ≤ n + 2C(n/2) ≤ n + 2[n /2+2C(n/22)] = 2n+4 (n/ 22) ≤ 2n + 4[n/4+2C(n/23 )] = 3n+8 (n / 23) ≤ ...... ≤ kn+2k C(n/2k) = nlog2n + nC(1) = O(nlog2n) 可以证明,快速排序的平均 时间复杂度是O(nlog2n),它是目前基于比较的内部排序方法中速度最快的一种,快速排序也因此而得名。 |
快速排序的程序实现
| |
//* * * * * * * * * * * * * * * * * * * * * * * * //*PROGRAM :快速排序 * //*CONTENT :快速排序 * //* * * * * * * * * * * * * * * * * * * * * * * * #include <dos.h> #include <conio.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #define MAXSIZE 20 //排序表的最大容量 typedef struct //定义排序表的结构 {int elemword[MAXSIZE]; //数据元素关键字 int count; //表中当前元素的个数 }SqList; void InitialSqList(SqList&); //初始化排序表 void QuickSort(SqList &); //快速排序 void QSort(SqList &,int,int); //子序列快速排序 int Partition(SqList &,int,int); //一趟快速排序 void PrintSqList(SqList); //显示表中的所有元素 void main() {SqList L; //声明表L char j='y'; //-------------------------程序说明------------------------------- printf("本程序将演示快速排序的操作。/n"); //---------------------------------------------------------------- while(j!='n'&&j!='N') {InitialSqList(L); //待排序列初始化 QuickSort(L); //快速排序 PrintSqList(L); //显示排序结果 printf("继续进行下一次排序吗?(Y/N)"); scanf(" %c",&j); } printf("程序运行结束!/n按任意键关闭窗口!/n"); getchar();getchar(); } void InitialSqList(SqList &L) {//表初始化 int i; printf("请输入待排序的记录的个数:"); scanf("%d",&L.count); printf("请输入待排序的记录的关键字(整型数):/n"); for(i=1;i<=L.count;i++) scanf("%d",&L.elemword[i]); } void QuickSort(SqList &L) {//对顺序表L做快速排序。 QSort(L,1,L.count); } void QSort(SqList &L,int low,int high) {//对顺序表中的子序列L.r[low..high]作快速排序 int pivotloc; if(low<high) //长度大于1 {pivotloc=Partition(L,low,high); //将L.elemword[low..high]一分为二 QSort(L,low,pivotloc-1); //对低子表递归排序,pivotloc是枢轴位置 QSort(L,pivotloc+1,high); //对高子表递归排序 } } int Partition(SqList &L,int low,int high) {//交换顺序表L中子表r[low..high]的记录,枢轴记录到位,并返回其所在位置,此时 //在它之前(后)的记录均不大(小)于它 int pivotkey; pivotkey=L.elemword[low]; //用子表的第一个记录作枢轴记录 while(low<high) //从表的两端交替地向中间扫描 {while(low<high&&L.elemword[high]>=pivotkey) --high; L.elemword[low]=L.elemword[high];//将比枢轴记录小的记录移到低端 while(low<high&&L.elemword[low]<=pivotkey) ++low; L.elemword[high]=L.elemword[low]; //将比枢轴记录大的记录移到高端 } L.elemword[low]=pivotkey; //枢轴记录到位 return low; //返回枢轴记录 } void PrintSqList(SqList L) {//显示表中所有元素 int i; printf("已排好序的序列如下:/n"); for(i=1;i<=L.count;i++) printf("%4d",L.elemword[i]); printf("/n"); } |
快速排序
快速排序 Quick Sort
我们已经知道,在决策树计算模型下,任何一个基于比较来确定两个元素相对位置的排序算法需要Ω(nlogn)计算时间。如果我们能设计一个需要O(n1ogn)时间的排序算法,则在渐近的意义上,这个排序算法就是最优的。许多排序算法都是追求这个目标。
下面介绍快速排序算法,它在平均情况下需要O(nlogn)时间。这个算法是由C.A.R.Hoare发明的。
算法的基本思想
快速排序的基本思想是基于分治策略的。对于输入的子序列L[p..r],如果规模足够小则直接进行排序(比如用前述的冒泡、选择、插入排序均可),否则分三步处理:
分 解(Divide):将待排序列L[p..r]划分为两个非空子序列L[p..q]和L[q+1..r],使L[p..q]中任一元素的值不大于 L[q+1..r]中任一元素的值。具体可通过这样的途径实现:在序列L[p..r]中选择数据元素L[q],经比较和移动后,L[q]将处于 L[p..r]中间的适当位置,使得数据元素L[q]的值小于L[q+1..r]中任一元素的值。
递归求解(Conquer):通过递归调用快速排序算法,分别对L[p..q]和L[q+1..r]进行排序。
合并(Merge):由于对分解出的两个子序列的排序是就地进行的,所以在L[p..q]和L[q+1..r]都排好序后不需要执行任何计算L[p..r]就已排好序,即自然合并。
这个解决流程是符合分治法的基本步骤的。因此,快速排序法是分治法的经典应用实例之一。
快速排序法是对冒泡排序法的一种改进,也是基于交换排序的一种算法。因此,被称为"分区交换排序"。
在待排序序列中按某种方法选取一个元素K,以它为分界点,用交换的方法将序列分为两个部分:比该值小的放在左边,否则在右边。形成"{左子序列}K{右子序列}"。再分别对左、右两部分实施上述分解过程,直到各子序列长度为1,即有序为止。
分界点元素值K的选取方法不同,将构成不同的排序法,也将影响排序的效率:例如,可取左边第1个元素为分界点、取中点A[(left+right)/2]为分界点、或选取最大和最小值的平均值为分界点等。