Loading

快速排序详解与java程序模版

1. 快速排序

1.1 快速排序的思想

快速排序本质上是荷兰国旗问题的拓展(建议先了解一下荷兰国旗问题,再食用更佳),算法思路如下:

  • 定义数组array的待排序范围为lohi,默认就是0length-1,即整个数组都排序

  • 将排序数组的最后一个数字array[hi]作为基准数字baseNum,执行下面的partition过程

    • partition的目标就是基于基准数字,将小于baseNum的放在数组左边,等于的放中间,大于的放右边,并且返回小于区域和大于区域的位置
    • 在数组的左边界前维护一个小于区域指针,这个指针之前的都是小于基准数字的数,指针的初始值为lo-1
    • 同样数组的右边界后维护一个大于区域指针,这个指针之后的都是大于基准数字的数,指针的初始值为hi(这里因为将最后一个array[hi]作为基准了,因此待比较的数在hi前一个开始)
    • 定义一个指针index,从lo出发,如果array[index] < array[hi],此时将小于区域的后一个数字和当前index的值交换,并让index++,小于区域也向右扩展一个
    • 如果array[index] = array[hi],表示当前的区域为等于基准数字的,因此只需要将index++即可
    • 如果array[index] > array[hi],表示当前值应当是大于区域的值,所以将其与大于区域的前一个交换,然后将大于区域左移,(注意这里不能将index++,因为刚刚换过来的数不知道其大小如何,需要再比较一次)
    • 重复执行上述过程,当index和大于区域相遇停止,返回小于区域和大于区域的边界
  • 将得到的新的小于区域和大于区域进行partition过程,中间等于区域已经是最终的位置

注意点:

  • 实际中的排序不是直接取数组的最后一个,而是随机的选择中间一个,然后让其和最后一个数交换,这样可以保证时间复杂度位\(O(NlogN)\),否则在顺序的数组这种最坏情况下时间复杂度位\(O(N^2)\),概率论证明。

1.2 java代码:

package com.company.com.chao.blog.sort;

import java.util.Arrays;
import java.util.Random;

public class QuickSort {

    public static void main(String[] args) {
        int[] array = new int[100];
        Random r  = new Random();
        for (int i = 0; i < array.length; i++) {
            array[i] = r.nextInt(100);
        }
        System.out.println("Before QuickSort: " + Arrays.toString(array));
        quickSort(array, 0, array.length - 1);
        System.out.println("After QuickSort: " + Arrays.toString(array));
    }

    /**
     * 核心实现就是荷兰国旗问题:每次选择最右边的点作为边界,
     * 然后将小于该点的数放到左边,等于的放中间,大于的放右边,
     * 对左右边界继续执行上述过程
     * 注意点:1. 每次随机从数组选择一个点,将其与右端点交换作为基准节点,
     *           这样可以将复杂度从O(N^2)降低到O(NlogN),概率论证明
     */
    private static void quickSort(int[] array, int lo, int hi) {
        if (lo >= hi) return;

        // 随机的从lo到hi上选择一个数字,这里操作的是索引
        int base = (new Random().nextInt(hi - lo + 1)) + lo;
        // 将这个数字和最后的位置hi交换
        swap(array, base, hi);
        // 执行一次partition过程
        int[] nextPosition = partition(array, lo, hi);
        // 将一次partition过程的左右边界进行下一次的递归排序
        quickSort(array, lo, nextPosition[0] - 1);
        quickSort(array, nextPosition[1] + 1, hi);
    }

    // 将hi的位置作为基准 将array的数据划分为三部分 前面的小于array[hi]
    // 中间的等于array[hi], 后面的等于array[hi]
    private static int[] partition(int[] array, int lo, int hi) {
        // 定义两个指针
        int leftIndex = lo - 1, rightIndex = hi, index = lo;

        while (index < rightIndex) {
            if (array[index] < array[hi]) {
                // 让i处的和L区的右边一个交换
                swap(array, ++leftIndex, index++);
            } else if (array[index] == array[hi]){
                index++;
            } else {
                // 让i处的和R区的左边交换
                swap(array, --rightIndex, index);
            }
        }
        // 交换最后的hi所在的基准数和大于区域的左边界
        swap(array, rightIndex, hi);
        return new int[]{leftIndex + 1, rightIndex};
    }

    // 将数组中的两个索引对应的位置的值交换
    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}
posted @ 2021-12-30 19:36  smallchaochao  阅读(32)  评论(0)    收藏  举报