zoukankan      html  css  js  c++  java
  • 17-快速排序

    参考和引用了 白话经典算法系列之六——快速排序 的一些内容

    1. 基本思想

    • 概述
      • 快速排序(Quicksort) 是一种分治的排序算法,它将一个数组分成两个子数组,将两部分独立地排序;排序的方式是当两个子数组都有序时整个数组也就自然有序了。
      • 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
    • 切分元素 arr[x]
      • 该元素值又被称为"基准数" / "中轴"
      • 一般选取数组首元素、尾元素、中间元素作为切分元素
      • 作用:将数组分割成两个子数组,其中一个子数组的元素都比基准数小,另一个子数组的元素都比基准数要大。
    • 切分方法 partition(arr, left, right)
      • 快速排序递归地将子数组 arr{left, right} 排序,每调用一次 partition 就会使基准数 arr[x] 放到最终位置,然后再递归调用 partition 将其他位置的元素排序。
      • 切分过程使得数组满足下面 3 个条件:
        • 对于某个 x,arr[x] 已经排定
        • arr[left] 到 arr[x-1] 的所有元素都小于 arr[x]
        • arr[x+1] 到 arr[right] 的所有元素都不小于 arr[x]
      • 因为切分过程总是能排定一个元素,用归纳法不难证明递归能够正确地将数组排序:如果左子数组和右子数组都是有序的,那么左子数组(有序且没有任何元素大于切分元素)、切分元素和右子数组(有序且没有任何元素小于切分元素)组成的结果数组也一定是有序的。切分算法就是实现了这个思路的一个递归程序
    • 切分过程
      • 先从数组中选定一个切分元素作为基准数
      • 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
      • 再对左右区间重复 step2,直到各区间只有一个数

    2. 标配

    • 平均时间复杂度:O(NlogN)
    • 最坏时间复杂度:O(N^2)
    • 最好时间复杂度:O(NlogN)
    • 空间复杂度:O(logN)

    3. 挖坑填数

    1. i = left, j = right; 将基准数挖出而形成了第一个坑 arr[i]
    2. j-- 由后向前找比基准数小的数,找到后挖出此数填入前面一个坑 arr[i] 中
    3. i++ 由前向后找比基准数大的数,找到后也挖出来填到前面一个坑 arr[j] 中
    4. 再重复执行 step2,step3 两步,直到 i == j,将基准数填入 a[i] 中

    4. 代码实现

    public class QuickSort2 {
        public static void main(String[] args) {
            int[] arr = {5, 9, 78, 0, 23, -567, 70, 42, -5, 36};
            quickSort_2(arr, 0, arr.length-1);
            print(arr);
        }
    
        public static void quickSort_1(int[] arr, int left, int right) {
            // 分治
            if (left < right) {
                int x = partition(arr, left, right);
                quickSort_1(arr, left, x-1);
                quickSort_1(arr, x+1, right);
            }
        }
    
        /**
         * 递归调用切分方法完成子数组排序
         * @param arr 待排序数组
         * @param left 数组首元素索引
         * @param right 数组尾元素索引
         * @return 切分元素的最终存放位置
         */
        public static int partition(int[] arr, int left, int right) {
            int i = left;
            int j = right;
            int pivot = arr[i]; // 挖出左边的, 待会要先从右边找
    
            while (i < j) {
                // 1. 先从右向左找小于 pivot 的数来填 arr[i] 的坑
                while (i < j && arr[j] >= pivot) j--;
                if (i < j) arr[i++] = arr[j];
    
                // 2. 再从左向右找大于/等于 pivot 的数来填 arr[j] 的坑
                while (i < j && arr[i] < pivot) i++;
                if(i < j) arr[j--] = arr[i];
            }
    
            // 退出时 i == j; 该位置也是 pivot 在数组中的最终位置
            arr[i] = pivot;
            return i;
        }
    
    
        public static void quickSort_2(int[] arr, int left, int right) {
            if (left < right) {
                int i = left;
                int j = right;
                int pivot = arr[right]; // 挖右, 始左
    
                while (i < j) {
                    while (i < j && arr[i] < pivot) i++;
                    if(i < j) arr[j--] = arr[i];
                    while (i < j && arr[j] >= pivot) j--;
                    if(i < j) arr[i++] = arr[j];
                }
    
                arr[i] = pivot;
                quickSort_2(arr, left, i-1);
                quickSort_2(arr, i+1, right);
            }
        }
    
        public static void print(int[] arr) {
            for (int i = 0; i < arr.length; i++)
                System.out.print(arr[i] + " ");
            System.out.println();
        }
    }
    

    5. Tips

    • while 里 i < j 的判断一个都不能少!
    • 想一想就俩元素的时候,快排是怎么个情况?
    • N 个元素,一共 logN 层,每层都是 O(N),一共 NlogN
    • 每选出一个 pivot 对其 partition 过后,也就确定了这个数最终在数组中的位置
    • 有的 pivot 是随机从数组中选的一个数,这个咋弄? 选出来之后直接让它和 arr[left/right] 做交换
  • 相关阅读:
    C# WinForm 只允许运行一个实例
    C# WinForm 获得文字的像素宽度
    Windows 下使用命令行升级 Npm 和 NodeJS
    每日踩坑 2020-04-15 C# 与 Java 对应 DES 加密算法
    robot自动化测试(二)--- template使用
    robot自动化测试(一)---安装
    linux io优化
    python 远程统计文件
    编程类学习资料整合,更多干货
    两份安卓学习资料,我建议你看完
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/12327610.html
Copyright © 2011-2022 走看看