其他分享
首页 > 其他分享> > 数据结构|排序(二)(快速排序)

数据结构|排序(二)(快速排序)

作者:互联网

8. 快速排序

8.1 原理

  1. 从待排序区间选择一个数,作为基准值(pivot);
  2. Partition:遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边(不要求任何顺序),将比基准值大的(可以包含相等的)放到基准值的右边;
  3. 采用分治思想,对左右两个小区间按照相同的方法处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。

8.2 实现

public static void quickSort(long[] array) {
    quickSortRange(array, 0, array.length - 1);
}

private static void quickSortRange(long[] array, int from, int to) {
    int size = to - from + 1;
    // 3. 直到,待排序区间的元素个数 <= 1
    if (size <= 1) {
        return;
    }

    // 1. 选择基准值 array[from]
    // 2. 做 partition,返回基准值跑到哪里去了 [pivotIdx]
    int pivotIdx = partition(array, from, to);

    // 左边的区间表示:[from, pivotIdx - 1]
    // 右边的区间表示:[pivotIdx + 1, to]
    // 3. 分别对左右两个小区间按照相同的方式处理(递归调用)
    quickSortRange(array, from, pivotIdx - 1);
    quickSortRange(array, pivotIdx + 1, to);
}

8.3 Partition过程

Hoare法

private static int partition1(long[] array, int from, int to) {
    long pivot = array[from];
    int left = from;
    int right = to;

    // 只要还有未比较的元素,循环就得继续
    // (left, right] 的元素个数 right - left > 0
    while (left < right) {
        // 因为我们基准值在左边,所以先走右边
        while (left < right && array[right] >= pivot) {
            right--;
        }

        while (left < right && array[left] <= pivot) {
            left++;
        }

        long t = array[left];
        array[left] = array[right];
        array[right] = t;
    }

    long t = array[from];
    array[from] = array[left];
    array[left] = t;
    

    return left;
}

挖坑法
基本思路和Hoare法一致,只是不再进行交换,而是进行赋值(填坑+挖坑)

private static int partition2(long[] array, int from, int to) {
    long pivot = array[from];
    int left = from;
    int right = to;

    while (left < right) {
        while (left < right && array[right] >= pivot) {
            right--;
        }

        array[left] = array[right];

        while (left < right && array[left] <= pivot) {
            left++;
        }

        array[right] = array[left];
    }

    array[left] = pivot;

    return left;
}

前后遍历法

private static int partition3(long[] array, int from, int to) {
    long pivot = array[from];
    int d = from + 1;
    for (int i = from + 1; i <= to; i++) {
        if (array[i] < pivot) {
            
            long t = array[d];
            array[d] = array[i];
            array[i] = t;

            d++;
        }
    }

    long t = array[from];
    array[from] = array[d - 1];
    array[d - 1] = t;

    return d - 1;
}

8.4 性能分析

时间复杂度:
最好的情况:o(n * log(n)),完全二叉树
最坏的情况:o(n ^ 2),单支树
平均:o(n * log(n))

空间复杂度:
一次partition的空间复杂度o(1),递归调用时栈帧需要消耗空间,影响其的是二叉树的高度。
最好的情况:o(log(n))
平均:o(log(n))
最坏:o(n)

稳定性:不具备稳定性

8.5 如何进行快速排序优化

  1. 基准值的选择
    (1) 随机选择 --> 使得选到最坏情况的概率降低
    (2) 三数取中 --> 选择第一个数,最后一个数,中间数,这三个数中大小是中值的数作为基准值,选出基准值后,重新放到最左边进行partition

  2. 做partition的时,等于基准值的元素分离出来
    [< pivot] [== pivot] [> pivot]

  3. 当待排序区间的元素个数小于阈值,用插排代替快排

  4. 非递归 --> 递归

  5. 双基准值选择
    pivot1 < pivot2
    [< pivot1] [== pivot1] [> pivot1 && < pivot2] [== pivot2] [> pivot2]

标签:数据结构,基准值,int,long,right,array,排序,快速,left
来源: https://blog.csdn.net/Yang_ccH/article/details/122639937