《图解算法》第4章 快速排序

时间:2022-07-23
本文章向大家介绍《图解算法》第4章 快速排序,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

第4章 快速排序

我们将探索分而治之(divide and conquer,D&C)——一种著名的递归式问题解决方法

分而治之

  • D&C算法是递归的。使用D&C解决问题的过程包括两个步骤
  1. 找出基线条件,这种条件必须尽可能简单
  2. 不断将问题分解(或者说缩小规模),直到符合基线条件

欧几里得算法:适用于这小块地的最大方块,也是适用于整块地的最大方块。 可汗学院很清楚地阐述了这种算法 https://www.khanacademy.org/computing/computer-science/ryptography/modarithmetic/a/the-euclidean-algorithm

  • 这里重申一下D&C的工作原理
  1. 找出简单的基线条件
  1. 确定如何缩小问题的规模,使其符合基线条件

提示:编写涉及数组的递归函数时,基线条件通常是数组为空或只包含一个元素。陷入困境时,请检查基线条件是不是这样的 函数式编程一瞥:为何还要使用递归方式呢?看看函数式编程你就明白了!诸如Haskell等函数式编程语言没有循环,因此你只能使用递归来编写这样的函数。如果你对递归有深入的认识,函数式编程语言学习起来将更容易

快速排序

首先,从数组中选择一个元素,这个元素被称为基准值 (pivot),接下来,找出比基准值 小的元素以及比基准值 大的元素

这被称为分区(partitioning)。现在你有

  1. 一个由所有小于基准值 的数字组成的子数组
  2. 基准值
  3. 一个由所有大于基准值 的数字组成的子数组
  • 操作步骤如下
  1. 选择基准值
  2. 将数组分成两个子数组:小于基准值 的元素和大小基准值的元素
  3. 对这两个子数组进行快速排序
class QuickSort
{

    static public function sort($array)
    {
        if (count($array) <= 1) {
            return $array;
        }
        $leftArray = $rightArray = [];
        $key = array_shift($array);
        foreach ($array as $item) {
            $key > $item ? ($leftArray[] = $item) : ($rightArray[] = $item);
        }

        return array_merge(self::sort($leftArray), [$key], self::sort($rightArray));
    }

}

$array = [5, 3, 6, 2, 10, 7, 9, 20, 15];
echo join(QuickSort::sort($array), ',') . PHP_EOL;

再谈大O表示法

快速排序的独特之处在于,其速度取决于选择的基准值。快速排序在最糟糕情况下,其运行时间为O(n2)。与选择排序一样慢!但这是最糟情况。在平均情况下,快速排序的运行时间为O(n log n)

比较合并排序和快速排序

快速查找的速度确实更快,因为相对于遇上最糟情况,它遇上平均情况的可能性要大得多

平均情况和最糟情况

快速排序的性能高度依赖于你选择的基准值 。假设你总是将第一个元素用作基准值 ,且要处理的数组是有序的。由于快速排序算法不检查输入数组是否有序,因此它依然尝试对其进行排序

注意,数组并没有被分成两半,相反,其中一个子数组始终为空,这导致调用栈非常长 在最糟情况下,栈长为O(n),而在最佳情况下,栈长为O(log n) 在这个示例中,整个算法需要的时间为O(n)O(log n)=O(n log n),这是最佳情况。在最糟情况下,有O(n)层,因此该算法的运行时间为O(n)O(n)=O(n2)`