一.思想简介:
快排也是用到分治的思想,将问题分层次解决。总体思想就是拿出一个该列表的数,将所有小于等于这个数的数都放在它的左边,所有大于等于它的数都放在右边,那么这个数的位置就确定了。至于怎么实现很巧妙,和想法有些不同。是先找到位置,再将该数放到那个位置上的。
1.拿到一个序列:
(1)我们将第一个数(人为设定,方便求解)规定为中间数;
(2)从右往左找第一个比它小的数 ,然后再从左往右找第一个比它大的数,交换两数;
(3)再继续此过程,直到两个数重合,此时该位置左边应全小于等于中间数,该位置右边应全大于等于中间数。此时我们将中间数放到这个位置上,也就是交换该位置的数和第一个位置的数,则中间数的位置就排好了(整个数列有序后它在的位置就是这个位置),接下来只要分别排该数两边的序列就好了;
(4)分别将该数两边的序列进行以上方法的排序,可以用递归来实现。那么就差递归的退出条件啦,当该序列只有一个元素(l = r)时,可以看作有序,即递归结束。
2.细节考虑:
(1)开始一直再考虑,从左右两边找数的时候,等于的情况用不用单独分析,后来觉得没必要分析,等于既可以放到右边也可以放到左边,所以就不考虑等于了,直接找小于或大于。
(2)有一个重点,要先从<——这个方向找小于的数,再从——>这个方向找大于的数。
为什么呢?因为我们得到的中间位置(i = j 的位置)的数要和第一个位置的数做交换,而第一个位置处于左边,最后这个地方的数一定是小于等于中间数的。在最后一次找数时,i 位置的数小于等于中间数,j 位置的数大于等于中间数,如果先——>这个方向找小于值,结果找不到,和 j 上面的数重合了,于是会将 j 位置当作中间位置,将该数与第一个数做交换,结果第一个数大于等于中间数,大于时就背离了我们的初衷了。同理可得,按我们规定的正确顺序,第一个数结果小于等于中间数的,满足我们的要求。
(3)按两个方向找数时,当 i >= j 时也要退出了。
二.代码实现
#include <iostream>
#include <vector>
using namespace std;
vector<int> a = { 2,5,5,5,2,1,4,4,3,1 };
// 传入带排序列的最左和最右下标
void qsort(int l, int r)
{
if(l >= r) // 循环退出条件,序列中只有一个数,认为有序,不用排了
return;
int i = l,j = r; // 初始化 i,j;
while (i < j) // 当中间位置还没有出现时
{
for (j; a[j] >= a[l] && i < j; j--); // <—方向找小于值 注意要加 i < j
for (i; a[i] <= a[l] && i < j; i++); // —>方向找大于值
if (i != j) swap(a[i], a[j]); // 交换两值,为了使左边为小于值,右边为大于值
}
swap(a[l],a[i]); //交换中间位置值和第一个位置值
qsort(l, i - 1); //排序前边的列表
qsort(i + 1, r); //排序后边的列表
}
int main()
{
qsort(0, a.size() - 1);
for (auto i : a)
cout << i << " ";
return 0;
}
over!