😜 不懂就看看--快速排序和荷兰国旗问题

662 阅读2分钟

「这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战」。

快速排序

概念:

任取待排序元素序列中的某个元素(例如取第一个元素)作为基准,按照该元素的排序码大小,将整个元素序列划分为左右两个子序列:左侧子序列中的排序码都小于基准元素的排序码,右侧子序列中所有元素的排序码都大于或等于基准元素的排序码。基准元素则排在两个子序列中间(这也是该元素最终应安放的位置)。然后分别对这两个子序列重复实行上述方法(递归过程),直到所有元素都排在相应位置上为止。

简单来说:基于交换思想对冒泡排序的优化,也称为分区的交换排序。核心是划分,可以用递归来实现。

核心代码:

public class QuickSort {
    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSort(arr, 0, arr.length - 1);
    }

    // arr[l...r] 排好序
    public static void quickSort(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);
            quickSort(arr, L, p[0] - 1);// p【0】 --》等于区域的左边界  减一则为小于区域的
            quickSort(arr, p[1] + 1, R);// p【1】 --》 等于区域右边界  
        }
    }

   
    /**
     * 
     * 这是一个处理arr【l...r】的函数
     * 默认以arr【r】 做划分,arr【r】--》p    p《  ==p  》p
     * 返回等于区域(左边界,右边界) 所以返回一个长度为2的数组res,res【0】,res【1】
     * 
     */
    private static int[] partition(int[] arr, int L, int R) {
        int less = L - 1; //< 区右边界
        int more = R;    // >区左边界
        while (L < more) {// L表示当前数的位置,arr[R] -> 划分值
            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};
    }

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

图例

荷兰国旗问题

图片.png

给定一个数组arr和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边,要求额外空间复杂度O(1),时间复杂度O(N)

有了上面的快速排序的了解,将数组分成三块。在左边设置了一个大于等于区,这个问题可以在左边设置一个小于区,在右边设置一个大于区,不断向中间逼近,最终中间就是相等的数字,然后跟上面快排的思想基本一致,代码如下:


 private static int[] partition(int[] arr, int L, int R) {
        int less = L - 1; //< 区右边界
        int more = R;    // >区左边界
        while (L < more) {// L表示当前数的位置,arr[R] -> 划分值
            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};
    }

//比较排序
    private static void swap(int[] arr, int i, int j) {
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

总结:类似C语言的双指针的移动,当小于num时,则左指针带着区域移动。