Gnome Sort
Гномья сортировка получила название по методу, которым садовый гном якобы сортирует горшки с цветами: он смотрит на два соседних горшка, и если порядок неправильный - меняет их местами и делает шаг назад, а если порядок правильный - делает шаг вперёд.
Проблема
Сортировка вставками эффективно перемещает элемент на нужное место, но требует внутреннего цикла со своим индексом для сдвига предыдущих элементов. Хочется алгоритма с той же идеей - «протолкнуть неуместный элемент назад», - но выраженного через единственный указатель и минимум управляющей логики, без вложенных циклов.
Решение
Указатель i движется по массиву. Если i в начале массива или a[i-1] <= a[i] - сравниваемая пара уже в порядке, указатель сдвигается вперёд. Если же a[i-1] > a[i] - элементы меняются местами, а указатель сдвигается на шаг назад, чтобы проверить новую пару перед собой. Это ровно тот же эффект, что и сдвиг элемента в сортировке вставками, но без явного внутреннего цикла - вся работа выполняется одним указателем, который то идёт вперёд, то пятится назад.
Как это работает
Прослеживая работу на конкретном примере видно, как именно указатель «пятится». Для массива [5, 2, 8, 1] алгоритм совершает 12 итераций цикла и делает 4 обмена: сначала меняет местами 5 и 2 (i возвращается с 1 на 0), затем после нескольких шагов вперёд встречает пару (8, 1) и запускает цепочку из трёх обменов подряд - 8↔1, 5↔1, 2↔1 - пока единица не займёт своё место в начале массива.
На уже отсортированном массиве из 10 элементов алгоритм делает ровно 10 итераций и 0 обменов - указатель ни разу не отступает назад, что даёт линейное O(n) поведение лучшего случая, идентичное лучшему случаю сортировки вставками.
Обратная картина на массиве [10, 9, 8, ..., 1] (n = 10, обратный порядок): 100 итераций цикла и 45 обменов. Число обменов совпадает с количеством инверсий (пар элементов не в том порядке) в исходном массиве - для полностью обратного массива это n(n-1)/2 = 45, откуда и берётся квадратичный худший случай O(n²).
Каждый обмен в гномьей сортировке - это тройное присваивание (временная переменная, две записи), тогда как сдвиг в сортировке вставками - одна запись. На том же обратно отсортированном массиве из 10 элементов гномья сортировка выполняет 45 × 3 = 135 записей в память, а сортировка вставками - всего 54 записи: одна и та же логическая работа, но в 2.5 раза больше операций записи.
Алгоритм придумал нидерландский информатик Дик Грюне (Dick Grune), изначально назвав его «stupid sort» - «глупая сортировка». Современное имя «gnome sort» он предложил в 2000 году после того, как кто-то в рассылке сравнил метод с гипотетическим способом, которым садовый гном мог бы сортировать горшки с цветами вдоль дорожки - шутка прижилась и стала официальным названием.
Структурно гномья сортировка - это сортировка вставками, у которой внутренний цикл сдвига «развёрнут» в повторные проходы внешнего указателя назад. Там, где вставка сдвигает целый хвост элементов одним циклом с явным индексом, гном делает то же самое по одному попарному обмену за раз, платя за простоту кода дополнительными операциями записи.
Итог: гномья сортировка не даёт никакого асимптотического выигрыша - её ценность полностью учебная. Она показывает, что идею «протолкнуть элемент на место» можно выразить единственным указателем без вложенных циклов, но за это приходится платить тройными обменами вместо одиночных сдвигов.
Нюансы выбора
В учебных курсах как мостик к сортировке вставками - показать, что тот же результат достижим без явного внутреннего цикла, а затем объяснить, почему вставка со сдвигом эффективнее.
Никогда в продакшене - на любом реальном объёме данных сортировка вставками или библиотечная сортировка делает то же самое с меньшим числом операций записи.
В минималистичных средах выполнения (эзотерические языки, микроконтроллеры с крайне ограниченным набором инструкций), где важна компактность кода, а не скорость - единственный указатель проще выразить, чем пару вложенных индексов.
При сравнении с bubble sort - оба алгоритма квадратичны и работают через попарные обмены, но гномья сортировка не требует отдельного флага «была ли перестановка» и явного внешнего прохода, что делает её чуть компактнее в коде.
Примеры в коде
Дик Грюне, автор алгоритма - нидерландский информатик из Vrije Universiteit Amsterdam, также известный работами по компиляторам и синтаксическому анализу; страница алгоритма на его личном сайте (dickgrune.com) - основной первоисточник истории названия.
Учебные курсы по основам алгоритмов используют гномью сортировку как первый пример «сортировки без вложенного цикла» перед тем, как вводить сортировку вставками во всей её полноте.
Реализации на эзотерических языках (Brainfuck и подобные), где минимальный набор инструкций делает управление единственным указателем проще, чем вложенные циклы с несколькими индексами.
Форумы и статьи по занимательным алгоритмам регулярно приводят гномью сортировку как пример того, как переформулировка известного алгоритма через другую структуру управления не меняет асимптотику, а лишь константы.
Когда применять
- Как самый простой способ показать идею «сдвинуть элемент на место», не вводя понятие внутреннего цикла - хороший первый шаг перед сортировкой вставками.
- В средах с крайне ограниченной кодовой базой (например, встраиваемые системы с жёстким лимитом на размер программы), где важна абсолютная простота кода, а не скорость.
Примеры из практики
- Учебные курсы по алгоритмам используют гномью сортировку как забавный, легко запоминающийся пример того, как переформулировать сортировку вставками без вложенных циклов.
- Идея встречается в реализациях Brainfuck и других эзотерических языков для сортировки чисел, так как единственный указатель проще реализовать при минималистичном наборе инструкций.
Похожие алгоритмы
Insertion Sort
Сортировка вставками строит отсортированную часть массива слева направо, забирая по одному элементу из неотсортированной части и вставляя его на правильную позицию.
Bubble Sort
Пузырьковая сортировка многократно проходит по массиву, меняя местами соседние элементы, пока весь массив не окажется упорядочен.