Визуализация
Данные: Случайные
Легенда цветов
Как работает поразрядная сортировка
Идея алгоритма. В отличие от большинства сортировок, поразрядная (Radix Sort) вообще не сравнивает числа друг с другом. В этой анимации наглядно представлена схема LSD (Least Significant Digit - сортировка по разрядам, начиная с младшего): числа распределяются по корзинам в зависимости от их цифр, начиная с единиц и продвигаясь к старшим разрядам.
Порядок фаз (LSD-подход). Алгоритм работает в несколько проходов (их число равно числу разрядов у самого большого числа). На каждом проходе:
- Раздача (Distribute). Каждое число из массива кладется в одну из 10 корзин (от 0 до 9) в зависимости от текущей цифры.
- Сборка (Collect). Числа извлекаются из корзин по порядку (от нулевой до девятой) и снова записываются в массив.
Почему это работает? Секрет в устойчивости процесса сборки. Когда мы собираем числа из корзин, элементы с одинаковой текущей цифрой извлекаются строго в том порядке, в каком они туда попали. Таким образом, сортировка по старшим разрядам не нарушает порядок, установленный на предыдущих шагах по младшим разрядам.
Схемы поразрядной сортировки: LSD и MSD. В зависимости от направления обработки разрядов выделяют две основные схемы:
- LSD (Least Significant Digit) - от младшего разряда к старшему (начиная с единиц). На каждом шаге элементы распределяются по корзинам и собираются обратно в единый массив. Требует устойчивой сборки. Идеально подходит для целых чисел фиксированной длины.
- MSD (Most Significant Digit) - от старшего разряда к младшему. Делит элементы по корзинам, после чего каждая корзина рекурсивно сортируется отдельно (без сквозной сборки массива на каждом шаге). По принципу работы напоминает быструю сортировку (Quick Sort) и префиксные деревья (Trie). Удобна для строк и ключей переменной длины.
Синонимы названия. Этот алгоритм также известен как цифровая сортировка (Digital Sort). Не путайте с карманной/блочной сортировкой (Bucket Sort) - в Bucket Sort элементы распределяются по диапазонным корзинам значений, а не по отдельным цифровым разрядам.
Сложность алгоритма
Сложность - это оценка того, как быстро растет потребление ресурса при росте длины массива n. Ресурса у алгоритма два, поэтому и оценки две: по времени и по памяти. Обе записывают через O-нотацию - у нас есть отдельная анимация о том, как читать такие записи.
По времени
Показывает, как растет число операций
(здесь это раздача по корзинам и сборка) при росте n. Пусть d - количество разрядов в максимальном числе,
а k - система счисления (для десятичной системы k = 10). Если разрядность чисел d мала и фиксирована,
сложность фактически становится линейной O(n), благодаря чему Radix Sort обгоняет алгоритмы сравнения вроде Quick Sort O(n log n) на больших массивах. Лейбл "Сложность" под визуализацией -
время, которое получилось фактически на ваших данных.
| Случай | Время | Комментарий |
|---|---|---|
| Все случаи (лучший, средний, худший) | O(d × (n + k)) | Количество операций зависит от разрядов d и числа корзин k = 10. При фиксированном d алгоритм работает за линейное время O(n) независимо от исходного порядка элементов |
По памяти
Показывает, сколько дополнительной памяти нужно алгоритму. Сам входной массив в эту оценку не входит: дополнительной считается память сверх него.
| Характеристика | Оценка | Комментарий |
|---|---|---|
| Дополнительная память | O(n + k) | Необходима память под 10 корзин (для k=10) и размещение в них элементов массива |
Устойчивость алгоритма
Устойчивой называется сортировка, которая не меняет взаимный порядок равных элементов. Пример: сортируем учеников по оценке, а внутри одной оценки они уже стояли по алфавиту. Устойчивая сортировка сохранит алфавитный порядок внутри каждой оценки, неустойчивая - может перемешать.
| Характеристика | Оценка | Комментарий |
|---|---|---|
| Устойчивость | Да | Это обязательное условие алгоритма. Если бы сортировка по старшему разряду переставляла элементы с одинаковыми цифрами, работа предыдущих проходов (по младшим разрядам) была бы испорчена. |
Примеры реализации поразрядной сортировки (Radix Sort)
Код на C
// Учебная реализация LSD Radix Sort на C (Base-10).
// В production-коде для эффективности используют основание 256 (побайтово)
// и битовые операции вместо деления и взятия остатка по модулю 10.
void counting_sort_for_radix(int arr[], int n, int exp) {
int output[n];
int i, count[10] = {0};
// 1. Подсчет количества вхождений каждой цифры (0-9) в текущем разряде
for (i = 0; i < n; i++)
count[(arr[i] / exp) % 10]++;
// 2. Вычисление префиксных сумм: теперь count[i] содержит
// конечную позицию элементов с цифрой i в массиве output
for (i = 1; i < 10; i++)
count[i] += count[i - 1];
// 3. Сборка массива: идем с конца (n-1 до 0) для сохранения
// устойчивости (относительного порядка одинаковых элементов)
for (i = n - 1; i >= 0; i--) {
output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// 4. Копируем отсортированные по текущему разряду элементы обратно
for (i = 0; i < n; i++)
arr[i] = output[i];
}
void radix_sort(int arr[], int n) {
if (n <= 1) return;
// Находим максимальный элемент для определения количества разрядов
int max = arr[0];
for (int i = 1; i < n; i++)
if (arr[i] > max)
max = arr[i];
// Выполняем сортировку подсчетом для каждого разряда
// exp = 1, 10, 100, 1000...
for (int exp = 1; max / exp > 0; exp *= 10)
counting_sort_for_radix(arr, n, exp);
}
Код на Python
def radix_sort(arr):
# Учебная реализация LSD Radix Sort (база 10).
# На практике в Python встроена Timsort, которая быстрее для большинства задач,
# но Radix Sort отлично подходит для больших массивов целых чисел.
if not arr:
return arr
max_val = max(arr) # Ищем максимум для определения длины
exp = 1 # Множитель разряда (1 - единицы, 10 - десятки...)
# Пока в максимальном числе есть необработанные разряды
while max_val // exp > 0:
# Создаем 10 пустых корзин (списков) для цифр от 0 до 9
buckets = [[] for _ in range(10)]
# 1. Раздача элементов по корзинам в зависимости от текущей цифры
for num in arr:
digit = (num // exp) % 10
buckets[digit].append(num)
# 2. Сборка массива обратно (по порядку от 0 до 9)
# Так как мы используем append, порядок внутри корзины сохраняется (устойчивость)
arr.clear()
for bucket in buckets:
arr.extend(bucket)
# Переходим к следующему старшему разряду
exp *= 10
return arr
Код на JavaScript
function radixSort(arr) {
// Учебная реализация LSD Radix Sort (база 10).
// Показывает наглядное распределение по 10 корзинам.
if (arr.length <= 1) return arr;
// Находим максимальный элемент
let maxVal = Math.max(...arr);
// Проходим по каждому разряду (exp = 1, 10, 100...)
for (let exp = 1; Math.floor(maxVal / exp) > 0; exp *= 10) {
// Инициализируем 10 пустых корзин
let buckets = Array.from({ length: 10 }, () => []);
// 1. Распределяем числа по корзинам (Distribute)
for (let i = 0; i < arr.length; i++) {
let digit = Math.floor(arr[i] / exp) % 10;
buckets[digit].push(arr[i]);
}
// 2. Собираем числа обратно в исходный массив (Collect)
// concat объединяет элементы всех корзин по порядку, сохраняя устойчивость
arr = [].concat(...buckets);
}
return arr;
}
Хотите разобрать тему глубже? В нашем бесплатном курсе есть урок про поразрядную сортировку - с подробной теорией, задачами и проверочными вопросами.