6-5快速排序

快速排序


基本思想

快速排序(Quicksort) 是对冒泡排序的一种改进。基本思想是:通过一趟排序,将要排序的数据分割成独立的两部分,其中一部分的所有 数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

挖坑填数 + 分治法

图解

1621135025638

解法步骤

快速排序使用分治法策略来把一个序列分为两个子序列,基本步骤为:

  1. 先从序列中取出一个数作为基准数;

  2. 分区过程:将把这个数大的数全部放到它的右边,小于或者等于它的数全放到它的左边;

  3. 递归地对左右子序列进行步骤2,直到各区间只有一个数。

    1621138469863

以一个数组作为示例,取区间第一个数为基准数。

0 1 2 3 4 5 6 7 8 9
72 6 57 88 60 42 83 73 48 85
  1. 初始时,i = 0; j = 9; temp = a[i] = 72

    由于已经将 a[0] 中的数保存到 temp 中,可以理解成在数组 a[0] 上挖了个坑,可以将其它数据填充到这来。

  2. j 开始向前找一个比 temp 小或等于 temp 的数。当 j = 8,符合条件,将 a[8] 挖出再填到上一个坑 a[0] 中。

    a[0] = a[8]; i++; 这样一个坑 a[0] 就被搞定了,但又形成了一个新坑 a[8],这怎么办了?简单,再找数字来填 a[8] 这个坑。

  3. 这次从i开始向后找一个大于 temp 的数,当 i = 3,符合条件,将 a[3] 挖出再填到上一个坑中 a[8] = a[3]; j--;

数组变为:

0 1 2 3 4 5 6 7 8 9
48 6 57 88 60 42 83 73 88 85

i = 3; j = 7; temp = 72

再重复上面的步骤,先从后向前找,再从前向后找

从 j 开始向前找,当 j = 5,符合条件,将 a[5] 挖出填到上一个坑中,a[3] = a[5]; i++;

从i开始向后找,当 i = 5 时,由于 i==j 退出。

此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将 temp 填入 a[5]。

数组变为:

0 1 2 3 4 5 6 7 8 9
48 6 57 42 60 72 83 73 88 85

可以看出 a[5] 前面的数字都小于它,a[5] 后面的数字都大于它。因此再对 a[0…4] 和 a[6…9] 这二个子区间重复上述步骤就可以了。

对挖坑填数进行总结

1.i = L; j = R; 将基准数挖出形成第一个坑 a[i]。

2.j-- 由后向前找比它小的数,找到后挖出此数填前一个坑 a[i] 中。

3.i++ 由前向后找比它大的数,找到后也挖出此数填到前一个坑 a[j] 中。

4.再重复执行 2,3 二步,直到 i==j,将基准数填入 a[i] 中。

基准数要三数取中

代码实现

public class Test09 {

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

    private static void printArr(int[] arr) {
        for (int anArr : arr) {
            System.out.print(anArr + " ");
        }
    }

    private static int partition(int[] arr, int left, int right) {
        // 采用三数中值分割法
        //--------------------------------------
        int mid = left + (right - left) / 2;
        
        // 保证左端较小
        if (arr[left] > arr[right])
            swap(arr, left, right);
        // 保证中间较小
        if (arr[mid] > arr[right])
            swap(arr, mid, right);
        // 保证中间最小,左右最大
        if (arr[mid] > arr[left])
            swap(arr, left, mid);
        //--------------------------------------
        int pivot = arr[left];
        
        while (right > left) {
            // 先判断基准数和后面的数依次比较
            // 从后向前依次寻找一个比基准数小的数
            while (pivot <= arr[right] && left < right) {
                --right;
            }
            // 当基准数大于了 arr[right],则填坑。
            // 将比基准数小的放到基准数位置,right的数填到left位置
            //以基准数为界 将数组分为两个序列 分别比基准数大和小
            if (left < right) {
                arr[left] = arr[right];
                ++left;
            }
            // 现在是 arr[right] 需要填坑了
            while (pivot >= arr[left] && left < right) {
                ++left;
            }
            if (left < right) {
                arr[right] = arr[left];
                --right;
            }
        }
        arr[left] = pivot;
        return left;
    }

    private static void quickSort(int[] arr, int left, int right) {
        if (arr == null || left >= right || arr.length <= 1)
            return;
        int mid = partition(arr, left, right);
        quickSort(arr, left, mid);
        quickSort(arr, mid + 1, right);
    }


    public static void main(String[] args) {
        int[] arr = {6, 4, 3, 2, 7, 9, 1, 8, 5};
        quickSort(arr, 0, arr.length - 1);
        printArr(arr);
    }
}

posted @ 2021-11-23 21:12  剪水行舟  阅读(64)  评论(0)    收藏  举报