快速排序 Quick Sort
快速排序的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
一趟快速排序(或一次划分)的过程如下:首先任意选取一个记录(通常可选第一个记录)作为枢轴(或支点)(pivot),然后按下列原则重新排列其余记录:将所有关键字比它小的记录都安置在它的位置之前,将所有关键字比它大的记录都安置在它的位置之后。
经过一趟快速排序之后,以该枢轴记录最后所落的位置i作分界线,将序列分割成两个子序列,之后再分别对分割所得的两个子序列进行快速排序。
可以看出这个算法可以递归实现,可以用一个函数来实现划分,并返回分界位置。然后不断地这么分下去直到排序完成,可以看出函数的输入参数需要提供序列的首尾位置。
快速排序的实现
划分实现1 (枢轴跳来跳去法)
一趟快速排序的实现:设两个指针low和high,设枢轴记录的关键字为pivotkey,则首先从high所指位置起向前搜索找到第一个关键字小于pivotkey的记录和枢轴记录互相交换,然后从low所指位置起向后搜索,找到第一个关键字大于pivotkey的记录和枢轴记录互相交换,重复这两步直至low==high为止。
下面的代码例子元素类型为int,并且关键字就是其本身。
typedef int ElemType; int Patition(ElemType A[], int low, int high) { ElemType pivotkey=A[low]; ElemType temp; while(low<high) { while(low <high && A[high]>=pivotkey) { --high; } temp=A[high]; A[high]=A[low]; A[low]=temp; while(low<high && A[low]<=pivotkey) { ++low; } temp=A[high]; A[high]=A[low]; A[low]=temp; } return low; }
划分实现2 (枢轴一次到位法)
从上面的实现可以看出,枢轴元素(即最开始选的“中间”元素(其实往往是拿第一个元素作为“中间”元素))在上面的实现方法中需要不断地和其他元素交换位置,而每交换一次位置实际上需要三次赋值操作。
实际上,只有最后low=high的位置才是枢轴元素的最终位置,所以可以先将枢轴元素保存起来,排序过程中只作元素的单向移动,直至一趟排序结束后再将枢轴元素移至正确的位置上。
代码如下:
int Patition(ElemType A[], int low, int high) { ElemType pivotkey=A[low]; ElemType temp = A[low]; while(low<high) { while(low <high && A[high]>=pivotkey) { --high; } A[low]=A[high]; while(low<high && A[low]<=pivotkey) { ++low; } A[high]=A[low]; } A[low] = temp; return low; }
可以看到减少了每次交换元素都要进行的三个赋值操作,变成了一个赋值操作。
细节就是每次覆盖掉的元素都已经在上次保存过了,所以不必担心,而第一次覆盖掉的元素就是枢轴元素,最后覆盖在了它应该处于的位置。
递归形式的快速排序算法
void QuickSort(ElemType A[], int low, int high) { if(low<high) { int pivotloc=Patition(A,low, high); QuickSort(A, low, pivotloc-1); QuickSort(A, pivotloc+1, high); } }
不管划分是上面哪一种实现,都可以用这个递归形式进行快速排序。
需要注意的是这个if语句不能少,不然没法停止,会导致堆栈溢出的异常。
快速排序的性能分析
时间复杂度
快速排序的平均时间为Tavg(n)=knln(n),其中n为待排序列中记录的个数,k为某个常数,在所有同数量级的先进的排序算法中,快速排序的常数因子k最小。
因此,就平均性能而言,快速排序是目前被认为是最好的一种内部排序方法。通常认为快速排序在平均情况下的时间复杂度为O(nlogn)。
但是,快速排序也不是完美的。
若初始记录序列按关键字有序或基本有序,快速排序将蜕化为冒泡排序,其时间复杂度为O(n2)。
原因:因为每次的枢轴都选择第一个元素,在有序的情况下,性能就蜕化了。
如下图:
快速排序的空间利用情况
从空间上看,快速排序需要一个栈空间来实现递归。
若每一趟排序都将记录序列分割成长度相接近的两个子序列,则栈的最大深度为log2n+1(包括最外层参量进栈);但是,若每趟排序之后,枢轴位置均偏向子序列的一端,则为最坏情况,栈的最大深度为n。
如果在一趟划分之后比较分割所得两部分的长度,且先对长度短的子序列中的记录进行快速排序,则栈的最大深度可降为O(logn)。
性能改善
为改进快速排序算法,随机选取界点或最左、最右、中间三个元素中的值处于中间的作为界点,通常可以避免原始序列有序的最坏情况。
然而,即使如此,也不能使快速排序在待排记录序列已按关键字有序的情况下达到O(n)的时间复杂度(冒泡排序可以达到)。
为此,可以如下修改划分算法:在指针high减去1和low增加1的同时进行“起泡”操作,即在相邻两个记录处于“逆序”时进行互换,同时在算法中附设两个布尔型变量分别指示指针low和high在从两端向中间移动的过程中是否进行过交换记录的操作,若没有,则不需要对低端或高端子表进行排序,这将进一步改善快速排序的平均性能。
另外,将递归算法改为非递归算法,也将加快速度,因为避免了进出栈和恢复断点等工作。
相关推荐
python编写 快速排序 Quick Sort
python 一行代码实现的快速排序 quick sort
快速排序是一种高效的、分治策略的排序算法,由C.A.R. Hoare在1960年提出。在Python中实现快速排序,我们通常利用递归来分解问题,然后合并结果。快速排序的基本思想是通过一趟排序将待排记录分隔成独立的两部分,...
### 快速排序(Quick Sort) #### 算法原理 快速排序是一种高效的排序算法,其基本思想是采用分治法(divide and conquer)来解决问题。对于待排序的数组A[0]...A[N-1],快速排序通过选择一个基准元素(pivot),通常...
快速排序(Quick Sort)是一种高效的排序算法,由C. A. R. Hoare在1960年提出。它采用分治法(Divide and Conquer)的策略来把一个序列分为较小和较大的两个子序列,然后递归地排序两个子序列。快速排序在实现时,...
数据结构,排序算法,快速排序算法的C语言实现, quick sort C qsort.c an c implementation of quick sort
快速排序(Quick Sort)源码及运行示例
快速排序(Quick Sort)作者原版论文,快速排序的作者C.A.R Hoare 发表的原著论文。
快速排序是一种高效的排序算法,由英国计算机科学家C.A.R. Hoare在1960年提出。它基于分治法的策略,通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此...
快速排序是一种高效的排序算法,由英国计算机科学家C.A.R. Hoare在1960年提出。它的基本思想是分治法(Divide ...在`quick_sort.py`文件中,你应该能找到一个具体的快速排序实现,可以作为学习和理解快速排序的参考。
快速排序是一种高效的排序算法,由英国计算机科学家C.A.R. Hoare在1960年提出。它的基本思想是分治法(Divide and Conquer)的应用,将一个大问题分解为若干个小问题来解决。在快速排序中,我们将一个数组分为两个子...
c++快排(快速排序)的源代码。代码简洁明了。让您知道理解快排。 用一个函数解决快排问题。 请您赶快来下载吧!
C#,双向链表(Doubly Linked List)快速排序(Quick Sort)算法与源代码。双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始...
鉴于初学C语言或C++时对快速排序算法的了解不够深入,在此上传快速排序的C语言实现代码,该实现代码具有模块化特点,并且在代码中写了注释,并在调试过程中易出错的关键地方做了标注;此外,在代码实现中添加了良好...
在B站讲快速排序的笔记,需要的同学可以免费下载
2. **快速排序函数** (`quick_sort`) ```cpp void quick_sort(int *a, int left, int right) { int m; if (left ) { m = huafen(a, left, right); quick_sort(a, left, m - 1); quick_sort(a, m + 1, right)...
各种数据结构、算法及实用的C#源代码.C#,单向链表(Simply Linked List)快速排序(Quick Sort)算法与源代码.单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部...
在`GF_quick_Sort`这个库文件中,我们可以将快速排序的具体实现封装起来,只暴露输入和输出接口,使得其他程序调用时无需关心内部实现细节。 在实际应用中,我们可能需要对不同类型的数值进行排序,例如整型、实型...
在这个名为"Quick sort Analysis.zip"的压缩包中,重点是分析快速排序的确定性与随机化实现。确定性快速排序通常是指每次选取固定的基准元素,如选择第一个或最后一个元素,这样对于相同的输入,排序过程完全可预测...