快排之java实现

场景

假设我们现在对“6 1 2 7 9 3 4 5 10 8”这个10个数进行升序排序

实现方法

1. 基准数法

  1. 首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来参照的数,待会你就知道它用来做啥的了)为了方便,就让第一个数6作为基准数吧。
  2. 接下来,需要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边
  3. 在初始状态下,数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,假设这个位置是k。现在就需要寻找这个k,并且以第k位为分界点,左边的数都小于等于6,右边的数都大于等于6。
  4. 用两个变量i和j,分别指向序列最左边和最右边。即i=1,指向数字6。让哨兵j指向序列的最右边(即=10),指向数字8。
  5. 首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要(请自己想一想为什么)。哨兵j一步一步地向左挪动(即j–),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。
  6. 现在交换哨兵i和哨兵j所指向的元素的值(至少交换值,i、j位置不变)。交换之后的序列如下:

x x x x i x x x x j
6 1 2 5 9 3 4 7 10 8 (第一次交换结果)
以此类推:
x x x x x i x x j x x
6 1 2 5 4 3 9 7 10 8 (第二次交换结果)
x x x x x x ij x x
6 1 2 5 4 3 9 7 10 8 (第三次交换结果)

  1. 哨兵i和哨兵j都走到3面前。说明此时“探测”结束。我们将基准数6和3进行交换。交换之后的序列如下:

3 1 2 5 4 6 9 7 10 8
到此第一轮“探测”真正结束
回顾一下刚才的过程,其实哨兵j的使命就是要找小于基准数的数,而哨兵i的使命就是要找大于基准数的数,直到i和j碰头为止。

  1. 此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“9 7 10 8”。接下来还需要分别处理这两个序列。因为6左边和右边的序列目前都还是很混乱的。不过不要紧,我们已经掌握了方法,接下来只要模拟刚才的方法分别处理6左边和右边的序列即可。现在先来处理6左边的序列现吧。最终结果如下:

1 2 3 4 5 6 7 8 9 10

  1. 其实上述方法是利用了二分法的思想
  2. 代码实现
package com.example.sort;

import java.util.Arrays;

public class QuickSortDemo01 {
    public static void quickSort(Integer[] arr, int low, int high) {
        int i, j, temp, t;
        if (low > high) {
            return;
        }
        i = low;
        j = high;
        //temp就是基准位
        temp = arr[low];

        while (i < j) {
            //先从右边,依次往左递减
            while (temp <= arr[j] && i < j) {
                j--;
            }
            //再从左边,依次往右递增
            while (temp >= arr[i] && i < j) {
                i++;
            }
            //如果满足条件则交换
            if (i < j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }

        }
        //最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[i];
        arr[i] = temp;
        //递归调用左半数组
        quickSort(arr, low, j - 1);
        //递归调用右半数组
        quickSort(arr, j + 1, high);
    }


    public static void main(String[] args) {
        Integer[] arr = {16, 71, 2, 4, 7, 62, 13, 4, 2, 1, 8, 9, 19};
        quickSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.asList(arr));       
    }
}
// [1, 2, 2, 4, 4, 7, 8, 9, 13, 16, 19, 62, 71]

2. 三数取中法

  1. 在上面基准数法中快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。
  2. 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行(升序)排序,将中间数作为枢纽值。
  3. 原始数据

4 5 7 8 1 2 3 6
此处中间数8是根据取模 左端[下标0]和右端[下标7]之和取模[ (0+7)/2 = 3 ]得到,8的下标为3.

  1. 对4、8、6进行排序[ 4 6 8 ]并且把枢纽值6放在数组最后 [ 8之前 ]

4 5 7 1 2 3 6 8

  1. 根据枢纽值6进行分割

4 5 7 1 2 3 6 8
对分割之后的 5 7 1 2 3 采用基准数法进行排序
4 5 2 3 1 6 7 8 ( 第一轮分割完成)

  1. 递归在子序列进行上述相同处理(先三位取中,再以中值分割)

4 5 2 3 1 (左子序列)
7 8 ( 右子序列)
右子序列因为已经是有序的,所以不用处理;对左子序列进行处理即可

  1. 最终排序结果为:

1 2 3 4 5 6 7 8

  1. 代码实现:
package com.example.sort;

import java.util.Arrays;

public class QuickSortDemo02 {
    public static void main(String[] args) {
        int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
        quickSort(arr, 0, arr.length - 1);
        System.out.println("排序结果:" + Arrays.toString(arr));
    }

    /**
     * 排序
     */
    public static void quickSort(int[] arr, int left, int right) {
        if (left < right) {
            //获取枢纽值,并将其放在当前待处理序列末尾
            dealPivot(arr, left, right);
            //枢纽值被放在序列末尾
            int pivot = right - 1;
            //左指针
            int i = left;
            //右指针
            int j = right - 1;
            while (true) {
                while (arr[++i] < arr[pivot]) {
                }
                while (j > left && arr[--j] > arr[pivot]) {
                }
                if (i < j) {
                    swap(arr, i, j);
                } else {
                    break;
                }
            }
            if (i < right) {
                swap(arr, i, right - 1);
            }
            quickSort(arr, left, i - 1);
            quickSort(arr, i + 1, right);
        }

    }

    /**
     * 处理枢纽值
     */
    public static void dealPivot(int[] arr, int left, int right) {
        int mid = (left + right) / 2;
        if (arr[left] > arr[mid]) {
            swap(arr, left, mid);
        }
        if (arr[left] > arr[right]) {
            swap(arr, left, right);
        }
        if (arr[right] < arr[mid]) {
            swap(arr, right, mid);
        }
        swap(arr, right - 1, mid);
    }

    /**
     * 交换元素通用处理
     */
    private static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}
// 排序结果:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章