快速排序
快速排序算法运行时间取决于输入,从输入数据项的线性关系到平方关系不等。
如果一个应用程序不能确定使用快速排序算法是否正确,那么它最好使用shell排序,因为shell排序在实现时不需要太多分析。然而对于大文件,快速排序算法的性能是shell排序的5倍到10倍,它还可以更有效处理其他类型文件。
快速排序算法是一种分治的排序方法。它将数组分成两个部分,然后分别对两个部分进行排序。划分的位置决定于输入文件中元素的初始位置。关键在于划分的过程,它遵循以下三条性质对数组进行重组:
1)a[i]应该位于它最终在数组中的某个位置上;
2)a[1], ..., a[i-1]中元素都比a[i]小;
3)a[i+1], ..., a[r]中元素都比a[i]大;
我们通过划分来完成排序,然后递归处理子文件。
我们使用一般的策略来实现划分。首先,我们选择a[r]作为划分元素——划分后位于最终应该在的位置上的元素。然后,我们从数组的最左边向中间扫描数据,直到找到一个比划分元素大的元素;同时,从最右边向中间扫描数据,直到找到一个比划分元素小的元素。显然,是扫描停止的两个元素的位置与它们的大小关系相反,于是交换这两个元素。继续这样的过程,我们就可以保证数组中位于左侧指针左侧元素都比划分元素小,位于右侧指针右侧的元素都比划分元素大。当扫描指针相遇时,我们需要做的就是完成划分过程,将a[r]与右侧子文件的最左端元素交换(左指针所指向的元素)。
1 template <class Item> 2 void quicksort(Item a[], int l, int r) 3 { 4 if (r <= l) return; 5 int i = partition(a, l, r); 6 quicksort(a, l, i-1); 7 quicksort(a, i+1, r); 8 } 9 10 template <class Item> 11 int partition(Item a[], int l, int r) 12 { 13 int i = l-1, j = r; Item v = a[r]; 14 for (;;) 15 { 16 while (a[++i] < v); 17 while (v < a[--j]) if (j == l) break; 18 if (i >= j) break; 19 exch(a[i], a[j]); 20 } 21 exch(a[i], a[r]); 22 return i; 23 }
注:快速排序算法的递归实现中程序多次因为小的子文件而调用自身,这使得我们可以对小的子文件尽可能使用其他好的排序方法进行速度改善。一个明显的方法就是将递归调用过程前面的return语句改为一个插入排序,如: if (r-l <= M) insertion(a, l, r)。
无论何时我们处理一个递推算法,这一技术都会带来很多好处。因为它很接近本质,我们可以保证所有递归算法在执行小的问题时效率很高;我们通常对算法中那些小的部分采取一些激进的算法以保证整个算法效率的提高。
一种比上述调用插入排序更有效的解决这类小的子文件的方法,就是将前面的判断改为:if(r-l <= M) return;
就是我们忽略那些在划分过程中的小子文件。在一个非递归的实现中就是不将大小小于等于M的任何文件压入栈中。在划分之后,得到的就是几乎排好序的文件。最后再对这种几乎排好序的文件用插入排序来处理。因为插入排序特别适合处理这类几乎排好序的文件。
进一步改善快速排序算法可以从划分的位置入手,就是从文件中取出三个元素,使用三个元素的中间元素作为划分元素。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?