Дерево со сбалансированным весом
В информатике , бинарные деревья со сбалансированным весом ( WBT ) — это тип самобалансирующихся двоичных деревьев поиска которые можно использовать для реализации динамических наборов , словарей (карт) и последовательностей. [1] Эти деревья были представлены Нивергельтом и Рейнгольдом в 1970-х годах как деревья ограниченного баланса , или BB[α]-деревья . [2] [3] Их более распространенное название связано с Кнутом . [4]
Хорошо известным примером является Хаффману кодирование корпуса по .
Как и другие самобалансирующиеся деревья, WBT хранят бухгалтерскую информацию, относящуюся к балансу, в своих узлах и выполняют вращения для восстановления баланса, когда он нарушается операциями вставки или удаления. В частности, каждый узел хранит размер поддерева, корнем которого является узел, а размеры левого и правого поддеревьев сохраняются в пределах некоторого различия друг от друга. В отличие от информации о балансе в деревьях AVL (использующих информацию о высоте поддеревьев) и красно-черных деревьях (которые хранят вымышленный бит «цвета»), информация о балансе в WBT является действительно полезным свойством для приложений: количество элементов в дереве равна размеру его корня, а информация о размере — это именно та информация, которая необходима для реализации операций дерева статистики порядка , а именно: получения n -го по величине элемента в наборе или определения индекса элемента в отсортированный порядок. [5]
Деревья со сбалансированным весом популярны в сообществе функционального программирования и используются для реализации множеств и карт в MIT Scheme , SLIB , SML-NJ и реализациях Haskell . [6] [4]
Описание [ править ]
Дерево со сбалансированным весом — это двоичное дерево поиска, в узлах которого хранятся размеры поддеревьев. То есть узел имеет поля
- ключ любого заказанного типа
- значение (необязательно, только для сопоставлений)
- влево , вправо , указатель на узел
- размер целочисленного типа.
По определению, размер листа (обычно представленного нулевой указатель) равен нулю. Размер внутреннего узла — это сумма размеров двух его дочерних узлов плюс один: ( размер[n] = размер[n.left] + размер[n.right] + 1 ). В зависимости от размера определяется вес, который будет вес[n] = размер[n] + 1 . [а] Преимущество веса состоит в том, что вес узла представляет собой просто сумму весов его левого и правого потомков.

