2.3.1.9 — последняя статья раздела: связный список (2.3.1.1) с добавленными "экспресс-полосами"2.3.1.9 — the section's last article: a linked list (2.3.1.1) with added "express lanes"

Skip List

Обычный отсортированный связный список ищет за O(n) — нет способа пропустить элементы, не проверив их. Skip list добавляет поверх базового уровня несколько дополнительных "уровней" связей, каждый следующий уровень соединяет случайное подмножество узлов предыдущего — получаются "экспресс-полосы", позволяющие сначала быстро проскочить большую часть списка на верхнем уровне, а затем спуститься на более подробные уровни только рядом с искомым значением.An ordinary sorted linked list searches in O(n) — there's no way to skip elements without checking them. A skip list adds several extra "levels" of links on top of the base level, each successive level connecting a random subset of the previous level's nodes — creating "express lanes" that let a search quickly skip over most of the list at the top level, then descend to more detailed levels only near the target value.

avg O(log n) worst O(n) проще балансировки дереваsimpler than tree balancing

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

Каждый узел получает случайную "высоту" при вставке: подбрасывается монета (или генерируется случайное число), и пока выпадает "орёл" (с вероятностью p, обычно 0.5), высота узла увеличивается на 1 — в среднем высота узла равна 1/(1-p), геометрическое распределение. Узел присутствует на всех уровнях от 0 до своей высоты. Поиск начинается с самого верхнего уровня головного узла и движется вправо, пока следующий узел на этом уровне не превышает искомое значение — тогда поиск спускается на уровень ниже и продолжает оттуда. Инвариант: на уровне i в среднем остаётся n·p^i узлов — уровни становятся экспоненциально более разреженными, что и даёт логарифмическую высоту всей структуры (число уровней ~ log_{1/p}(n)) при линейном ожидаемом числе шагов на уровень.Every node gets a random "height" on insertion: a coin is flipped (or a random number generated), and as long as it comes up "heads" (with probability p, usually 0.5), the node's height grows by 1 — a node's average height is 1/(1-p), a geometric distribution. A node is present at every level from 0 up to its height. Search starts at the head node's topmost level and moves right as long as the next node at that level doesn't exceed the target value — then it drops down a level and continues from there. Invariant: level i has, on average, n·p^i nodes remaining — levels become exponentially sparser, which is exactly what gives the structure its logarithmic height (number of levels ~ log_{1/p}(n)) with a linear expected number of steps per level.

поиск 19: старт с верхнего уровня, спуск при перескоке L2 25 > 19 L1 20 > 19 L0 19 = 19 ✓ 3 шага вместо линейного прохода по всем узлам базового уровня

та же логика "отбросить большую часть за один шаг", что у бинарного поиска, но реализованная через структуру связей, а не индексацию массиваthe same "discard most of it in one step" logic as binary search, but implemented via link structure rather than array indexing

struct Node {
    int val;
    std::vector<Node*> next;  // next[i] — указатель на уровне i// next[i] — pointer at level i
};

bool search(Node* head, int level, int val) {
    Node* cur = head;
    for (int i = level; i >= 0; --i) {
        while (cur->next[i] && cur->next[i]->val < val)
            cur = cur->next[i];               // двигаться направо на текущем уровне// move right at the current level
    }                                          // затем неявно спуститься на уровень ниже// then implicitly drop a level
    cur = cur->next[0];
    return cur && cur->val == val;
}

int random_level() {                        // геометрическое распределение// geometric distribution
    int lvl = 0;
    while ((double)rand() / RAND_MAX < 0.5 && lvl < MAX_LEVEL) ++lvl;
    return lvl;
}

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

Узлы расположены на нескольких уровнях (высота каждого — результат случайного подбрасывания монеты при вставке). Жёлтая подсветка — путь поиска: движение вправо, затем вниз при перескоке цели.Nodes sit at several levels (each one's height is the result of a random coin flip on insertion). Yellow highlights the search path: moving right, then dropping down on overshooting the target.

введите значение, чтобы выполнить операцию сразуenter a value to execute the operation immediately

Почему это O(log n) в среднем, но не гарантированноWhy this is O(log n) on average, but not guaranteed

Высота структуры (число уровней) — случайная величина, определяемая максимальной высотой среди всех вставленных узлов. Ожидаемая максимальная высота среди n независимых геометрических случайных величин — Θ(log n), что и даёт ожидаемую O(log n) сложность поиска. Но теоретически (с исчезающе малой, но ненулевой вероятностью) все n узлов могут получить высоту 0 — тогда структура вырождается в обычный связный список, и поиск деградирует до O(n). Это принципиально отличает skip list от детерминированно сбалансированных деревьев (2.3.2, AVL/Red-Black) — там O(log n) гарантирован структурными инвариантами, а не вероятностью; skip list — сознательный компромисс простоты реализации против абсолютной гарантии.The structure's height (number of levels) is a random variable, determined by the maximum height among all inserted nodes. The expected maximum height among n independent geometric random variables is Θ(log n), giving the expected O(log n) search complexity. But theoretically (with vanishingly small but nonzero probability) all n nodes could get height 0 — the structure would then degenerate into an ordinary linked list, and search would degrade to O(n). This is what fundamentally distinguishes skip lists from deterministically balanced trees (2.3.2, AVL/red-black) — there O(log n) is guaranteed by structural invariants, not probability; a skip list is a deliberate trade-off of implementation simplicity against an absolute guarantee.

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

01

Redis Sorted Sets (ZSET)

Redis реализует упорядоченные множества именно через skip list — простота вставки и удаления без сложных операций перебалансировки (поворотов, перекраски узлов, как в красно-чёрных деревьях) была явно названа авторами Redis причиной этого выбора.Redis implements sorted sets precisely via a skip list — the simplicity of insertion and deletion without complex rebalancing operations (rotations, node recoloring as in red-black trees) was explicitly cited by Redis's authors as the reason for this choice.

02

Memtable в LevelDB и RocksDBThe memtable in LevelDB and RocksDB

Структура в памяти, куда сначала попадают все новые записи перед сбросом на диск в виде SSTable, во многих LSM-движках реализована skip list'ом — конкурентные вставки в skip list проще делать lock-free, чем в сбалансированное дерево, что важно для многопоточной записи.The in-memory structure where all new writes first land before flushing to disk as an SSTable is implemented as a skip list in many LSM engines — concurrent insertions into a skip list are easier to make lock-free than into a balanced tree, which matters for multi-threaded writes.