PHP 计数排序算法
最后修改于 2025 年 4 月 16 日
基本定义
算法是一种逐步解决问题或执行计算的程序。排序算法将元素按特定顺序排列。
常见的排序算法包括冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序和计数排序。每个算法都有不同的性能特征。
计数排序概述
计数排序是一种整数排序算法,它通过计算具有不同键值的对象的数量来工作。它以 O(n + k) 时间运行,其中 n 是元素的数量,k 是输入数据的范围。
当输入数据 (k) 的范围不明显大于对象的数量 (n) 时,计数排序是有效的。它不是比较排序,在这些情况下,它可以比 O(n log n) 算法更快。
计数排序实现
以下是 PHP 中正整数的基本计数排序实现。
counting_sort.php
<?php function countingSort(array $array): array { $max = max($array); $count = array_fill(0, $max + 1, 0); foreach ($array as $num) { $count[$num]++; } $sorted = []; for ($i = 0; $i <= $max; $i++) { while ($count[$i]-- > 0) { $sorted[] = $i; } } return $sorted; } $numbers = [4, 2, 2, 8, 3, 3, 1]; $sorted = countingSort($numbers); print_r($sorted); // Outputs: [1, 2, 2, 3, 3, 4, 8]
此实现首先计算每个数字的出现次数,然后从计数中重建排序后的数组。它适用于小整数范围。
带有负数的计数排序
这是一个增强版本,通过调整索引来处理负数。
counting_sort_negative.php
<?php function countingSort(array $array): array { $min = min($array); $max = max($array); $range = $max - $min + 1; $count = array_fill(0, $range, 0); foreach ($array as $num) { $count[$num - $min]++; } $sorted = []; for ($i = 0; $i < $range; $i++) { while ($count[$i]-- > 0) { $sorted[] = $i + $min; } } return $sorted; } $numbers = [-5, 2, -3, 8, 0, -1, 2]; $sorted = countingSort($numbers); print_r($sorted); // Outputs: [-5, -3, -1, 0, 2, 2, 8]
该算法通过减去最小值来调整索引,使其能够处理负数,同时保持 O(n + k) 的时间复杂度。
降序计数排序
要以降序排序,我们只需反向迭代计数数组。
counting_sort_desc.php
<?php function countingSortDesc(array $array): array { $max = max($array); $count = array_fill(0, $max + 1, 0); foreach ($array as $num) { $count[$num]++; } $sorted = []; for ($i = $max; $i >= 0; $i--) { while ($count[$i]-- > 0) { $sorted[] = $i; } } return $sorted; } $numbers = [4, 2, 2, 8, 3, 3, 1]; $sorted = countingSortDesc($numbers); print_r($sorted); // Outputs: [8, 4, 3, 3, 2, 2, 1]
与升序排序的唯一区别是构建排序数组时的循环方向。这与升序版本保持相同的时间复杂度。
文本数据的计数排序
计数排序可以通过使用字符代码作为键来适应文本数据。
counting_sort_text.php
<?php function countingSortText(string $str): string { $chars = str_split($str); $max = max(array_map('ord', $chars)); $min = min(array_map('ord', $chars)); $range = $max - $min + 1; $count = array_fill(0, $range, 0); foreach ($chars as $char) { $count[ord($char) - $min]++; } $sorted = ''; for ($i = 0; $i < $range; $i++) { while ($count[$i]-- > 0) { $sorted .= chr($i + $min); } } return $sorted; } $text = "counting sort"; $sorted = countingSortText($text); echo $sorted; // Outputs: " cgiinnoorsttu"
此版本将字符转换为其 ASCII 值进行计数。请注意,它保留空格并区分大小写(大写字母排在小写字母之前)。
性能比较:计数排序与快速排序
让我们将计数排序与 PHP 内置的快速排序实现进行比较。
sort_benchmark.php
<?php function generateRandomArray(int $size, int $min, int $max): array { return array_map(fn() => rand($min, $max), array_fill(0, $size, 0)); } function benchmark(callable $func, array $array): float { $start = microtime(true); $func($array); return microtime(true) - $start; } $smallRange = generateRandomArray(10000, 0, 100); $largeRange = generateRandomArray(10000, 0, 1000000); $countingTimeSmall = benchmark('countingSort', $smallRange); $quickTimeSmall = benchmark('sort', $smallRange); $countingTimeLarge = benchmark('countingSort', $largeRange); $quickTimeLarge = benchmark('sort', $largeRange); echo "Small range (0-100):\n"; echo "Counting sort: " . number_format($countingTimeSmall, 6) . "s\n"; echo "Quick sort: " . number_format($quickTimeSmall, 6) . "s\n\n"; echo "Large range (0-1000000):\n"; echo "Counting sort: " . number_format($countingTimeLarge, 6) . "s\n"; echo "Quick sort: " . number_format($quickTimeLarge, 6) . "s\n";
结果会有所不同,但通常计数排序对于小范围更快,而快速排序对于大范围表现更好。计数排序的内存使用量随着范围大小的增加而增加,这使得它对于大范围不太有效。
何时使用计数排序
- 小整数范围:当 k 为 O(n) 或更小时最佳。
- 需要稳定排序:可以实现为稳定排序。
- 非比较:当比较成本很高时很有用。
- 已知范围:需要事先了解数据范围。
计数排序的局限性
- 整数数据:最适用于整数键。
- 内存使用:需要 O(k) 的额外内存。
- 大范围:对于分散的数据效率低下。
- 负数:需要进行调整才能处理。
来源
本教程通过数字和文本数据的示例介绍了 PHP 中的计数排序算法,包括与快速排序的性能比较。
作者
列出 所有 PHP 教程。