Операции, которые изменяют дерево, должны гарантировать, что вес левого и правого поддеревьев каждого узла остается в пределах некоторого коэффициента α друг от друга, используя те же операции ребалансировки, которые используются в деревьях AVL : повороты и двойные повороты. Формально баланс узла определяется следующим образом:
- Узел является α -весобалансированным, если вес[n.left] ≥ α · вес[n] и вес[n.right] ≥ α · вес[n] . [7]
Здесь α — числовой параметр, который необходимо определить при реализации деревьев со сбалансированным весом. Большие значения α создают «более сбалансированные» деревья, но не все значения α подходят; Нивергельт и Рейнгольд доказали, что
является необходимым условием работы алгоритма балансировки. Более поздние работы показали нижнюю границу 2 ⁄ 11 для α , хотя его можно сделать сколь угодно маленьким, если использовать специальный (и более сложный) алгоритм ребалансировки. [7]
Правильное применение балансировки гарантирует, что дерево из n элементов будет иметь высоту. [7]
Если α задано максимально допустимое значение, наихудшая высота дерева со сбалансированным весом такая же, как у красно-черного дерева в точке .
Количество операций балансировки, требуемых в последовательности из n вставок и удалений, линейно по n , т. е. балансировка требует постоянного объема накладных расходов в амортизированном смысле. [8]
Хотя поддержание дерева с минимальной стоимостью поиска требует четырех видов двойных вращений (LL, LR, RL, RR, как в дереве AVL) в операциях вставки/удаления, если нам нужна только логарифмическая производительность, LR и RL являются единственными необходимыми вращениями. за один проход сверху вниз. [9]
Операции с наборами и массовые операции [ править ]
Для деревьев со сбалансированным весом было определено несколько операций над множествами: объединение , пересечение и разность множеств . быстрые массовые Затем на основе этих функций набора можно реализовать операции по вставке или удалению. Эти операции над множествами основаны на двух вспомогательных операциях: Split и Join . Благодаря новым операциям реализация деревьев со сбалансированным весом может стать более эффективной и поддающейся параллелизации. [10] [11]
- Join : функция Join работает с двумя деревьями со сбалансированным весом t 1 и t 2 и ключом k и возвращает дерево, содержащее все элементы в t 1 , t 2 , а также k . Требуется, чтобы k было больше, чем все ключи в t 1 , и меньше, чем все ключи в t 2 . Если два дерева имеют сбалансированный вес, Join просто создает новый узел с левым поддеревом t 1 , корнем k и правым поддеревом t 2 . Предположим, что t 1 имеет больший вес, чем t 2 (другой случай симметричен). Соединение следует за правым позвоночником t 1 до узла c , который сбалансирован с t 2 . новый узел с левым дочерним элементом c , корнем k и правым дочерним элементом t2 На этом этапе для замены c создается . Новый узел может сделать недействительным инвариант со сбалансированным весом. Это можно исправить с помощью одинарного или двойного поворота, предполагая, что
- Разделение : чтобы разделить дерево со сбалансированным весом на два меньших дерева: меньшее, чем ключ x , и больше, чем ключ x , сначала нарисуйте путь от корня, вставив x в дерево. После этой вставки все значения меньше x будут найдены слева от пути, а все значения больше x будут найдены справа. Применяя Join , все поддеревья с левой стороны объединяются снизу вверх с использованием ключей на пути в качестве промежуточных узлов снизу вверх, чтобы сформировать левое дерево, а правая часть становится симметричной. Для некоторых приложений Split также возвращает логическое значение, указывающее, присутствует ли x в дереве. Стоимость Сплита , порядок высоты дерева. Этот алгоритм на самом деле не имеет ничего общего с какими-либо особыми свойствами дерева со сбалансированным весом и, таким образом, является общим для других схем балансировки, таких как деревья AVL .
Алгоритм соединения следующий:
function joinRightWB(TL, k, TR) (l, k', c) = expose(TL) if balance(|TL|, |TR|) return Node(TL, k, TR) else T' = joinRightWB(c, k, TR) (l', k', r') = expose(T') if (balance(|l|,|T'|)) return Node(l, k', T') else if (balance(|l|,|l'|) and balance(|l|+|l'|,|r'|)) return rotateLeft(Node(l, k', T')) else return rotateLeft(Node(l, k', rotateRight(T')) function joinLeftWB(TL, k, TR) /* symmetric to joinRightWB */ function join(TL, k, TR) if (heavy(TL, TR)) return joinRightWB(TL, k, TR) if (heavy(TR, TL)) return joinLeftWB(TL, k, TR) Node(TL, k, TR)
Здесь баланс значит два веса и сбалансированы. Exposure(v)=(l, k, r) означает извлечение узла дерева левый ребенок , ключ узла и правильный ребенок . Node(l, k, r) означает создание узла левого дочернего элемента , ключ и правильный ребенок .
Алгоритм разделения следующий:
function split(T, k) if (T = nil) return (nil, false, nil) (L, (m, c), R) = expose(T) if (k = m) return (L, true, R) if (k < m) (L', b, R') = split(L, k) return (L', b, join(R', m, R)) if (k > m) (L', b, R') = split(R, k) return (join(L, m, L'), b, R))
Объединение двух сбалансированных по весу деревьев t 1 и t 2, представляющих множества A и B , представляет собой сбалансированное по весу дерево t , которое представляет A ∪ B . Следующая рекурсивная функция вычисляет это объединение:
function union(t1, t2): if t1 = nil: return t2 if t2 = nil: return t1 t<, t> ← split t2 on t1.root return join(union(left(t1), t<), t1.root, union(right(t1), t>))
Здесь Split предполагается, что возвращает два дерева: одно содержит ключи, меньшие, чем входной ключ, а другое — более крупные ключи. (Алгоритм неразрушающий , но существует и деструктивная версия.)
Алгоритм пересечения или различия аналогичен, но требует вспомогательной процедуры Join2 , такой же, как и Join , но без среднего ключа. Благодаря новым функциям объединения, пересечения или разности в дерево со сбалансированным весом можно вставлять или удалять один или несколько ключей. Поскольку Split и Union вызывают Join, но не имеют дело непосредственно с критериями балансировки деревьев со сбалансированным весом, такую реализацию обычно называют алгоритмами на основе соединения .
Сложность каждого объединения, пересечения и различия равна для двух сбалансированных по весу деревьев размером и . Эта сложность оптимальна по количеству сравнений. Что еще более важно, поскольку рекурсивные вызовы объединения, пересечения или различия независимы друг от друга, они могут выполняться параллельно с параллельной глубиной. . [10] Когда , реализация на основе соединения имеет тот же вычислительно- ориентированный ациклический граф (DAG), что и одноэлементная вставка и удаление, если корень большего дерева используется для разделения меньшего дерева.
Примечания [ править ]
Ссылки [ править ]
- ^ Цакалидис, А.К. (1984). «Поддержание порядка в обобщенном связанном списке». Акта Информатика . 21 : 101–112. дои : 10.1007/BF00289142 . S2CID 26127563 .
- ^ Нивергельт, Дж.; Рейнгольд, Э.М. (1973). «Двоичные деревья поиска ограниченного баланса». SIAM Journal по вычислительной технике . 2 : 33–43. дои : 10.1137/0202005 .
- ^
В этой статье использованы общедоступные материалы из Пол Э. Блэк. «Дерево BB(α)» . Словарь алгоритмов и структур данных . НИСТ .
- ^ Jump up to: Перейти обратно: а б с Хираи, Ю.; Ямамото, К. (2011). «Балансировка деревьев со сбалансированным весом» (PDF) . Журнал функционального программирования . 21 (3): 287. doi : 10.1017/S0956796811000104 .
- ^ Рура, Сальвадор (2001). Новый метод балансировки бинарных деревьев поиска . ИКАЛП . Конспекты лекций по информатике. Том. 2076. стр. 469–480. дои : 10.1007/3-540-48224-5_39 . ISBN 978-3-540-42287-7 .
- ^ Jump up to: Перейти обратно: а б Адамс, Стивен (1993). «Функциональные жемчужины: эффективные наборы — баланс» . Журнал функционального программирования . 3 (4): 553–561. дои : 10.1017/S0956796800000885 .
- ^ Jump up to: Перейти обратно: а б с Брасс, Питер (2008). Расширенные структуры данных . Издательство Кембриджского университета. стр. 61–71 .
- ^ Блюм, Норберт; Мельхорн, Курт (1980). «О среднем количестве операций ребалансировки в деревьях со сбалансированным весом» (PDF) . Теоретическая информатика . 11 (3): 303–320. дои : 10.1016/0304-3975(80)90018-3 .
- ^ Чо, Сонхун; Сахни, Сартадж (2000). «Новое бинарное дерево поиска со сбалансированным весом» . Международный журнал основ компьютерных наук . 11 (3): 485–513. CiteSeerX 10.1.1.36.3888 . дои : 10.1142/S0129054100000296 .
- ^ Jump up to: Перейти обратно: а б Блеллок, Гай Э.; Феризович, Дэниел; Сунь, Ихан (2016), «Просто присоединяйтесь к параллельным упорядоченным наборам», Симпозиум по параллельным алгоритмам и архитектурам, Proc. 28-го симпозиума ACM. Параллельные алгоритмы и архитектуры (SPAA 2016) , ACM, стр. 253–264, arXiv : 1602.02120 , doi : 10.1145/2935764.2935768 , ISBN 978-1-4503-4210-0 , S2CID 2897793 .
- ^ Адамс, Стивен (1992), Эффективная реализация множеств на функциональном языке , CiteSeerX 10.1.1.501.8427 .