Files
2026-03-22 17:41:18 +08:00

110 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
事实上STL的sort函数在上一题基础版的基础上还采用了下列优化手段以进一步提升快速排序算法的效率。
3“三数取中”选基准元素。不是选取第一个元素作为基准元素而是在当前子数组中选取3个元素取中间大的那个元素作为基准元素。从而保证选出的基准元素不是子数组的最小元素也不是最大元素避免Partition分到子数组最边上以降低最坏情况发生的概率。
为确保本题答案唯一本算法的实现请以教材为准即普林斯顿大学Sedgewick教授给出的方法若当前子数组是R[m]…R[n]选取R[m]、R[(m+n)/2]和R[n]的中位数作为基准元素。先将R[(m+n)/2]与R[m+1]交换若R[m+1]比R[n]大交换二者若R[m]比R[n]大交换二者若R[m+1]比R[m]大,交换二者。
4尾递归转为循环。即将传统快速排序代码
void QuickSort(int R[],int m,int n){
if(n - m + 1 > threshold){
int j = Partition(R, m, n);
QuickSort(R, m, j-1); //递归处理左区间
QuickSort(R, j+1, n); //递归处理右区间,尾递归
}
}
转换为
void QuickSort(int R[],int m,int n){
while(n - m + 1 > threshold){ //注意此处不是if而是while
int j = Partition(R, m, n);
QuickSort(R, m, j-1); //递归处理左区间
m = j+1; //通过while循环处理右区间从而消除尾递归
}
}
即先递归处理左区间,后循环处理右区间,从而消除一个尾递归,以减少递归调用带来的时空消耗。
这里需注意尾递归转循环后转入堆排序的时机不仅仅是递归深度达到2logn而是递归深度和while循环迭代的次数加一起达到2logn时转入堆排序。
5优先处理短区间。在上述策略4的基础上进一步改进不是按固定次序处理左右子区间每次都先处理左区间、后处理右区间而是先通过递归处理左右两个子区间中“较短的那个区间”然后再通过循环处理两个子区间中“较长的那个区间”。从而使每次递归处理的子数组长度至少缩减一半使最坏情况下递归深度算法最坏情况空间复杂度为logn。
6三路分划3-Way Partition。当重复元素很多时传统快速排序效率较低。可修改Partition操作不是把当前数组划分为两部分而是三部分小于基准元素K的元素放在左边等于K的元素放在中间大于K的元素在右边。接下来仅需对小于K的左半部分子数组和大于K的右半部分子数组进行排序。中间等于K的所有元素都已就位无需处理。
为确保本题答案唯一此处请采用如下做法若当前子数组是R[m]…R[n]可设置3个指针前指针i中指针j后指针k。初始时i和j指向第一个元素k指向最后一个元素指针j从左往右扫描数组
若R[j]小于基准元素交换R[j]和R[i], i++, j++
若R[j]大于基准元素交换R[j]和R[k], k--
若R[j]等于基准元素j++
通过指针j的遍历使小于基准元素的元素换到当前子数组左侧大于基准元素的元素换到右侧。当j扫描完当前子数组后R[m]…R[i-1]即小于基准元素的元素R[i]…R[k]即等于基准元素的元素R[k+1]…R[n]即大于基准元素的元素。
在本题中请你在之前实现的STL sort()初级版的基础上,进一步实现上述优化策略。
提示本题只需把原来的Partition改为3-way Partition并使用“三数取中”法选择基准元素。
在快速排序函数里,改为“尾递归转循环 + 先处理短区间”。你可以在Visual Studio里对sort函数右键点击“转到定义”查看VS中STL的sort()实现细节
vstudio sort转到定义图片.jpg
函数接口定义:
void sort(int *R, int n);
功能为对整数R[1]…R[n]递增排序。
裁判测试程序样例:
#include<iostream>
#include<stdlib.h>
#include<math.h>
using namespace std;
int threshold;
/* 请在这里补充你的代码即你所实现的sort函数 */
int main()
{
int n,i;
int a[50010];
scanf("%d %d", &n, &threshold);
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a,n);
printf("Final:");
for (i = 1; i <= n; i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
备注提交代码时只需提交sort函数以及你自定义的其他函数不用提交#include或者main函数等内容
输入格式:
输入第一行为2个正整数n和thresholdn为待排序的元素个数不超过50000threshold为改用插入排序的阈值不超过20含义如上所述。第二行为n个空格间隔的整数。本题中读入数据的操作无需你来实现而由框架程序完成。
输出格式:
输出第一行为以depth_limit:开头的整数表示转为堆排序的递归深度即⌊2log
2
n⌋。从第二行开始输出对某子数组转为堆排序后该子数组初始建堆的结果每个元素后一个空格每个堆占一行以Heap:开头。注意可能不止一个堆。接下来下一行输出n个整数每个整数后一个空格为快速排序所有递归退出后插入排序执行前的数组元素以Intermediate:开头。最后一行为n整数每个整数后一个空格表示排序后的数组以Final:开头(最后一行由框架程序完成,无需你来输出)。
输入样例1
10 2
10 9 8 7 6 5 4 3 2 1
输出样例1
depth_limit:6
Intermediate:1 2 3 5 4 6 7 8 9 10
Final:1 2 3 4 5 6 7 8 9 10
输入样例2
60 2
66 61 92 22 50 80 39 2 25 60 49 17 37 19 24 57 40 82 11 52 45 0 33 78 32 25 19 42 92 50 39 87 74 87 56 79 63 63 80 83 50 3 87 2 91 77 87 10 59 23 25 6 49 85 9 95 60 16 28 1
输出样例2
depth_limit:11
Intermediate:1 0 2 2 3 6 9 10 11 16 17 19 19 22 23 24 25 25 25 32 28 33 37 39 39 40 42 45 49 49 50 50 50 52 56 57 59 60 60 61 63 63 66 74 77 78 79 80 80 82 83 85 87 87 87 87 91 92 92 95
Final:0 1 2 2 3 6 9 10 11 16 17 19 19 22 23 24 25 25 25 28 32 33 37 39 39 40 42 45 49 49 50 50 50 52 56 57 59 60 60 61 63 63 66 74 77 78 79 80 80 82 83 85 87 87 87 87 91 92 92 95
Code Size Limit
16 KB
Time Limit
100 ms
Memory Limit
64 MB