如果单纯要排序的话,可以用sort,归并也更加的好懂,但是快排有思想
可能是被C++养废了,我竟然不会快排了。
马上就要csp了。。。。
于是我想垂死挣扎一下。
ps:洛谷特别贴心的专门给了一道题作为快速排序的模板,题解里也有许多dalao的分享,并附带各种优化,蒟蒻我只会基础
首先快排的思想基于分治(它并没有严格意义上二分),每次选择一个基准点,那么这个数列就可以分成两部分,使得左部分的数都比这个基准数要小,而右边都要大(以升序排序为例),就像是一个分类。那么如果左右两边分别有序的话这就是一个有序的数列了对吧(这显然我们可以递归处理)。
那么问题的关键就是如何达到这种将数分类的效果。/讲真我觉得你瞎搞一顿弄出来了,也差不多就是快排了,毕竟原理其实差不多是吧。(小声bb)
看网上说有两种方法,一是挖坑法,二是交换法(??)。
挖坑法
以升序排列为例
这里假设基准点是最左端的数
这个坑呢开始也在基准数所在的位置(相当于把基准数取了出来)
维护两个指针l,r。
l从序列最左端开始,r从序列最右端开始
个人理解,l是在审判这些点中是否有>基准数的,r是在审判这些点中是否有<基准数的
毕竟l相对在左边,r相对在右边嘛。
轮流进行,先从r开始如果r所指的数比基准数小,就停下来,这个数是不合法的,它应该到左边去,去哪里呢?我们发现我们有一个坑,于是它就愉快的跑去了坑里。这时候这个数原来所在的位置就空了出来,于是这个位置就变成了了一个新的坑。并且这时l所指向的点就是这个被填了的坑,它肯定合法,所以l右移(我觉得不移也没有事)
现在该轮到l移动了,它也会找到一个不合法的数,这个数应该要到右边去,恰好此时坑又在右边,于是它就去填坑了,它的位置有变成坑,以此类推。
现在留下的问题是,什么时候结束呢?
注意到,每次一个指针在查找到一个不合法的数后,这个不合法的数转移了位置,轮到另一个指针行动,但是这个指针还在之前那个不合法的位置上。假如另一个指针找不到不合法的数不断向它靠近,就会遇到它,这说明两边都没有不合法的数了,且现在两个指针共同在的地方就是最后的一个坑,这是留给基准数的。把基准数填进去即可。
然后以基准数所在位置为分界点,递归分别操作
这么做为什么可以?
可以发现每次r发现有比基准数小的数时,会将它填入坑中,这个坑就是l所在的地方,它不会使得l前面出现不合法的。所以最终比基准数小的数一定在l的前面,并且l会将所有比基准数大的都弄走。r也是同理
在最后,我们将基准数填入了最终的坑中,这个坑是l,r相交的地方,那么它的左边就不可能有比基准数的的数,右边也不可能有比它小的数(上一段所述)。所以以这个位置为分界点,就是合法的。
总结一下流程
- 选定基准数,并设它所在位置为坑
- r指针左移,直到找到一个不合法的数,将这个数填到坑里去,并让这个数的位置为坑,l右移一格。或r遇到l停止
- l指针右移,直到找到一个不合法的数,将这个数填到坑里去,并让这个数的位置为坑,r左移一格。或l遇到r停止
- 若l,r相遇,则将基准数填到l,r相遇的位置上(也是最后剩下的一个坑)。否则就继续2,3
- 以基准数最后所处位置为分界点两边分别处理。
然鹅。。。
观察数据发现,给的是升序序列,成功被卡,正确性应该没问题。
快排优化在于基准数的选择,但是这种方法基准数一定要选第一个,所以只要随机选择一个数,让它和第一个数交换即可(妙啊)
Code:
#include <bits/stdc++.h>
using namespace std;
int n,a[100010];
void Quick_Sort(int L,int R)
{
if (L>=R) return;
int val=a[L];
int l=L,r=R;
while (l!=r)
{
while (r!=l&&a[r]>=val) r--;
a[l]=a[r];
while (l!=r&&a[l]<=val) l++;
a[r]=a[l];
}
a[l]=val;
Quick_Sort(L,l);
Quick_Sort(l+1,R);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
Quick_Sort(1,n);
for (int i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
我怎么写这么长。。。
交换法
随便选择一个基准数。
初始两个指针l,r分别区间最左端和最优端
先让l指针向右直到找到第一个大于基准数的数。再让r指针向左直到找到第一个小于基准数的数。
如果l小于等于r就将这两个指针所指的数交换,再让l++,r–
当l>r
我感觉要坑了…
但我还是发了出来