算法入门之最常用的排序:快速排序算法

回顾前面2篇文章我们提到了桶算法和冒泡算法,虽然冒泡算法解决了桶算法的空间问题,但是如果排序的基数比较大,你会发现冒泡算法的时间复杂度O(N²)也是惊人的,有没有一种更好的算法既能解决空间问题又能解决时间复杂度的问题呢?答案就是我们今天要说的:快速排序算法

 

先上代码实现:

public class QuickSort {

    public static int[] sort(int[] waitNumbers, int start, int end) {
        // 当开始下标大于等于结束下标时,递归结束 返回排序后的结果
        if (start >= end) {
            return waitNumbers;
        }
        int i = start; //左边哨兵 初始值
        int j = end; //右边哨兵 初始值
        int t;  // 临时变量 交换值用的
        int temp = waitNumbers[start]; //存放基准数

        //循环执行直到开始下标 大于等于 结束下标时为止,
        //说明此轮排序结束,交换基准值,再次执行排序
        while (i < j) {
            /**
             * 右边下标先开始扫描 直到扫描到比基准数小的数为止,
             * 思考:为什么一定是j先开始扫描?
             * 解:因为我们要满足比基准数小的在左边,如果i先开始
             *   扫描,会出现i大于基准数的情况,为了满足i所在的值
             *   一定要小于基准数,我们必须先从右边开始扫描
             *   当j和i相交时,一定是j找到了比基准值小的数,如果
             *   是i先开始,可能会当i找到了比基准值大的时候,j还
             *   没找到比基准值小的,这个时候交换的不是i j的值,
             *   而是i和基准值的值,此时出现了比基准值大的值跑到
             *   基准值左边去了
             */
            while (waitNumbers[j] >= temp) {                        
                j--;
            }
            // 左边下标开始往右扫描。直到扫描到比基准数大的数为止
            while (waitNumbers[i] <= temp && i < j) {
                i++;
            }
            // 当i找到比基准数大,j找到比基准数小的时候,
                  // 交换i和j下标的值 继续往下执行,直到i>=j
            if (i < j) {
                t = waitNumbers[i];
                waitNumbers[i] = waitNumbers[j];
                waitNumbers[j] = t;
            }
        }
        // 交换基准值
        waitNumbers[start] = waitNumbers[i];
        waitNumbers[i] = temp;
        // 递归调排序执行基准值左边的值
        sort(waitNumbers, start, i - 1);
        // 递归调排序执行基准数右边的值
        sort(waitNumbers, i + 1, end);
        return waitNumbers;
    }

    public static void main(String[] args) {
        // 待排序的数字
        int[] waitNumbers = {8, 15, 3, 13, 6, 35, 45, 2, 10};
        QuickSort.sort(waitNumbers, 0, waitNumbers.length - 1);
        System.out.println(JSON.toJSONString(waitNumbers));
    }
}
结果:[2,3,6,8,10,13,15,35,45]

 

分析:思路分析已经在代码中分析很明确,这里就不再说了,既然这种算法能有效解决冒泡算法时间复杂度的问题,那么我们就来分析下

快速排序为什么比冒泡排序速度要快?

解:我们都知道冒泡排序是每次将相邻两个数进行比较和交换,而快速排序是每次设置一个基准点,将小于基准点的数放在左边,大于基准点的数放右边,每次交换的就不是相邻的两个数了,交换的距离大了,总的交换次数也少了,当然速度也就比冒泡排序快了,这种算法有没有一种似曾相识的感觉,如果有,那就对了,那就是二分查找思想,当然这不是二分查找,不要混淆。

 

快速排序算法的时间复杂度?

平均时间复杂度:快速排序算法的时间复杂度是跟原始数据的顺序有密切关系的,基准值显得特别重要了,我们了解到快速排序之所以快,是因为他可以从基准值的左边两边同时递归排序下去,所以最理想的状态就是基准值处于原始无序数据的中间,这样能最大效率让两边排序,同时减少递归划分出的区域,此时时间复杂度为O(NlogN),即平均时间复杂度;

最差时间复杂度:还有一种最差的情况就是每次作为基准值的数刚好是数组中最小/最大的那个数,此时其实就相当于冒泡,每次排好一个元素位置,所以最差时间复杂度等于冒泡算法时间复杂度O(N²)

 

优点:时间复杂度小,效率高,速度快

缺点:对于越是有序的数组排序效率越低

 

学习了三种排序算法我们做个简单的总结:

 桶算法:速度最快O(M+N),占空间最大O(N),适合基数小的正整数排序,

                适合先去重后排序的场景

 冒泡算法:速度最慢,占空间最小O(1),最稳当

 快速排序算法:速度优于冒泡O(NlogN),占优于桶O(log2n)~O(n),

                          稳定性次于冒泡

 

你可能感兴趣的:(算法系列)