ZetCode

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";

结果会有所不同,但通常计数排序对于小范围更快,而快速排序对于大范围表现更好。计数排序的内存使用量随着范围大小的增加而增加,这使得它对于大范围不太有效。

何时使用计数排序

计数排序的局限性

来源

维基百科上的计数排序

本教程通过数字和文本数据的示例介绍了 PHP 中的计数排序算法,包括与快速排序的性能比较。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有十多年的编程教学经验。

列出 所有 PHP 教程