Алгоритм поразрядной сортировки (Radix Sort): принцип работы

Модуль "Сортировки" • урок 13 из 17 •

Также упоминается как: Цифровая сортировка, Сортировка по основанию

Суть урока. Поразрядная сортировка - алгоритм, который не сравнивает числа целиком, а сортирует их по отдельным разрядам (цифрам). Начиная с младшего разряда к старшему (LSD) или наоборот (MSD).

Время работы: O(D * (N + K)), где D - количество разрядов (длина максимального числа), N - элементов, K - основание системы счисления (для десятичной K=10).

Идея метода сортировки

Чаще всего используют метод LSD (Least Significant Digit) - сортировку, начиная с самого младшего разряда (справа налево). Этот алгоритм опирается на важное свойство: промежуточная сортировка по каждому разряду обязана быть устойчивой (stable).

Почему важна устойчивость? Если мы сначала отсортировали числа по единицам, а затем сортируем по десяткам, алгоритм сортировки десятков не должен перепутать порядок чисел с одинаковыми десятками! Например, числа 21 и 25 после шага единиц стоят как 21, 25. На шаге десятков они попадают в одну корзину "2". Если промежуточная сортировка устойчива, их взаимный порядок сохранится, и они так и останутся 21, 25.

Алгоритм поразрядной сортировки: последовательные устойчивые проходы от младших разрядов к старшим
Поразрядная сортировка: последовательные устойчивые проходы от младших разрядов (единиц) к старшим (десяткам).

Тренажер позволяет в деталях рассмотреть поразрядную сортировку: как числа группируются сначала по младшим цифрам, а затем по старшим.

Тренажер: алгоритм поразрядной сортировки

Отсортируем 53 41 22 45 27 21 по разрядам справа налево.

число в массиве кладем в очередь отсортировано
Проход: 0 / 2 Разряд: - Шаг: 0 / 0
123
Radix Sort
Поразрядная сортировка
Сортировка по разрядам: числа распределяются по корзинам 0..9 последовательно от младшего разряда к старшему.
Разобрать по шагам в большой версии →

Анализ сложности

Каждый вызов сортировки подсчетом требует времени O(N + K), где K=10 (цифры от 0 до 9). Мы делаем этот вызов D раз. Итоговое время: O(D * (N + K)).

Может показаться, что это чистая O(N). Но вспомните: если максимальное число равно M, то количество цифр D в нем примерно равно log_10(M). Таким образом, истинная сложность зависит от величины чисел. Если мы сортируем очень большие числа, алгоритм может проиграть той же сортировке Хоара из-за накладных расходов на создание очередей и копирование массивов.

Реализация на C

Поразрядная сортировка: код целиком C, 39 строк
// Функция для получения максимума (чтобы узнать число разрядов)
int GetMax(int arr[], int n) {
    int max = arr[0];
    for (int i = 1; i < n; i++)
        if (arr[i] > max) max = arr[i];
    return max;
}

// Устойчивая сортировка подсчетом для конкретного разряда (exp = 1, 10, 100...)
void CountingSortByDigit(int arr[], int n, int exp) {
    int* output = (int*)malloc(n * sizeof(int)); // Временный массив
    int count[10] = {0}; // Очереди для цифр от 0 до 9

    // Считаем частоту каждой цифры в заданном разряде
    for (int i = 0; i < n; i++) count[(arr[i] / exp) % 10]++;

    // Считаем префиксные суммы (для обеспечения устойчивости алгоритма)
    for (int i = 1; i < 10; i++) count[i] += count[i - 1];

    // Восстанавливаем элементы во временный массив (ИДЕМ С КОНЦА ДЛЯ УСТОЙЧИВОСТИ)
    for (int i = n - 1; i >= 0; i--) {
        int digit = (arr[i] / exp) % 10;
        output[count[digit] - 1] = arr[i];
        count[digit]--;
    }

    // Копируем обратно
    for (int i = 0; i < n; i++) arr[i] = output[i];
    free(output);
}

// Главная функция поразрядной сортировки
void RadixSort(int arr[], int n) {
    int max = GetMax(arr, n);
    // Проходим по каждому разряду (exp умножается на 10 на каждом шаге)
    for (int exp = 1; max / exp > 0; exp *= 10) {
        CountingSortByDigit(arr, n, exp);
    }
}

Коротко: главное

  • Поразрядная сортировка (Radix Sort) обрабатывает числа по отдельным разрядам (цифрам).
  • В версии LSD числа сортируются справа налево (от младшего разряда к старшему).
  • Сортировка внутри каждого разряда обязательно должна быть устойчивой (Stable).
  • Время работы составляет O(D * (N + K)), где D - максимальное число разрядов.
  • При очень больших значениях сортируемых элементов алгоритм может проиграть быстрой сортировке из-за накладных расходов.

Проверьте себя

Почему в LSD Radix Sort сортировка внутри отдельного разряда (например, Counting Sort) обязательно должна быть устойчивой (Stable)?

Как вам урок? Спасибо за отзыв!