Quik Sort

快排三部曲:找基准,划区间,左右分治。其中划分区间(让左边的数都小于x,让右边的数都大于x)的操作最难,涉及到边界问题,代码需要兼容很多特殊情况。

不过暴力解法也挺好的,选中基准x后,遍历原数组将区间分别写入新的两个数组,之后再将原数组值写回。(就是多开数组了,时间复杂度不变)

优雅的实现方式有很多种,但是其中坑也是满满的。

第一种
最规范的实现方式,这种实现方式的基准只能是数组第一个元素,并且每次递归,都能实现“标准“的左右区间划分。所谓标准就是,base左边都小于等于它,右边都大于等于它。

划分区间的思想大概是:从右边找一个小于base的值,从左边找一个大于base的值,然后将他俩交换,之后再次寻找值进行swap。

void quick_sort(int q[], int low, int high) {
    if (low >= high) return;
  
    int base = q[low], L = low, R = high;
  
    while (L < R) {
        while (L < R && q[R] >= base) R--;
        q[L] = q[R];
        while (L < R && q[L] <= base) L++;
        q[R] = q[L];
    }
    q[L] = base;
  
    quick_sort(q, low, L - 1);
    quick_sort(q, L + 1, high);
}

第二种
这种方法是y总的实现方式,算法思想不变,划分区间的方式细节不同,这导致base不仅可以选择第一个数,中间数,随机位置的数都可以,划分的区间都是左边小于某值,右边大于某值的形式。

但是这不是“标准“的做法,因为每次递归结束,并不是左边的数都小于等于base,右边的数都大于等于base。例如4 5 2 1 3,第一次快排结果是3 1 2 5 4,base=4,很明显不满足预想。但是,这行结果仍然满足一种左小右大的形式,3 1 2 5 4,看起来好像2是base一样。

真是奇怪又精妙!

void quick_sort(int q[], int low, int high) {
    if (low >= high) return;
  
    int base = q[low], L = low - 1, R = high + 1;
  
    while (L < R) {
        do L++; while (q[L] < base);
        do R--; while (q[R] > base);
        if (L < R) swap(q[L], q[R]);
    }
  
    quick_sort(q, low, R);
    quick_sort(q, R + 1, high);
}

总结
对比来看,y总的方式可能更胜一筹,因为其选择基准的方式更灵活,如果出现test case为递增数字,base选为第一个数,整体算法复杂度将退化为O(n^2),如果base选为(low + high) >> 1将更具有普适性。

posted @ 2021-11-19 17:49  moon_orange  阅读(41)  评论(0编辑  收藏  举报