目录

PHP 快速排序

曾经在一次面试中,面试官问过我这个算法,我当时一脸懵逼,就说它比较快,所以就是快速排序,至于怎么快的一问三不知。所谓吃一堑长一智嘛,不管你怎么理解,最终还不得反应到代码实现上么?不费话了,上代码。

概念

快速排序 - Quick Sort:又称划分交换排序 - partition-exchange sort,简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序 n 个项目要 O(n log n) 次比较。在最坏状况下则需要 O(n^2) 次比较,但这种状况并不常见。事实上,快速排序 O(n log n) 通常明显比其他算法更快,因为它的内部循环 (inner loop)可以在大部分的架构上很有效率地达成。

步骤

  1. 从数列中挑出一个元素,称为『基准』- pivot
  2. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作
  3. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序

实现

方案一

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<?php

class QuickSort
{
    /**
     * 主运行方法
     *
     * @return void
     */
    public static function main(): void
    {
        $random = self::random();
        $array = self::sort($random);
        print_r($array);
    }

    /**
     * 快速排序
     *
     * @param array $array
     *
     * @return array
     */
    public static function sort(array &$array): array
    {
        $count = count($array);

        if ($count <= 1) {
            return $array;
        }

        $pivot = $array[0]; // 取数组第一个元素为基准值

        $left = $right = [];

        for ($i = 1; $i < $count; $i++) {
            $value = $array[$i];
            if (self::compare($pivot, $value) > 0) {
                $left[] = $array[$i];
            } else {
                $right[] = $array[$i];
            }
        }
        $left = self::sort($left);
        $right = self::sort($right);

        return array_merge($left, (array)$pivot, $right);
    }

    /**
     * 比较大小
     *
     * @param int $x
     * @param int $y
     *
     * @return int
     */
    private static function compare(int $x, int $y): int
    {
        return $x <=> $y;
    }

    /**
     * 互换位置
     *
     * @param int $x
     * @param int $y
     *
     * @return void
     */
    private static function swap(int &$x, int &$y): void
    {
        if ($x !== $y) {
            $t = $x;
            $x = $y;
            $y = $t;
        }
    }

    /**
     * 生成随机数组
     *
     * @param int $low
     * @param int $high
     * @param int $num
     *
     * @return array
     */
    private static function random(int $low = 1, int $high = 9999, int $num = 10): array
    {
        $num = $num > $high ? $high : $num;
        $range = range($low, $high);
        $array = array_rand(array_flip($range), $num);
        shuffle($array);

        return $array;
    }
}

QuickSort::main();

// 结果
Array
(
    [0] => 1073
    [1] => 1535
    [2] => 3376
    [3] => 3702
    [4] => 3961
    [5] => 4139
    [6] => 4644
    [7] => 5032
    [8] => 5803
    [9] => 6450
)

方案二

使用 Lomuto partition scheme 方式

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php

class QuickSort
{
    /**
     * 主运行方法
     *
     * @return void
     */
    public static function main(): void
    {
        $random = self::random();
        $array = self::sort($random, 0, count($random) - 1);
        print_r($array);
    }

    /**
     * 快速排序
     *
     * @param array $array
     * @param int   $low
     * @param int   $high
     *
     * @return array
     */
    public static function sort(array &$array, int $low, int $high): array
    {
        if ($low < $high) {
            $index = self::partition($array, $low, $high);
            self::sort($array, $low, $index - 1);
            self::sort($array, $index + 1, $high);
        }

        return $array;
    }

    /**
     * 原地分区
     *
     * @param array $array
     * @param int   $low
     * @param int   $high
     *
     * @return int
     */
    private static function partition(array &$array, int $low, int $high): int
    {
        $pivot = $array[$high]; // 选取最右边的元素为基准
        $i = $low - 1;

        for ($j = $low; $j < $high; $j++) {
            if (self::compare($pivot, $array[$j]) > 0) {
                $i++;
                self::swap($array[$i], $array[$j]);
            }
        }
        self::swap($array[$i + 1], $array[$high]);

        return $i + 1;
    }

    /**
     * 比较大小
     *
     * @param int $x
     * @param int $y
     *
     * @return int
     */
    private static function compare(int $x, int $y): int
    {
        return $x <=> $y;
    }

    /**
     * 互换位置
     *
     * @param int $x
     * @param int $y
     *
     * @return void
     */
    private static function swap(int &$x, int &$y): void
    {
        if ($x !== $y) {
            $t = $x;
            $x = $y;
            $y = $t;
        }
    }

    /**
     * 生成随机数组
     *
     * @param int $low
     * @param int $high
     * @param int $num
     *
     * @return array
     */
    private static function random(int $low = 1, int $high = 9999, int $num = 10): array
    {
        $num = $num > $high ? $high : $num;
        $range = range($low, $high);
        $array = array_rand(array_flip($range), $num);
        shuffle($array);

        return $array;
    }
}

QuickSort::main();

// 结果
Array
(
    [0] => 1411
    [1] => 3434
    [2] => 3776
    [3] => 6020
    [4] => 6047
    [5] => 6367
    [6] => 7107
    [7] => 7783
    [8] => 9135
    [9] => 9248
)

方案三

使用 Hoare partition scheme 方式

未完待续…