AcWing785.快速排序
题目描述
785. 快速排序 - AcWing题库
运行代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6+6;
int q[N];
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int m = l + r >> 1;//>>1是位运算,等价于除以2
nth_element(q + l, q + m, q + r);//nth_element用于快速选择中位数
quick_sort(q, l, m);
quick_sort(q, m + 1, r);
}
int main()
{
int n;
cin>>n;
for (int i = 0; i < n; i ++ )
cin>>q[i];
quick_sort(q, 0, n);
for (int i = 0; i < n; i ++ )
cout<<q[i]<<" ";
return 0;
}
代码思路
通常的快速排序算法会选择一个"pivot"(基准元素),然后将数组分为两部分:一部分是小于基准的元素,另一部分是大于基准的元素。选择使用std::nth_element
函数来直接找到分区中的中位数(或确切地说,是第m
个元素,其中m = (l + r) / 2
),并将这个元素放到正确的位置,从而达到部分排序的目的。这种方法相较于传统的选取首个元素或随机选取元素作为基准,可能在某些数据分布下有更好的性能表现。
代码解析
-
#include
指令和命名空间: 引入必要的头文件<iostream>
和<algorithm>
,并使用std
命名空间。常量定义:const int N = 1e6+6;
定义了一个足够大的数组大小,用于存放至多10^6个整数。 -
quick_sort
函数: 实现了快速排序的递归逻辑。- 输入参数: 整型数组的起始指针
q
, 左边界索引l
, 右边界索引r
。 - 基础情况: 当左边界等于或大于右边界时,递归结束。
- 选择中位数: 使用
nth_element
将数组的中位数放到正确的位置。这一步代替了传统快速排序中选择基准元素的过程。 - 递归调用: 分别对中位数左边和右边的子序列进行递归排序。
- 输入参数: 整型数组的起始指针
-
main
函数:读取数组: 从标准输入读取整数n,表示数组长度,然后读取n个整数到数组q
中。排序数组: 调用quick_sort
函数对数组进行排序。输出结果: 将排序后的数组元素输出到标准输出。
改进空间
-
避免全局变量:尽量不要使用全局变量,特别是数组
q[N]
。这会影响代码的可重用性和模块化。可以将数组作为参数传递给quick_sort
函数,同时考虑使用std::vector<int>
来自动管理内存,这样可以更灵活地处理不同大小的数据集。 -
异常处理和输入验证:增加对输入的验证,例如检查
n
是否在合理范围内,防止数组越界。同时,可以考虑加入异常处理机制,提升程序的健壮性。 -
优化递归调用:虽然
nth_element
的使用简化了代码,但直接在快速排序中使用可能不是最高效的,因为它不提供两边的分割点,可能导致递归深度较大。传统快速排序中选择枢轴的方式(如三数取中法)可能在多数情况下提供更好的性能。 -
尾递归优化:虽然C++标准并未规定编译器必须执行尾递归优化,但在递归调用中,可以考虑调整逻辑,使其更接近尾递归形式,尽管现代编译器对于递归深度的优化已经做得很好,但良好的编程习惯仍值得提倡。
-
使用迭代而非递归(可选):对于非常大的数据集,递归可能导致栈溢出。虽然这不是常见的问题,但作为一种优化手段,可以考虑将递归逻辑转换为循环迭代,使用栈来手动管理递归状态。
-
添加函数注释和文档:代码的可读性和可维护性可以通过添加函数注释和总体说明来提升,使得其他阅读者更容易理解代码逻辑。
改进代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void quick_sort(vector<int>& nums, int l, int r) {
if (l >= r) return;
int mid = l + (r - l) / 2;
nth_element(nums.begin() + l, nums.begin() + mid, nums.begin() + r + 1);
quick_sort(nums, l, mid);
quick_sort(nums, mid + 1, r);
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
quick_sort(nums, 0, n - 1);
for (const auto& num : nums) {
cout << num << " ";
}
return 0;
}
使用vector<int>
替代全局数组,并对输入数据进行了封装,提高了代码的灵活性和可维护性。
上述代码运行时间太长了
提供几种更快的运行代码
代码另解
#include<algorithm>
using namespace std;
int main()
{
int n, a[100000];
int i;
scanf("%d",&n);
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
sort(a, a + n);
for (i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
}
-
读取数组元素:
scanf("%d",&n);
读取用户输入的数组长度n
。接下来的循环for (i = 0; i < n; i++)
读取用户输入的每个整数,并存储到数组a
中。scanf("%d", &a[i]);
实现了这一操作。 -
排序数组:
sort(a, a + n);
使用了STL中的sort
函数对数组a
进行排序。a
和a + n
作为参数,分别表示待排序数组的起始地址和结束地址(不含结束地址),这会按照升序排列数组中的元素。 -
输出排序后的数组:通过循环
for (i = 0; i < n; i++)
遍历排序后的数组,并使用printf("%d ", a[i]);
输出每个元素。注意,每个数字后面都有一个空格,以区分不同的元素 。