Martube Home for writing

快速排序

快速排序

本文大部分知识点来源于知乎专栏:Java必学知识

快速排序

思想

快速排序(QuickSort)是对冒泡排序的一种改进。快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:

  1. 从要排序的数据中取一个数为“基准数”。

  2. 通过一趟排序将要排序的数据分割成独立的两部分,其中左边的数据都比“基准数”小,右边的数据都比“基准数”大。

  3. 然后再按步骤2对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

该思想可以概括为:挖坑填数 + 分治法。

分治法

分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如快速排序,归并排序,傅立叶变换(快速傅立叶变换)等等。

代码

/**
     * @param arr 需要排序的数组
     * @param low 开始时最左边的索引=0
     * @param high 开始时最右边的索引=arr.length-1
     */
    public static void quickSort(int[] arr,int low, int high){
        if(low >= high){
            return;
        }
        //取最左边的数作为基准位
        int i = low,j = high,index = arr[i];

        while (i < j){
            //从右向左寻找第一个小于index的数,当arr[j]<index时,下面这个while循环会结束
            while (i < j && arr[j] >= index){
                j--;
            }
            //将找到的arr[j] 填入arr[i],同时将i右移
            if(i<j){
                arr[i++] = arr[j];
            }
            //从左向右寻找第一个大于index的数
            while (i < j && arr[i] < index){
                i++;
            }
            //将找到的arr[i] 填入arr[j],同时将j左移
            if(i < j){
                arr[j--] = arr[i];
            }
        }
        //将基准数填入最后的坑
        arr[i] = index;

        //对基准数两边的数组再进行上面的操作
        quickSort(arr, low, i - 1); // 递归调用,分治
        quickSort(arr, i + 1, high);  // 递归调用,分治
    }

适用场景

最好情况的时间复杂度为O(nlogn),过程比较复杂,就不在此赘述。

最差情况下每一次取到的数(基准数)都是当前要比较的数中的最大/最小值,

在这种情况下,每次都只能得到比上一次少1个数的子序列(即要么全比基准数大,要么全比基准小),

此时相当于一个冒泡排序,比较的次数 = (n - 1) + (n - 2) + … + 2 + 1 = (n - 1) * n / 2,此时的时间复杂度为:O(n^2)。

最差情况一般出现在:待排序的数据本身已经是正序或反序排好了。

基本上在任何需要排序的场景都可以使用快速排序。虽然快速排序的最坏情况时间复杂度为O(n^2),但是由于基本不会出现,因此可以放心的使用快速排序。在本人的电脑测试,100万的随机数字,快速排序大约耗时120毫秒。