快速排序
1.概念
快速排序算法是对冒泡排序算法的一种改进算法,在当前所有内部排序算法中,快速排序算法被认为是最好的排序算法之一。
2.基本思想
快速排序的基本思想: 通过一趟排序将待排序的序列分割为左右两个子序列,左边的子序列中所有数据都比右边子序列中的数据小,然后对左右两个子序列继续进行排序,直到整个序列有序。
例如我们对以下的数字进行排序:
5 4 2 3 1 6
我们首先先选择一个中间数字:例如可以选择 2(2 是这串数字的物理上 的中间数字,当然也可以选择其他的数字,另外我们只是选择了这个数字的值作为中间数,并不是将它取出来)
然后把小于 2 的数字放到左边,大于 2 的数字放到右边。等于 2 的数字放到那边都可以。
我们只需要找到 最左边大于等于 2 的数,找到最右边小于等于 2 的数,然后交换它们的位置即可。(两者都可以选择等于 2 的数,是因为这样可以更好的判断结束的条件,即最左边大于等于 2 的数和最右边小于等于 2 的数重合的时候)
对 5 4 2 3 1 6
进行排序,中间数字选 2
5 4 2 3 1 6
最左边大于等于 2 的数是 5,最右边小于等于 2 的数是 1,交换两者
1 4 2 3 5 6
最左边大于等于 2 的数是 4,最右边小于等于 2 的数是 2,交换两者
1 2 4 3 5 6
最左边大于等于 2 的数是 2,最右边小于等于 2 的数字是 2,两者重合,结束
对 1 2 4 3 5 6
分析我们可以发现,这行数字被分为了两个部分,a: 1 2
和 b: 4 3 5 6
,我们可以看到左边的数字都比右边的数字小,现在我们只要对这两个部分分别进行排序,那么这行数字就排序成功了。这就是分治的思想。
对 a: 1 2
进行排序。中间数字选 1
1 2
结束,分为两个部分 aa: 1
ab: 2
对 b: 4 3 5 6
进行排序。中间数字选 3
4 3 5 6
最左边大于等于 3 的数是 4,最右边小于等于 3 的数是 3,交换两者
3 4 5 6
最左边大于等于 3 的数和最右边小于等于 3 的数字都是 3,两者重合,结束,分为两个部分 ba: 3
bb: 4 5 6
aa: 1
ab: 2
ba: 3
bb: 4 5 6
aa、ab 和 ba 都只有一个数字了,不需要再次进行排序
对 bb: 4 5 6
进行排序。中间数字选 5
4 5 6
最左边大于等于 5 的数和最右边小于等于 5 的数字都是 5,两者重合,结束,分为两个部分 bba: 4 5
bbb: 6
aa: 1
ab: 2
ba: 3
bba: 4 5
bbb: 6
bbb 只有一个数字了,不需要再次进行排序
对 bba: 4 5
进行排序。中间数字选 4
4 5
最左边大于等于 4 的数和最右边小于等于 4 的数字是 4,两者重合,结束,分为两个部分 bbaa: 4
bbab: 5
aa: 1
ab: 2
ba: 3
bbaa: 4
bbab: 5
bbb: 6
全部排序成功,最后结果是:1 2 3 4 5 6
,排序成功。
3.代码实现
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int q[N];
void quick_sort(int* arr, int l, int r) {
if (l >= r) return; //递归结束条件,即只有一个数字了
int i = l - 1, j = r + 1, x = arr[(l + r) >> 1]; //-1和+1是为了确保do...while循环顺利进行,x取中间值最好
while (i < j) {
do i ++ ; while (arr[i] < x); //找到左边第一个大于等于x的值(从左往右)
do j -- ; while (arr[j] > x); //找到右边第一个小于等于x的值(从右往左),以上为确定排序顺序的条件
if (i < j) swap(arr[i], arr[j]); //交换两者
}
quick_sort(arr, l, j); //把左边的排序
quick_sort(arr, j + 1, r); //把右边的排序
}
//需要注意的是,每次递归都可以确定两个部分的顺序之分,可以根据这个特点,来求解第k大的数等问题
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 ++ ) {
printf("%d ", q[i]);
}
return 0;
}