快速排序的学习
快速排序
思想:
不同于归并,快速排序的本质是分治,且一直分,分到最小。即通过递归选中间值x将待排序的内容分为左右两个待排序区域。一直递归排序,将左边的都变成小于等于x的,右边的值都大于等于x。一直分分分到最小————即左右只有一个值。
代码如下
#include<iostream>
using namespace std;
const int N = 100010;
int q[N]; // 用于存储待排序的数组
void quick_sort(int q[], int l, int r)
{
if (l >= r) return; // 递归终止条件:当左边界大于或等于右边界时,停止排序
int i = l - 1, j = r + 1, x = q[(r + l) / 2]; // i 和 j 为左右指针,x 为基准值(这里选取中间元素)
while (i < j)
{
// 在 i 位置向右寻找第一个大于等于基准值的元素
do i++; while (q[i] < x);
// 在 j 位置向左寻找第一个小于等于基准值的元素
do j--; while (q[j] > x);
if (i < j) swap(q[i], q[j]); // 如果 i 小于 j,交换这两个元素
}
// 对左半部分进行递归排序
quick_sort(q, l, j);
// 对右半部分进行递归排序
quick_sort(q, j + 1, r);
}
int main()
{
int n;
cin >> n; // 输入数组的大小
for (int i = 0; i < n; i++) cin >> q[i]; // 输入待排序的元素
// 调用快速排序算法
quick_sort(q, 0, n - 1);
// 输出排序后的数组
for (int i = 0; i < n; i++) cout << q[i] << " ";
return 0;
}
平均时间复杂度:O(n log n),这是快速排序最常见的情况。
最坏时间复杂度:O(n^2),发生在数组已经有序或者基准元素总是选得不好时。为避免这种情况,通常可以随机选择基准元素或使用三数取中法。
空间复杂度:O(log n),这是由于递归调用栈的空间开销。
举个例子说明:
假设我们有一个数组:[10, 7, 8, 9, 1, 5],我们选择基准值为 8,并执行分区操作:
1.初始状态:[10, 7, 8, 9, 1, 5],选择基准值 8。
2.左指针 i 从左往右扫描,找到 10,右指针 j 从右往左扫描,找到 5。
3.交换 10 和 5,得到:[5, 7, 8, 9, 1, 10]。
4.继续扫描,交换 9 和 7,得到:[5, 7, 8, 9, 1, 10]。
5.继续分区操作直到指针相遇,最后,数组分成 [5, 7, 8] 和 [9, 10],基准值 8 被放到了正确的位置。
然后我们递归地对 [5, 7] 和 [9, 10] 继续排序。