快速排序详析的设计.docx
- 文档编号:8178983
- 上传时间:2023-01-29
- 格式:DOCX
- 页数:26
- 大小:472.74KB
快速排序详析的设计.docx
《快速排序详析的设计.docx》由会员分享,可在线阅读,更多相关《快速排序详析的设计.docx(26页珍藏版)》请在冰豆网上搜索。
快速排序详析的设计
数据结构课程设计报告
快速排序详析的设计
专业
计算机科学与技术
学生姓名
班级
学号
指导教师
完成日期
快速排序详析的设计
1.设计内容
1.1排序基本知识
排序(Sorting)是计算机程序设计的一种重要操作,它的功能是将一组任意顺序数据元素(记录),根据某一个(或几个)关键字按一定的顺序重新排列成为有序的序列。
由于待排序的记录数量不同,使得排序过程中涉及的存储器的不同,可将排序方法分为两大类:
一类是内部排序,指的是待排序的记录存放在计算机随机存储器中进行的排序过程;另一类是外部排序,指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需要对外在进行访问的排序过程。
内部排序的方法有很多,但就其全面性能而言,很难提出一种被认为是最好的方法,每一种方法都有各自的优缺点,适合在不同的环境下使用。
按照其策略不同,可归纳为五类:
插入排序、交换排序、选择排序、归并排序和基数排序。
1.2快速排序算法介绍
快速排序就像它的名称所暗示的,是一种快速的分而治之的算法,平均时间复杂度为O(nlog2n)。
它的速度主要归功于一个非常紧凑并且高度化的内部循环。
其基本算法Quicksort(S)由以下四个步骤组成:
(1)如果S中的元素的数目为0或1,则返回。
(2)选择S中的任意一个元素v,v叫做支点(Pivot)。
(3)将S-{v}(剩下的元素在S中)分成两个分开的部分。
L={x属于S-{v}|x<=v}和R={x属于S-{v}|x>=v}。
(4)依次返回Quicksort(L)、v和Quicksort(R)的结果。
基本的快速排序算法可以应用递归实现。
关键的细节包括支点的选择如何分组。
该算法允许把任何元素作为支点。
支点把数组分为两组:
小于支点的元素集和大于支点的元素集。
显然该算法成立,但是不清楚的是,为什么它比归并排序快。
如同归并排序那样,快速排序递归地解决两个之间问题并需要线性的附加工作(第3步),不过,与递归排序不同,这两个子问题并不保证逐步具有相等的大小,这是个潜在的隐患。
快速排序更快的原因在于,第3步划分成两组实际上是在适当的位置进行,并且非常有效,它的高效不仅弥补了大小不等的递归的不足而且还超过了它。
这里介绍的方法是大量分析和经验研究的结果,它代表实现快速排序的非常有效的方法,哪怕是对该方法最微小的偏差都可能引起意想不到的不良结果。
1.3设计题目
设计并实现一种快速排序(Quicksort)的优化版本,并且比较在下列组合情况下算法的性能表现。
cutoff值从0~20。
cutoff值的作用是只有当数组的长度小于等于这个值时,才使用另一种简单排序方法对其排序,否则使用Quicksort算法排序。
选定支点的方法分别是“第一个元素”,“三个元素的中值”,“五个元素的中值”。
对上述的测试分别要采用顺序、逆序、随机三种类型的输入文件。
输入文件中测试数组的大小可以从1000个数到10000个数不等。
程序从input.txt文件中读取输入,输出到output.txt文件。
例如:
input.txt内容如下。
5/*数字的个数*/
54321/*数字用空格分开*/
/*两组测试中间空一行*/
10
4687513920
相应的output.txt内容如下。
Casenumber:
1
Numberofelements:
5
12345
Casenumber:
2
Numberofelements:
10
0123456789
程序的重点在于对每种组合情况下算法性能的比较。
不同的运行时间要用图表表示出来,在图表中要区分由文件大小的不同而产生的差别。
2.设计分析
2.1快速排序的分析
(1)最好情况:
快速排序的最好情况是支点把集合分成两个同等大小的子集,并且在递归的每个阶段都这样划分。
然后就有了两个一半大小的递归调用和线性的分组开销。
在这种情况下运行的时间复杂度是O(nlog2n)。
(2)最坏情况:
假设在每一步的递归调用中,支点都恰好是最小的元素。
这样小元素的集合L就是空的,而大元素集合R拥有了除支点以外的所有元素。
设T是对N个元素进行快速排序所需的运行时间,并假设对0或1个元素排序的时间刚好是1个时间单位。
那么对于N>1,当每次都运气很差地选择最小的元素作为支点,得到的运行时间满足T(N)=T(N-1)+N。
即对N个项进行排序的时间等于递归排序大元素子集中的N-1个项所需要的时间加上进行分组的N个单位的开销。
最终得出:
T(N)=T
(1)+2+3+…+N=N(N+1)=O(N2)
2
(3)支点的选择
错误方式:
比较常见的不明智的选择就是把第一个元素作为支点。
但如果输入是已经预先排过序的,或者是倒序的,该支点给出的分组就很糟糕,因为它是一个末端的元素;而且这种情况会在迭代中继续出现,会以O(N2)的时间复杂度而告终,所以选择第一个元素作为支点不是好的策略。
中位选择:
把中间元素,即待排序序列中中间位置元素,作为支点是合理的选择。
当输入已经排过序时,这种选择在每次递归调用中都会给出理想的支点。
中值划分:
在上述选择中使用中间值得作为支点可以消除非随机输入时出现的退化情况。
但这是一种消极的选择,就是说仅仅试图避免一个坏的支点而并没有尝试选择一个更好的支点。
中值划分是一种选择比平均情况更好的支点的尝试。
在中值划分中,一种比较简单而有效的策略是选择待排序列的第一个、中间一个以及最后一个记录这3个值的中值作为支点。
同样道理,也可以从待排序列中等距离地选取5个值,并将这5个值的中值作为支点。
2.2系统流程图设计
基本的快速排序算法可以应用递归实现。
关键的细节包括支点的选择和如何分组。
该算法允许把任何元素作为支点。
支点把数组分为两组:
小于支点的元素集和大于支点的元素集。
图2-1展示了一组数据进行快速排序的基本过程。
2.3系统的详细设计
程序主要由6部分组成,分别是:
(1)程序入口main函数,从input.txt文件中读取数据,放Array数组中,在执行QuickSort函数之前用clock函数获取系统时95F44stop变量中。
使用stop-start获得QuickSort函数的执行时间。
将运行时间和排序后的数组一起输入到output.txt文件中。
关闭input.txt文件,关闭output.txt文件。
(2)Quicksort,快速排序算法的实现部分,Quicksort函数包括五个参数分别是数组的地址Array,待排序的第一个元素在数组中的下标,待排序的最后一个元素在数组中的下标,cutoff(小于cutoff时使用另一种简单的方法进行排序),支点median。
Quicksort函数分为以下几个步骤:
1.确定数组的大小是否为0或1,若为0或1则直接退出函数排序未完成。
2.判断支点median取值是否为1,3或5,否则返回错误。
3.判断cutoff取值是否恰当。
4.用median函数获得支点。
5.在分别设定指针low和high指向数组第一个和最后一个元素。
6.通过low++和high--的方式让low和high向中间移动直到Array[low]大于median或Array[high]小于median。
7.判断low是否小于high,若不小于,则交换Array[low]与Array[high]的值,否则跳出循环排序语句。
8.将原数组分成两部分后分别用Quicksort函数处理两个数组。
Quicksort函数排序是一个将数组以median为分界分成一个元素都小于median的数组和一个元素都大于median的数组。
再将这两个数组用Quicksort函数处理的递归调用过程。
(3)MedianOf3,选择三个值的中值作为支点;
(4)MedianOf5,选择五个值的中值作为支点;
(5)Swap,简单地交换两个元素的值;
(6)InsertionSort,在数组长度小于cutoff值时使用插入排序来代替快速排序。
下面描述main和Quicksort两个函数的基本算法的运算过程。
main函数:
打开input.txt和output.txt文件;
读入数的个数n;
从文件中顺序读入n个数,并放到数组中;
应用Quicksort对该数组排序;
将排序后的数输出到文件output.txt中;
读入下一个数组的个数,继续上述过程;
关闭文件。
Quicksort函数:
参数:
待排数组,待排段的起点位置,待排段的终点位置,cutoff值,支点选择方法
If数组是空的
Exit
If待排数段的元素个数大于等于cutoff值,且元素个数大于等于支点选择方法所要求的元素个数
根据支点选择方法选择一个元素作为支点
设置low为起点值、high为终点值
Whilelow<=high{
Whilelow位置的值小于支点值
low++
Whilehigh位置的值大于支点值
high--
Iflow 交换low、high两个元素 } 将low位置的元素与支点元素交换 Quicksort递归调用左半段 Quicksort递归调用右半段 Else 应用直接插入排序法对数组元素排序 3.设计实践 3.1开发环境 MicrosoftVisualC++6.0 MicrosoftVisualC++,(简称VisualC++、MSVC、VC++或VC)是Microsoft公司推出的开发Win32环境程序,面向对象的可视化集成编程系统。 它不但具有程序框架自动生成、灵活方便的类管理、代码编写和界面设计集成交互操作、可开发多种程序等优点,而且通过简单的设置就可使其生成的程序框架支持数据库接口、OLE2,WinSock网络、3D控制界面。 它以拥有“语法高亮”,IntelliSense(自动完成功能)以及高级除错功能而著称。 比如,它允许用户进行远程调试,单步执行等。 还有允许用户在调试期间重新编译被修改的代码,而不必重新启动正在调试的程序。 其编译及建置系统以预编译头文件、最小重建功能及累加连结著称。 这些特征明显缩短程式编辑、编译及连结花费的时间,在大型软件计划上尤其显著。 3.2快速排序过程 在待排序的n各记录中任取一条记录(通常去第一条记录),把它作为基准元素,确定该条记录的最终位置,即该条记录左边的记录的所有记录的关键字的值均小于该记录,右边的所有记录的关键字的值均大于等于该记录。 待排序序列以基准元素为界限被分割成两个区域,这个过程称作一次快速排序。 之后对所有的区间分别重复上述过程,直至每个区间只有一条记录为止。 快速排序是一个递归过程,整个排序过程对不同的区间进行快速排序。 假设要排序的数组是A[1]……A[N],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一躺快速排序。 一躺快速排序的算法是: 设置两个变量I、J,排序开始的时候I: =1,J: =N; 以第一个数组元素作为关键数据,赋值给X,即X: =A[1]; 从J开始向前搜索,即由后开始向前搜索(J: =J-1),找到第一个小于X的值,两者交换; 从I开始向后搜索,即由前开始向后搜索(I: =I+1),找到第一个大于X的值,两者交换; 重复第3、4步,直到I=J; 【事例模范】 例: 将数据(45,53,18,36,72,30,48,93,15,36)进行快速排序。 快速排序的一次划分过程如下所示: 【45531836723048931536】移动比较 【36531836723048931545】交换位置并比较 【36451836723048931553】交换位置并比较 【36151836723048934553】交换位置并比较 【36151836453048937253】交换位置并比较 【36151836304548937253】交换位置,i=j 【3615183630】45【48937253】一趟排序的结果 一次完整的快速排序的排序过程(待排序区间以中括号括起来)如下所示: 【45531836723048931536】 【3036183615】45【48937253】 【1815】30【3636】45【48937253】 151830【36】【36】45【48937253】 151830363645【48937253】 15183036364548【937253】 15183036364548【5372】93 15183036364548537293 3.3调试过程 1.启动VC 启动VC的前提是首先要安装VC软件。 如果你的系统安装了VC软件,当你启动了Windows系统之后,从“开始”菜单进入“所有程序”子菜单,找到MicrosoftVisualC++6.0并单击它即进入VC软件的主窗口,如图3-3-1所示: 图3-3-1 2.编辑程序 若要在VC窗口下进行C程序的编辑,首先,单击工具栏的NewTextFile按钮,生成一个新的文本文件窗口,如图所示;接着,单击Save按钮,激活“保存为”对话框,在指定的文件夹下,输入当前程序的文件名(注意: 文件名必须给出.C的扩展名),再按“保存”按钮。 到此为止,在指定的目录下,就生成了一个由读者自己命名的C文件(比如main.C),接下来,就可以进入编辑屏幕输入你的C源程序了。 由于当前的文件是C源程序文件,在其中输入的任何内容(如: 关键字、用户标识符及各种运算符),VC系统都会按C源程序的格式进行编排、组织。 比如: 在文件中,当你输入了一个C关键字时,VC系统自动将其设定为蓝色字体以示区别;在编辑过程中,如果你输入了一个块结构语句(如: for(i=0;i<10;i++)、if(s! =’\0’)、while(k<5)),按回车后,VC系统会把光标定位在该块语句起始位置开始的下一行的每五个字符位置上,以表示下面输入的内容是属于该块语句的,以体现C源程序的缩进式书写格式;此时,如果输入一个左花括号“{”并回车,VC系统将把该花括号左移到与上一行块语句起始位置对齐的位置上;接着,再按下回车键,VC系统会自动采用缩进格式,将当前光标位置定位在此花括号的下一行的第五列上;如果上一行语句与下一行语句同属于一个程序段(比如: 同一个复合语句中的语句),VC系统会自动将这两个程序行的起始位置对齐排列。 如图3-3-2所示: 3.编译程序 程序编辑完后,即可对源程序进行编译处理。 按“编译微型条”的Compile(功能键是Ctrl+F7)按钮,对程序进行编译,这时,屏幕上出现如图3-3-3所示的对话框,让建立一个默认的工程工作区,选“是”按钮确认;紧接着,又出现如图3-3-4所示的对话框,问是否要保存当前的C文件,回答“是”;然后,系统开始编译当前程序。 如果程序正确,即程序中不存在语法错误,则VC窗口的输出如图3-3-5所示的结果。 4.运行程序 当程序编译提示无错误信息(0error(s))后,按下微型编译条工具栏上的“建立执行程序”(BuildExecute)按钮,或相应的功能键Ctrl+F5,程序开始运行,然后显示程序的输出结果如图所示。 输出结果的屏幕将等待用户按下任意键后,才返回编辑状态,一个C程序的执行过程结束。 如图3-3-6所示: 图3-3-6 3.4代码实现 #include #include #include #include #defineSIZE10000/*数组最大的容量*/ intMedianOf3(inta[],intlow,inthigh); intMedianOf5(inta[],intlow,inthigh); voidSwap(int*a,int*b); voidQuickSort(inta[],intleft,intright,intcutoff,intmedian); voidInsertionSort(inta[],intlow,inthigh); intmain() { inti,group=0,numbOFelements,elements,Amount; intArray[10000]; intcutoff=0,median=3; clock_tstart,stop; FILE*InputPTR,*OutputPTR;/*input和output文件指针*/ InputPTR=fopen("input.txt","r+"); OutputPTR=fopen("output.txt","w+"); if(InputPTR==NULL)/*检查input文件是否存在*/ { printf("Cannotopenfile! "); exit(0); } printf("PleaseInputthevalueofcutoff(0~20): "); scanf("%d",&cutoff); printf("PleaseInputthevalueofmedian(1or3or5): "); scanf("%d",&median); Amount=fscanf(InputPTR,"%d",&numbOFelements);/*读取每次迭代的元素的个数*/ while(Amount! =EOF)/*当读到的不是文件的末尾*/ { group++; fprintf(OutputPTR,"Casenumber: %d\nNumberofelements: %d\n", group,numbOFelements);/*输出的格式*/ for(i=0;i fscanf(InputPTR,"%d",&Array[i]);/*将输入读入到数组中*/ fgetc(InputPTR); } fgetc(InputPTR); QuickSort(Array,0,numbOFelements-1,cutoff,median);/*给数组排序*/ for(i=0;i { fprintf(OutputPTR,"%d",Array[i]);/*打印排序后的数组*/ } Amount=fscanf(InputPTR,"%d",&numbOFelements); fprintf(OutputPTR,"\n\n"); } fclose(InputPTR); fclose(OutputPTR); return0; } /******支点(pivot)选择三个值的中值的算法******/ intMedianOf3(inta[],intlow,inthigh) { intmid=(low+high)/2;/*确定中间位置*/ if(a[low]>a[mid]){ Swap(&a[low],&a[mid]); }if(a[low]>a[high]){ Swap(&a[low],&a[high]); }if(a[mid]>a[high]){ Swap(&a[mid],&a[high]); } Swap(&a[mid],&a[high]);/*交换排序后的中间元素和最后元素的值*/ returna[high]; } /******支点(pivot)选择五个值的中值的算法******/ intMedianOf5(inta[],intlow,inthigh) { inti,temp,j,largest; intStep; Step=(high-low)/4;/*设定步长为四分之一,这样便能选出五个元素*/ for(j=0;j<4;++j) { largest=high-j*Step;/*每次迭代选择不同的值作为最大值*/ for(i=j+1;i<5;++i) {/*比较每次选中的值,用来找到最大值*/ if(a[high-i*Step]>a[largest]) { largest=high-i*Step;/*设定新的最大值*/ } } Swap(&a[high-j*Step],&a[largest]);/*将每次找到的最大值放在每次对应的位置*/ } Swap(&a[high-Step-Step],&a[high]);/*将选定的支点放到最后面*/ Swap(&a[high-3*Step],&a[low]); returna[high]; } /******交换两个元素******/ voidSwap(int*a,int*b) { inttemp=*a; *a=*b; *b=temp; } /******插入排序******/ voidInsertionSort(inta[],intmin,intmax) { intj,i,temp; for(i=min;i<=max;i++) { temp=a[i]; for(j=i;j>min&&a[j-1]>temp;j--) a[j]=a[j-1]; a[j]=temp; } } /******快速排序******/ voidQuickSort(intArray[],intmin,intmax,intcutoff,intmedian) { /*median=1时使用第一个值作为支点 median=3时使用三个值的中值作为支点 median=5时使用五个值的中值作为支点*/ intlow,high,Pivot; if((max-min)==0) return;/*如果数组中没有元素*/ if((median! =1)&&(median! =3)&&(median! =5)){ /*median只可以为1、3、5*/ printf("Invalidmedianvalue! \n"); exit(0); } if(min+cutoff<=max&&(max-min+1)>=median) {/*检查cutoff值是否合适*/ if(median==1
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 快速 排序 设计