快速排序(算法与数据结构课程设计)
![快速排序(算法与数据结构课程设计)](https://img.360docs.net/imgcf/1o9yg2mw5y3mo91ad95t0co8k96zbjw8-f1.webp)
![快速排序(算法与数据结构课程设计)](https://img.360docs.net/imgcf/1o9yg2mw5y3mo91ad95t0co8k96zbjw8-d2.webp)
^
快速排序
一、问题描述
排序是数据结构中典型的算法,经常有插入排序、选择排序、快速排序等。本文要求对排序表中的无序数据进行快速排序,并讨论快速排序的改进方法(双倍快速排序、基于归并的快速排序),这样可以对排序进行优化,提高效率。
二、基本要求
1、选择合适的存储结构建立排序表,并能遍历表输出元素。
2、编写快速排序算法,并能够输出排序的结果。
3. 快速排序及其改进—双倍快速排序和基于归并的快速排序算法。
三、测试数据
{
输入以下数据:5 、3、7、11、1、9、4、8
四、算法思想
1、普通快速排序的基本思想:可以运用一趟快速排序把序列分成分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行快速排序,以达到整个序列有序。一趟的快速排序是:附设两个指针low和high,它们的初值分别为low和high,设枢轴记录的关键字为pivotkey,则首先从high所指位置起向前搜索到第一个关键字小于pivotkey大的记录和枢轴记录互相交换,然后从low所指位置向后搜索,找到第一个关键字大于pivotkey的记录和枢轴记录互相交换,重复这两部直至low=high为止。
2、双倍快速排序思想:快速排序的基本思想是基于分支策略的思想。即对于输入的子序列L[low..high],如果规模足够小则直接进行排序,否则分三步处理:
1) 分解(Divide) :设输入的序列L[low..High],确定支点元素L[low]和L[High],并使L[Low].key<=Ll[High].key ,然后分解(Divide):将序列L[low ..High ]划分成三个子序列L[Low..L-1]、L[L+1..H-1]和L[H+1..High],使L[low ..High]中元素的关系为
L[Low..L-1] 2) 递归求解(Conquer) :通过递归调用快速排序算法分别对L[Low..L-1]、L[L+1..H-1]和L[H+1..High]分别进行分解排序。 3) 合并(Merge):对分解出的三个子序列的排序是就地进行的,所以在L[Low..L-1]、L[L+1..H-1] 和L[H+1..High]都排好序后不需要执行任何计算L[low..High]就已排好序。 3、基于归并的快速排序:对划分结果产生的两个子序列的长度进行检查,如果其中一个与另一个的长度比超过某一界限,则认为这是一个“畸形划分”,对较短的子序列继续使 用“快速排序”,而把较长的子序列平分为两个子序列分别排序,然后再进行一次合并。两个有序序列的合并是可以实现为线性的时间复杂度的,因此可以在每次都是畸形划分时仍然获得)*(LogN N O 的时间复杂度。其中Partition 就是众所周知的用于“快速排序”的划分子程序,Merge(Data, First,Size)把Data 中[0,First)和[First, Size)两个有序列合并为一个有序序列并存放在Data 中。Partition 划分的位置M 处的值就是划分的枢值,也就是说序列可以分成[0,M-1]、[M,M]和[M+1,Size-1]三部分。如果Partition 的实现不能保证这一点,则MoreData 应为Data[M],而MoreSize 也应为Size - M 。 ( 五、模块划分 1、void Create(SqList *L),建立排序表。 2、void Traverse(SqList L),遍历排序表(输出哨兵)。 3、void swap(int *a,int *b),用于交换两个数。 4、int Partition(SqList *L, int low, int high),将一个序列划分成两个子序列,后一子序列所有值都不大于前一子序列任意值。返回子序列分割处索引。 5、void QSort1(SqList *L, int low, int high),调用快排函数进行排序。 6、int QSort2(SqList *L, int low, int high),调用双倍快排函数进行排序。 7、void Merge (RedType SR[], RedType TR[], int i, int m, int n),两个有序序列合并为一个有序列序。 — 8、void MSort(RedType SR[], RedType TR1[], int s, int t),归并排序。 9、int qsort1(SqList *L, int low, int high),快速排序。 10、void menu ,输出时清晰。 11、int main(),主函数。 六、数据结构ey)); L->length=n; } /* 遍历排序表(输出哨兵) */ \ void Traverse(SqList L) { int i; for(i=1; i<=; i++) printf("%6d",[i].key); } /*交换函数*/ void swap(int *a,int *b) { int temp; 、 temp=*a; *a=*b; *b=temp; } /* 快速排序 */ int Partition(SqList *L, int low, int high) { KeyType pivotkey;/*关键字*/ L->r[0]=L->r[low]; — pivotkey=L->r[low].key; while(low { while (low while (low L->r[low]=L->r[0]; Traverse(*L);/*每一趟的输出*/ ( printf("\n"); return low; } /*快速排序函数*/ void QSort1(SqList *L, int low, int high) { int pivotloc;/*设置枢轴*/ if (low { pivotloc=Partition(L,low,high); ^ QSort1(L,low,pivotloc-1); QSort1(L,pivotloc+1,high); } } /*双倍快速排序函数*/ int QSort2(SqList *L, int low, int high) { int n,Ls,Hs,i;/*设置两个枢轴从两头一起排序*/n=high-low;/*控制循环次数*/ if(low>high||low==high) ~ return 1; if(L->r[low].key>L->r[high].key) /*确保区间内第一个元素的值不大于区间内最后一个元素的值*/ swap(&L->r[low].key,&L->r[high].key); Ls=low; Hs=high; for(i=low+1;i<=n;i++) { if(L->r[i].key — else if(L->r[i].key>L->r[high].key) {Hs--; swap(&L->r[i].key,&L->r[Hs].key);/*交换两数*/ i--;/*下一个比较位置不变*/ n--;/*循环次数减1*/ } } { swap(&L->r[Ls].key,&L->r[low].key); /*交换两数*/ swap(&L->r[Hs].key,&L->r[high].key);/*交换两数*/ Traverse(*L);/*每一趟的输出*/ printf("\n"); QSort2(L,low,Ls-1);/*对分解后的第一部分递归快速排序*/ QSort2(L,Ls+1,Hs-1);/*对分解后的第二部分第归快速排序*/ QSort2(L,Hs+1,high);/*对分解后的第三部分第归快速排序*/ return 0; % } /*基于归并的快速排序*/ void Merge (RedType SR[], RedType TR[], int i, int m, int n)/*调用归并函*/ { int j,k;/*定义两数*/ for (j=m+1, k=i; i<=m && j<=n; ++k) { if LQ(SR[i].key,SR[j].key) TR[k] = SR[i++]; else TR[k] = SR[j++]; 《 } if (i<=m) while (k<=n && i<=m) TR[k++]=SR[i++]; if (j<=n) while (k<=n &&j <=n) TR[k++]=SR[j++]; } void MSort(RedType SR[], RedType TR1[], int s, int t) /*调用归并函数*/ { int m; | RedType TR2[20]; if (s==t) TR1[t] = SR[s]; else { m=(s+t)/2; MSort(SR,TR2,s,m);/*调用归并函数*/ MSort(SR,TR2,m+1,t);/*调用归并函数*/ Merge(TR2,TR1,s,m,t); /*主要消除畸形划分*/ } & } int qsort1(SqList *L, int low, int high) /*返回整数类型的快排*/ { int k,f,s,r,mid; r=N-1; if(low>high) return 1; k=Partition(L,low,high); f=k-low;s=high-k; , if(f>s&&s!=0) r=f/s;/*检查其中一个子序列与另一个的长度比是否超过某一界限*/ else if(f!=0) r=s/f; if(r>M) { mid=(k-1-low)/2+s;/*较长的子序列平分为两个子序列*/ QSort1(L,s,mid); /*此时进行快速排序*/ | QSort1(L,mid+1,k-1); MSort(L->r,L->r,mid,k-1 ); mid=(f-k-1)/2+k+1; QSort1(L,k+1,mid); QSort1(L,mid+1,f); MSort(L->r,L->r,mid,f ); Traverse(*L); printf("\n"); \ } else QSort1(L,low,high); return 0; } void menu() { SqList L; int x; 【 printf("\n 08课程设计快速排序算法演示\n"); printf("\n1快速排序算法 \n"); printf("\n2双倍快速排序算法\n"); printf("\n3基于归并的快速排序算法\n"); printf("\n4退出演示\n"); scanf("%d",&x); switch(x) /*调用switch语句进行输出*/ {case 1: ) printf("\n普通快速排序\n"); Create(&L); QSort1(&L,1,; break; case 2: printf("\n双倍快速排序\n"); Create(&L); QSort2(&L,1,; 、 break; case 3: printf("\n基于归并改良的快速排序\n"); Create(&L); qsort1(&L,1, ; break; case 4: printf("\n演示结束\n"); ; break; default:printf("\n输入有错\n"); } } /* 主函数 */ int main() { menu(); system("pause"); ' return 1; } 八、测试情况 程序的测试结果如下: 1、快速排序结果: 2、双倍快速排序 运行结果正确经验证,手工运算也对。 3、基于归并的快速排序 运行正确,结果也正确。 九、参考文献 1、严蔚敏,《数据结构 C语言》,清华大学出版社。 2、谭浩强,《c语言程序设计》,清华大学出版社。 小结 经过一年的数据结构学习,对于程序编写的算法及其应用有了一定的了解和掌握,并且能够在一些方面将其运用。 这次的课程设计是关于快速排序以及其的一些改进方法的设计。虽然这次的课程设计并没有真正的涉及生活工作中的应用,但是我们通过本次的数据结构的课程设计,了解并知道了一些调试程序的方法,而且学会了一些处理错误的方法。这次的课程设计使得我们对数据结构有了进一步的掌握,并是的我们对VC++的运用更加的熟悉。经历了这次课程设计,不仅仅是对我们的学习提供了帮助,也同时对我们的意志力进行了一次锻炼。没有足够的耐心就会刚编辑一点程序或者出个错误找不到就会不耐烦,从而导致接下来的工作无法进行而被迫暂时搁置,最终使得课程设计的完成效率大大降低。快速排序算法的最好时间O(nlogn)平均时间)*(LogN N O 最坏时间)(2 N O 稳定性--不稳定 解释:在所有同数量级)*(LogN N O 的排序方法中,快速排序被认为是平均性能最好的一种;但是,若初始记录序列按关键字逆序或基本有逆序时,快速排序则蜕化为冒泡排序,此时,算法的时间复杂度为)(2N O 。 从时间上看快速排序平均性能优于书上的各种排序吧,但是也存在缺点,上面提到的双倍快速排序和基于归并的快速排序刚好弥补了这一瑕疵,从中找到不足使快排更快。 同时经过这次课程设计我们领略到分工合作精神,集体编程和个人编程有很大不同,相互间独立而又紧密联系在一起,我们每个人都是独立的个体,我们可以独立的查找资料来独立的编辑一个程序,然而我们也可以互相研讨而一起完成一个程序。相对来说独立的个人毕竟一个人的能力是有限的,而通过团队合作我们就可很好的完成一个人完成起来比较困难的工作,这种合作就使得我们的设计相对来说轻松了不少。 这次的课程设计不仅仅是使我们有了上面的收获,同时也使我们深刻的认识到自己专业知识的匮乏,以及独立完成程序设计的能力上的不足。通过这次的课程设计,我们认识到了自我的不足还有很多,因此我们在接下来的时间里将会更加努力的去学习和应用,以提高自身的专业技能和实际能力,自强自己在未来社会工作中的竞争力。这次课程设计我主要的任务是编写快速程序,有好多函数以前不懂不会编但在组长的帮助下我调试了并能正确运行。 决定程序成功与否的往往不是程序的整体思路和整体算法,而是细节,细节错误往往是程序失败的终级杀手,我在此次课程设计中深有体会。同时我也认识到编译工具的重要性,我用的是VC++环境,提高我编写程序的效率,如按回车后,它能自动空出一定距离,体现出程序的结构,不用人工输入空格、制表符,还有它的调试功能,不用我人工输出中间变量来查错,就能看到中间变量。