排序算法之快速排序

思路

  1. 找到一个基础值,一般取第一个。
  2. 从右向左找,找到第一个比基准值小的数停下来;
  3. 从左向右找,找到第一个比基准值大的数停下来;
  4. 交换这两个数;
  5. 继续2-4步进行下一轮的查找替换,直至在基准数的左边都比基准数小,右边都比基准数大。
  6. 以基准数为界,分成左右数组,分别进行1-5操作。

经典快排实现

private static void quickSort(int[] arr, int start, int end) {
        if(start > end) return;

        int base = arr[start];
        int p1 = start, p2 = end;

        while(p1 != p2){
            while(arr[p2] >= base && p2 > p1){
                p2--;
            }
            while(arr[p1] <= base && p1 < p2){
                p1++;
            }
            swap(arr, p1, p2);
        }
        arr[start] = arr[p1];
        arr[p1] = base;

        quickSort(arr, start, p1 - 1);
        quickSort(arr, p1 + 1, end);
    }

    private static void swap(int[] arr, int j, int i) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
        quickSort(arr, 0, arr.length - 1);
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }

  • 快排平均时间复杂度为:O(nlogn)
  • 最坏时间复杂度为:O(n²) 也就是冒泡排序
  • 空间复杂度:O(logn)
  • 不稳定

荷兰国旗问题–快排常量级的优化

荷兰国旗问题:荷兰国旗由红白蓝3种颜色的条纹拼接而成。假设这样的条纹有多条,且各种颜色的数量不一,并且随机组成了一个新的图形。需求是:把这些条纹按照颜色排好,红色的在上半部分,白色的在中间部分,蓝色的在下半部分,我们把这类问题称作荷兰国旗问题。
经典快排每次排序得到一个base基准值,分为两部分。一部分是小,一部分是大。如果数组中有多个相同的base值,依然要进行再次排序,这样对重复值进行多次操作。
考虑优化如下:

// 改进后的快排
// 前面的经典快排是以待排数组第一位作为哨兵,进行比较
// 现在是以待排数组最后一位作为哨兵
    public static void quickSortPro(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSortPro(arr, 0, arr.length - 1);
    }

    static void quickSortPro(int[] arr, int l, int r) {
        if (l < r) {
            // 这一步是随机快排,可以先跳过不看
            // swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
            int[] p = partition(arr, l, r);
            quickSortPro(arr, l, p[0] - 1);
            quickSortPro(arr, p[1] + 1, r);
        }
    }


    /**
     *  分为三部分,左边是小于,中间是等于的部分,右边是大于的部分
     * @param arr 待排序数组
     * @param l 待排起始位置
     * @param r 待排结束位置
     * @return 返回一个数组,记录相等部分的最左和最右边界两个值。
     */
    static int[] partition(int[] arr, int l, int r) {
        int less = l - 1;
        int more = r;
        while (l < more) {
            //
            if (arr[l] < arr[r]) {
                swap(arr, ++less, l++);
            } else if (arr[l] > arr[r]) {
                swap(arr, --more, l);
            // 相同值,只要移动
            } else {
                l++;
            }
        }
        swap(arr, more, r);
        return new int[] { less + 1, more };
    }


    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

随机快排

最坏时间复杂度为:O(n²) 退化成为了冒泡排序。
前面两种快排,都是选定固定位置作为哨兵,这样快排的初始顺序影响了快排的时间复杂度。

随机快排将随机选取数组中的一个值作为哨兵,只需要将数组划分前,选取一个随机值替换数组的第一个或者最后一个元素

static void quickSortPro(int[] arr, int l, int r) {
        if (l < r) {
            // 分组之前的选取随机值
            swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
            int[] p = partition(arr, l, r);
            quickSortPro(arr, l, p[0] - 1);
            quickSortPro(arr, p[1] + 1, r);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值