快排:
1、设定一个中间值 q[ l+r >>1 ] , 让左右区间来比较
2、左边通过 i++ 依次比较,如果比这个中间值小,就继续++ , 直到不符合
3、右边通过 j-- 依次比较,如果比这个中间值大,就继续++ ,直到不符合
4、两边指针都停止,代表此时左边此时的值,是大的。右边此时的值,是小的。让他们2个交换
5、当左边指针 i 走到了 右边指针J 的区间中,这时候我们要重新划定sort范围
也就是再来一次quicksort() , 只不过这一次区间不一样
- 左子区间
[l, j]
:包含所有 ≤x
的元素,且已经与右子区间分离(左子区间的最大值 ≤ 右子区间的最小值)。 - 右子区间
[j+1, r]
:包含所有 ≥x
的元素,同理与左子区间分离。
递归处理这两个子区间的目的是:`
- 让每个子区间内部的元素继续排序(通过同样的分区逻辑)。
- 由于子区间已经与原区间的其他部分 “分离”(满足左 ≤ 右),最终整个数组会自然有序。
两个递归的意义:
我进入了一个区间中,然后执行这个区间内的左右两边的排序:
quicksort(q,l,j);
quicksort(q,j+1,r);
第一个快排一直在递归,直到触发了返回条件,此时执行第二个快排quicksort(q,j+1,r);
注意,此时的边界参数 是最底层递归的参数,现在是在一个极小的区间中进行排序
排序完了以后,我这个大的quicksort()就结束了,然后执行我的上一层的区间,也就是他的右边的排序quicksort(q,j+1,r);
这样一路往上回溯,从2个小区间开始排序上去,最终就是左右区间都有序,且达成左边一直小于右边
1、其实关键是要记住:递归的“每一步”不是“顺序执行完左再执行右”,而是“先把左拆到最小,解决完左再回头拆右”,可以用“拆快递”的例子把这个过程拆透(就用数组 [3,1,4,2],基准选第一个数 3):
2、第一步:先拆“左区间”,拆到不能再拆:
原数组分区后:左区间 [1,2](比3小)、基准3(已归位)、右区间 [4](比3大)
此时递归会先处理 左区间 [1,2],暂时“搁置”右区间 [4]:1. 对 [1,2] 选基准1,分区后:左区间空(跳出条件)、基准1(归位)、右区间 [2]
2. 接着处理 [1,2] 的右区间 [2]:这是只有1个元素的子数组(满足跳出条件),直接返回
3. 到这里,左区间 [1,2] 已经全部排好(变成 [1,2]),“左半部分”彻底解决
3、第二步:回头处理之前“搁置”的“右区间”:
左区间解决完后,才会回头处理最开始搁置的 右区间 [4]:
右区间 [4] 只有1个元素(满足跳出条件),直接返回,“右半部分”也解决
4、第三步:拼起来就是最终结果
左区间 [1,2] + 基准3 + 右区间 [4] → 最终有序数组 [1,2,3,4]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>//快排
void quicksort(int q[],int l,int r)
{if(l>=r) return ;int temp;int i = l-1;int j = r+1;int x = q[l+r <<1];while(i<j){do i++ ; while(q[i]<x);do j-- ; while(q[j]>x);if(i<j){temp = q[i];q[i] = q[j];q[j] = temp;}}/*递归处理这两个子区间的目的是:让每个子区间内部的元素继续排序(通过同样的分区逻辑)。由于子区间已经与原区间的其他部分 “分离”(满足左 ≤ 右),最终整个数组会自然有序。*/quicksort(q,l,j);quicksort(q,j+1,r);} int main()
{int q[5] = {133,5020,10122,30,455};int len = sizeof(q) / sizeof(q[0]);quicksort(q,0,len-1);for(int i = 0;i<len;i++){printf("%d ",q[i]);}return 0;
}