1. 为什么蓝桥杯要背手写排序模板:就算有现成API也不能直接躺平
先聊一个很多备赛同学都会问的问题:蓝桥杯又不是ACM,Java有 Arrays.sort(),C++有 sort(),Python有 sorted(),为什么还要花时间手写快速排序、归并排序和桶排序?这不是脱了裤子放屁吗?
这个问题我当年备赛的时候也纠结过,直到真正上了考场才明白。先说结论:蓝桥杯省赛的填空题和部分编程大题,确实可以直接调API,但竞赛的分数差距从来不在"能不能排序",而在"排序之外你能多做什么"。
拿归并排序举例,Arrays.sort() 能帮你排好序,但它不会告诉你数组里有多少个逆序对。而"求逆序对数量"这个考点,蓝桥杯国赛和省赛都出过,标准解法就是归并排序的分治过程里顺手统计。你调API,排序是排完了,但逆序对一个都数不出来——这时候手写模板的价值就体现出来了。
另外还有一个非常现实的原因:蓝桥杯的评测机和平时写业务代码不一样,数据规模大了以后,API排序的底层实现不一定是你想要的。比如Java的 Arrays.sort() 对基本类型用的是双轴快速排序,对对象类型用的是TimSort,C++的 sort() 在数据量小的时候会退化成插入排序。这些底层策略本身没错,但你要想针对特定数据做优化,比如对一个几乎有序的数组做排序,手写插入排序的改良版反而更快。模板的意义不是替代API,而是让你在API之外有第二套、第三套手段。
更关键的是,手写排序是理解算法复杂度的最佳途径。很多人背了"快排平均O(n log n)"这句话,但不知道为什么快排在有序数组上会退化到O(n²)。当你亲手写出快排的partition代码,看着那两个指针在数组上移动,你会瞬间理解为什么枢轴选择那么重要。这种理解,是套API永远学不来的。
所以这篇文章不是让你放弃API,而是给你一套可以应对蓝桥杯各种变形考法的手写排序模板。一共三个:快速排序、归并排序、桶排序。每个我都会给出可以直接背诵的核心代码,同时把"为什么这样写"讲透。等你背熟了,遇到逆序对、第K大、区间排序这类变形题,你脑海里会自然浮现这些模板,然后在考场上比其他同学少花一半时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 快速排序的三个致命细节:从模板到翻车现场
快速排序大概是所有排序算法里"看起来最简单、写起来最容易翻车"的一个。网上随便一搜能搜到十几种写法,但很多写法在蓝桥杯的评测数据下会超时或者栈溢出。我这里给出一套经过大量测试、适合竞赛场景的模板,然后重点讲三个容易翻车的细节。
2.1 竞赛级快排模板(Java/C++双版本)
先看Java版本:
java复制public static void quickSort(int[] arr, int left, int right) {
if (left >= right) return;
int i = left, j = right;
int pivot = arr[(left + right) >>> 1]; // 取中点作为枢轴,避免有序数组退化
while (i <= j) {
while (arr[i] < pivot) i++;
while (arr[j] > pivot) j--;
if (i <= j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
quickSort(arr, left, j);
quickSort(arr, i, right);
}
再看C++版本:
cpp复制void quickSort(int arr[], int left, int right) {
if (left >= right) return;
int i = left, j = right;
int pivot = arr[(left + right) / 2];
while (i <= j) {
while (arr[i] < pivot) i++;
while (arr[j] > pivot) j--;
if (i <= j) {
swap(arr[i], arr[j]);
i++;
j--;
}
}
quickSort(arr, left, j);
quickSort(arr, i, right);
}
这里用的是双指针双向扫描的写法,不是很多教材上那种单向扫描的Lomuto分区。为什么竞赛都用双向扫描?因为Lomuto分区每次partition只交换一个元素,遇到大量重复元素时性能很差;双向扫描遇到等于pivot的元素会停下来交换,可以自动处理重复元素,实测在蓝桥杯那种包含大量重复数据的测试用例下更稳。
2.2 致命细节一:枢轴选不好,快排就是慢排
这是快排最容易踩的坑。如果每次都用 arr[left] 作为枢轴,而数组本身已经有序(比如题目输入一个升序数组),那么每次partition后数组并没有被平分,左边的递归区间是空的,右边是n-1个元素。这样递归深度会变成n,时间复杂度退化到O(n²),蓝桥杯的数据量到了10^5以上就会超时。
解决手段有两个:
- 取中点作为枢轴:`pivot = arr[(left
