1. 1

# конспект · шаг 1 из 1

Конспект раздела: кучи и непересекающиеся множества

выжимка - её можно унести в заметки

Понятия

Очередь с приоритетами - контракт: push, peek, pop экстремума, build. Реализация решает цену, контракт решает пригодность. Двоичная куча: O(log n) / O(1) / O(log n) / O(n).

Просеивание вверх - вставка: элемент в конец массива, потом наверх, пока больше родителя (в max-куче раздела; в min-куче - пока меньше). Одно сравнение на уровень (у просеивания вниз - два).

decrease_key - куча не знает позиций своих элементов, поэтому нужен словарь pos (обновлять ВНУТРИ обмена) или ленивая запись с отбрасыванием устаревших при извлечении.

Стабильность - её нет; даёт ключ-пара (приоритет, счётчик), она же спасает от сравнения объектов.

d-арная куча - дети d·i+1 .. d·i+d, родитель (i−1)//d, высота log_d n: дешевеют вставки, снятие остаётся при своих (d·log_d n).

Сливаемые кучи - лес деревьев на указателях вместо одного дерева в массиве. Биномиальная: деревья B_k по 2^k узлов, их набор повторяет двоичную запись n, слияние - сложение с переносом. Фибоначчи: ленивое сцепление корней, разрез вместо просеивания, уплотнение при extract_min, правило одной потери и каскадный разрез, степень ≤ 1.44·log₂ n.

Union-find - лес представителей: parent[x], корень = представитель, «в одной ли группе» - сравнение find. Объединение по рангу даёт O(log n), плюс сжатие пути - амортизированное O(α(n)), где α - обратная функция Аккермана (не коэффициент заполнения из раздела 7).

Что унести

Цена операций:

структура push pop min decrease_key merge
двоичная куча O(log n) O(log n) O(log n) + словарь O(n)
биномиальная аморт. O(1) O(log n) O(log n) O(log n)
Фибоначчи O(1) аморт. O(log n) аморт. O(1) O(1)
union-find find union
наивный лес до O(n) O(1) после поиска корней
+ ранг O(log n) O(log n)
+ сжатие пути аморт. O(α(n)) аморт. O(α(n))

Два доказательства из раздела стоит уметь воспроизводить:

дерево ранга r держит ≥ 2^r узлов  → r ≤ log₂ n
   (ранг растёт только при слиянии РАВНЫХ, то есть при удвоении)

поддерево узла степени k в куче Фибоначчи содержит ≥ F_(k+2) ≥ φ^k узлов → k ≤ 1.44·log₂ n
   (правило одной потери: вторая потеря отрезает сам узел)

Сквозная мысль раздела - ленивость: не делай работу, которую можно отложить, и прибирайся там, куда всё равно пришёл. Куча Фибоначчи откладывает уплотнение до extract_min; ленивая очередь не удаляет записи, а помечает; union-find не перекрашивает группу, а чинит путь при ближайшем find. Обратная сторона той же мысли - ослабление формы покупает операцию: двоичная куча быстра и компактна, потому что держит форму полного дерева, и по той же причине не умеет сливаться.

Практические выводы, которые расходятся с учебником: - куча Фибоначчи в прикладном коде редкость; чаще выигрывает двоичная куча с ленивым удалением или спаривающаяся куча; - прежде чем менять структуру, проверь ПОРЯДОК операций: слить k равных списков цепочкой - Θ(N·k), турниром - Θ(N·log k), а слияние в обоих случаях одно и то же; - union-find пишут сразу с обеими оптимизациями: это три строки, после которых структура перестаёт быть узким местом.

Ошибки, которые не падают, а тихо портят результат: забытое обновление pos при обмене; ленивое извлечение без сверки приоритета; подвешивание корней без сравнения рангов; попытка разъединить множества.

Куда это ведёт

Обе структуры - рабочие детали графовых алгоритмов. Дейкстра и Прим стоят на очереди с приоритетами (O((V+E)·log V) на двоичной куче, O(E + V·log V) на куче Фибоначчи - разница видна на плотных графах). Крускал стоит на union-find, и там структура дешевле сортировки рёбер. Следующий раздел - представление графов и обход в глубину.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.