2.2.1.10 — bubble sort, который не тратит проход впустую в одну сторону2.2.1.10 — bubble sort that doesn't waste a pass going only one way

Шейкерная СортировкаCocktail Shaker Sort

Bubble sort (2.2.1.3) всегда идёт слева направо, поэтому мелкий элемент у правого края доходит до своего места за один проход, а мелкий элемент у левого края — только за один шаг влево на проход, что при неудачном расположении даёт худший случай, растянутый на весь массив ("черепахи"). Шейкерная сортировка чередует направление — проход вправо толкает максимум к концу, проход влево толкает минимум к началу — устраняя именно этот перекос.Bubble sort (2.2.1.3) always goes left to right, so a small element near the right edge reaches its spot in one pass, while a small element near the left edge only moves one step left per pass, which in an unlucky arrangement stretches the worst case across the whole array ("turtles"). Cocktail shaker sort alternates direction — a rightward pass pushes the maximum toward the end, a leftward pass pushes the minimum toward the start — eliminating exactly that asymmetry.

stable in-place adaptive

Идея и инвариантIdea & invariant

Поддерживается сужающееся "рабочее окно" [lo, hi] — снаружи него элементы уже на своих финальных местах. Проход слева направо по [lo, hi] переставляет соседей не по порядку (как в bubble sort) — по его завершении максимум окна гарантированно оказался в позиции hi, и hi уменьшается на 1. Симметричный проход справа налево по новому [lo, hi] точно так же гарантированно доносит минимум окна до позиции lo, и lo увеличивается на 1. Если за проход в любую сторону не произошло ни одной перестановки — массив уже отсортирован, и можно остановиться раньше, не дожидаясь, пока окно схлопнется до пустого.A shrinking "working window" [lo, hi] is maintained — outside it, elements are already in their final spots. A left-to-right pass over [lo, hi] swaps out-of-order neighbors (like bubble sort) — by its end, the window's maximum is guaranteed to be at position hi, and hi decreases by 1. A symmetric right-to-left pass over the new [lo, hi] equally guarantees the window's minimum lands at position lo, and lo increases by 1. If either pass makes no swaps at all, the array is already sorted, and the loop can stop early instead of waiting for the window to collapse to empty.

[lo, hi] проход →: максимум окна прибывает в hi проход ←: минимум окна прибывает в lo, затем [lo,hi] сужается с обеих сторон

каждый полный цикл (→ затем ←) сужает рабочее окно на 2 элемента вместо одного, как у одностороннего bubble sorteach full cycle (→ then ←) shrinks the working window by 2 elements instead of one, as in one-directional bubble sort

void cocktail_shaker_sort(std::vector<int>& a) {
    int lo = 0, hi = (int)a.size() - 1;
    bool swapped = true;
    while (swapped) {
        swapped = false;
        for (int i = lo; i < hi; ++i)           // проход вправо// rightward pass
            if (a[i] > a[i + 1]) { std::swap(a[i], a[i + 1]); swapped = true; }
        if (!swapped) break;
        --hi;
        swapped = false;
        for (int i = hi; i > lo; --i)           // проход влево// leftward pass
            if (a[i - 1] > a[i]) { std::swap(a[i - 1], a[i]); swapped = true; }
        ++lo;
    }
}

Демонстрация вживуюLive demonstration

Синяя рамка — текущее рабочее окно [lo, hi]. Стрелка в логе показывает направление текущего прохода. Зелёная заливка — элементы, уже вышедшие за пределы окна (на финальных местах).The blue outline is the current working window [lo, hi]. The log's arrow shows the current pass direction. Green fill marks elements already outside the window (in their final spots).

сравнений:comparisons: 0   перестановок:swaps: 0

нажмите «Вперёд» или «Играть», чтобы начатьpress "Forward" or "Play" to begin

Что реально улучшается, а что нетWhat actually improves, and what doesn't

Асимптотика в худшем и среднем случае остаётся Θ(n²) — та же, что у bubble sort, потому что число сравнений на массиве общего положения не меняется от смены направления. Выигрыш конкретный и ограниченный: устраняется конкретный патологический случай "черепахи у левого края" ([2, 3, 4, ..., n, 1] — маленький элемент почти в конце требует n-1 проходов обычного bubble sort, а шейкерной сортировке хватает одного обратного прохода). На случайных данных практический выигрыш умеренный — обычно в 1.5-2 раза меньше сравнений, не смена класса сложности.Worst- and average-case asymptotics stay Θ(n²) — the same as bubble sort, because the number of comparisons on a general-position array doesn't change with the direction switch. The gain is specific and bounded: it eliminates a particular pathological case, "turtles near the left edge" ([2, 3, 4, ..., n, 1] — a small element almost at the end needs n-1 passes of plain bubble sort, while shaker sort needs just one reverse pass). On random data the practical gain is modest — typically 1.5-2x fewer comparisons, not a change of complexity class.

Где встречаетсяWhere it shows up

01

Учебная иллюстрация "почему направление прохода имеет значение"A teaching illustration of "why pass direction matters"

Чаще встречается как концептуальный пример в курсах по алгоритмам, показывающий, что даже незначительное изменение простого алгоритма может устранить конкретный класс патологических входов, не меняя общую асимптотику — важное различие для собеседований.More commonly seen as a conceptual example in algorithms courses, showing that even a small tweak to a simple algorithm can eliminate a specific class of pathological inputs without changing the overall asymptotics — an important distinction in interviews.

02

Малые встроенные массивы данных в игровых движкахSmall embedded data arrays in game engines

Для очень маленьких, часто почти отсортированных массивов (например, списка ближайших врагов по расстоянию, пересчитываемого каждый кадр) простота и адаптивность шейкерной сортировки иногда предпочтительнее накладных расходов quicksort/merge sort.For very small, often nearly-sorted arrays (e.g. a per-frame list of nearby enemies by distance), the simplicity and adaptivity of shaker sort is sometimes preferable to the overhead of quicksort/merge sort.