算法图解(四):快速排序

* <第4章> 快速排序 </第4章>
 *  4.1 分而治之(D&C策略)
 *   (1)找出基线条件,这种条件必须尽可能简单
 *   (2)不断将问题分解(或者说缩小规模),直到符合基线条件
 *  4.2 快速排序 示例:{@link QuickSort}
 *   (1)选择基准值
 *   (2)将数组分成两个子数组:小于基准值的元素和大于基准值的元素
 *   (3)对这两个子数组进行快速排序:递归
 *   (4)归纳证明:对于快速排序,可使用类似的推理。在基线条件中,我证明这种算法对空数组或包含一个
 *      元素的数组(不用排序)管用。在归纳条件中,我证明如果快速排序对包含一个元素的数组管用,对包含两
 *      个元素的数组也将管用;如果它对包含两个元素的数组管用,对包含三个元素的数组也将管用,以此类推。因此,我可以说,快速排序对任何长度的数组都管用。
 *   (5)快速排序的独特之处在于,其速度取决于选择的基准值
 *  4.3 再谈大O表示法
 *   (1)在平均情况下,快速排序的运行时间为O(nlog(n))
 *   (2)在最糟情况下,其运行时间为O(n²)
 *   4.3.1 比较合并排序和快速排序
 *    (1)快速查找的常量比合并查找小,因此如果它们的运行时间都为O(nlog(n)),快速查找的速度将更快
 *    (2)大O表示法中的n实际上是这样的:c*n,c是算法所需的固定时间量,被称为常量
 *    (3)通常不考虑这个常量,因为如果两种算法的大O运行时间不同,这种常量将无关紧要
 *   4.3.2 平均情况和最糟情况
 *    (1)快速排序的性能高度依赖于你选择的基准值
 *    (2)总是以第一个元素为基准值,且是有序的,那是最糟情况,时间复杂度为O(n²),如果以中间元素为基准值,那是最佳情况(平均情况),时间复杂度为O(nlog(n))
 *  4.4 小结
 *   (1)D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元素的数组
 *   (2)实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为O(nlog(n))
 *   (3)大O表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在
 *   (4)比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时,O(log n)的速度比O(n)快得多
 *

/**
 * 快速排序
 */
public class QuickSort {

    /**
     * 拼接数组和基准值
     *
     * @param a1
     * @param p
     * @param a2
     * @return
     */
    private static int[] concatArr(int[] a1, int p, int[] a2) {
        int[] arr = new int[a1.length + 1 + a2.length];
        System.arraycopy(a1, 0, arr, 0, a1.length);
        arr[a1.length] = p;
        System.arraycopy(a2, 0, arr, a1.length + 1, a2.length);
        return arr;
    }

    private static int[] quickSort(int[] arr) {
        if (arr.length < 2) {
            // 基线条件:数组元素为空,或只包含一个元素,不需要排序,直接返回
            return arr;
        } else {
            // 递归条件
            // 基线值:每次以中间元素作为基准值是最佳的选择
            int pivot = arr[arr.length / 2];
            int[] less = new int[arr.length];
            int j = 0;
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] <= pivot && i != arr.length / 2) {
                    less[j] = arr[i];
                    j++;
                }
            }
            // 小于基线值的数组元素a1
            less = Arrays.copyOf(less, j);

            int[] greater = new int[arr.length];
            j = 0;
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] > pivot && i != arr.length / 2) {
                    greater[j] = arr[i];
                    j++;
                }
            }
            //大于基线值的数据元素a2
            greater = Arrays.copyOf(greater, j);

            // 拼接a1+pivot+a2,将两部分数组分别继续快速排序
            return concatArr(quickSort(less), pivot, quickSort(greater));
        }
    }

    public static void main(String[] args) {
        int[] arr = new int[2000000];
        Random random = new Random(47);
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(100000);
        }

        long start = System.currentTimeMillis();
        quickSort(arr);
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start) + " ms");
        start = System.currentTimeMillis();
        Arrays.sort(arr);
        end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start) + " ms");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值