1. 简介

梳排序(Comb Sort)是由波兰计算机专家 Włodzimierz Dobosiewicz 于 1980 年在华沙大学提出的排序算法。我们知道,冒泡排序(Bubble Sort)是一种效率较低的排序算法,其最坏情况下的时间复杂度为 O(n²)。梳排序正是在冒泡排序的基础上进行改进的,类似于希尔排序(Shell Sort)对插入排序(Insertion Sort)的优化。

在本教程中,我们将学习梳排序如何改进冒泡排序,并理解它为何又被称为“带间隔的冒泡排序”。

2. 冒泡排序回顾

冒泡排序是一种基于比较的排序算法,它的基本思想是将较大的元素“冒泡”到数组末尾。我们通过多次遍历数组完成排序,每次遍历中比较相邻元素,如果顺序错误就交换它们。

以下是冒泡排序的伪代码:

algorithm BubbleSort(A):
    // INPUT
    //    A = the reference to the shuffled array
    // OUTPUT
    //    The sorted array A with all its elements in increasing order

    i <- 0

    // Perform n passes of the array
    for i <- 0 to N - 1:
        isSwapped <- false

        // Look at the subarray A[0 : N-i-1]
        // Step through the input array element by element
        for j <- 1 to N - i - 1:
            // Compare each element with its neighbor, swapping values if necessary
            if A[j - 1] > A[j]:
                temp <- A[j - 1]
                A[j - 1] <- A[j]
                A[j] <- temp

                isSwapped <- true

        if not isSwapped:
            break

    return A

如果某一轮遍历没有发生任何交换,说明数组已经有序,可以提前终止。

2.1. 兔子与乌龟

考虑一个无序数组:A = [9, 3, 4, 0, 8, 7, 6, 5, 1, 2],我们观察冒泡排序每轮迭代后的数组状态:

Iteration | Array
----------|-------------------------------------
0         | [9, 3, 4, 0, 8, 7, 6, 5, 1, 2]
1         | [3, 4, 0, 8, 7, 6, 5, 1, 2, 9]
2         | [3, 0, 4, 7, 6, 5, 1, 2, 8, 9]
3         | [0, 3, 4, 6, 5, 1, 2, 7, 8, 9]
4         | [0, 3, 4, 5, 1, 2, 6, 7, 8, 9]
5         | [0, 3, 4, 1, 2, 5, 6, 7, 8, 9]
6         | [0, 3, 1, 2, 4, 5, 6, 7, 8, 9]
7         | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

其中元素 9 被称为“兔子”,它很快被移动到末尾;而元素 1 被称为“乌龟”,它每次只能向前移动一步。这正是冒泡排序效率低下的主要原因。

3. 梳排序算法

梳排序的核心思想是:先对相距较远的元素进行比较和交换,逐步缩小这个距离(gap),从而减少“乌龟”的影响,提高排序效率

3.1. 基础梳排序

梳排序使用一个“gap”序列来决定每次比较的元素间隔。初始 gap 为数组长度,然后每次按比例缩小(通常为 1.3),直到 gap 为 1,此时算法退化为冒泡排序。

例如,对于长度为 16 的数组,若 shrink factor 为 1.3,则 gap 序列为:12, 9, 6, 4, 3, 2, 1

研究表明,1.3 是最优的缩小因子,可以有效平衡效率与实现复杂度。

3.2. 伪代码

以下是梳排序的伪代码实现:

algorithm CombSort(A):
    // INPUT
    //    A = the shuffled array
    // OUTPUT
    //    The sorted array with all its elements in increasing order

    gap <- length(A)
    isSwapped <- true

    // Keep iterating and stop if and only if the gap equals 1 and the array is fully sorted
    while gap != 1 or isSwapped:
        isSwapped <- false

        gap <- floor(gap / 1.3)

        if gap < 1:
            gap <- 1

        // Look at the subarray A[0 : N-gap-1]
        for i <- 0 to N - gap - 1:
            // Compare each pair of elements separated by a gap, swapping values if necessary
            if A[i] > A[i + gap]:
                temp <- A[i]
                A[i] <- A[i + gap]
                A[i + gap] <- temp

                isSwapped <- true

    return A

该算法的外层循环会持续运行,直到 gap 为 1 且没有发生任何交换操作。此时数组已经有序。

4. 梳排序的时间复杂度

梳排序的时间复杂度仍然是 O(n²),但在实际应用中比冒泡排序快很多。以下是梳排序与冒泡排序在不同数据规模下的性能对比:

n 梳排序耗时(ms) 冒泡排序耗时(ms)
10 0.0045960 0.00151599
100 0.077594 0.023565999
1000 1.606186 1.7639360
10000 24.872394 178.3646059
100000 327.263792 18536.333664
1000000 4079.795338 -

可以看到,随着数据量增大,梳排序的性能优势越来越明显。

5. Python 实现

下面是使用 Python 实现梳排序的完整代码:

def comb_sort(arr):
    gap = len(arr)
    is_swapped = True

    while gap != 1 or is_swapped:
        is_swapped = False
        gap = int(gap / 1.3)
        if gap <= 1:
            gap = 1

        for i in range(0, len(arr) - gap):
            if arr[i] > arr[i + gap]:
                arr[i], arr[i + gap] = arr[i + gap], arr[i]
                is_swapped = True

# 示例用法
arr = [8, 4, 1, 56, 3, -44, 23, -6, 28, 0]
comb_sort(arr)
print("Sorted array:", arr)

该实现遵循了伪代码逻辑,使用了 1.3 作为缩小因子,并在每次迭代中调整 gap 值,直到数组完全有序。

✅ 该算法原地排序
✅ 无需额外空间
⚠️ 不稳定排序算法(相同元素可能交换位置)
❌ 时间复杂度仍为 O(n²),不适用于大规模数据排序

6. 总结

梳排序是对冒泡排序的一种有效改进,通过引入 gap 概念,使得排序过程能够快速消除“乌龟”现象,从而显著提升性能。虽然其最坏时间复杂度仍为 O(n²),但在实际测试中,其效率远高于冒泡排序。

✅ 优点:

  • 实现简单
  • 性能优于冒泡排序
  • 原地排序,空间复杂度低

❌ 缺点:

  • 时间复杂度仍为 O(n²)
  • 不如快速排序、归并排序等算法高效

如果你需要一个简单、轻量级的排序方法,而又不希望像冒泡排序那样慢,那么梳排序是一个不错的选择。


原始标题:Comb Sort Explained