94
правки
Изменения
Нет описания правки
'''Сплей-дерево (Splay-tree)''' {{---}} это двоичное дерево поиска. Оно позволяет находить быстрее те данные, которые использовались недавно. Относится к разряду сливаемых деревьев. Сплей-дерево было придумано Робертом Тарьяном и Даниелем Слейтером в 1983 году.
==Эвристики==
Для того, чтобы доступ к недавно найденным данным был быстрее, надо, чтобы эти данные находились ближе к корню. Этого мы можем добиться, используя различные эвристики:
* '''Move to Root''' {{---}} совершает повороты вокруг ребра <tex>(x, p)</tex>, где <tex>x</tex> - найденная вершина, <tex>p</tex> - ее предок, пока <tex>x</tex> не окажется корнем дерева. Однако можно построить такую последовательность операций, что амортизированное время доступа к вершине будет <tex> O(n) </tex>.
* '''Splay''' {{---}} также совершает повороты, но чередует различные виды поворотов, благодаря чему достигается логарифмическая амортизированная оценка. Она будет подробно описана ниже.
==Операции со splay-деревом==
===Splay(Tree, x)===
"Splay" делится на 3 случая:
====Zig====
Если <tex>p</tex> - корень дерева с сыном <tex>x</tex>, то совершаем один поворот вокруг ребра <tex>(x, p)</tex>, делая <tex>x</tex> корнем дерева. Данный случай является крайним и выполняется только один раз в конце, если изначальная глубина <tex>x</tex> была нечетной.
[[file:ZigSplay.gif|500px|Zig - поворот]]
====Zig-Zig====
Если <tex>p</tex> - не корень дерева, а <tex>x</tex> и <tex>p</tex> - оба левые или оба правые дети, то делаем поворот ребра <tex>(p, g)</tex>, где <tex>g</tex> отец <tex>p</tex>, а затем поворот ребра <tex>(x, p)</tex>.
[[file:ZigZigSplay.gif|500px|Zig-zig - поворот]]
====Zig-Zag====
Если <tex>p</tex> - не корень дерева и <tex>x</tex> - левый ребенок, а <tex>p</tex> - правый, или наоборот, то делаем поворот вокруг ребра <tex>(x, p)</tex>, а затем поворот нового ребра <tex>(x, g)</tex>, где <tex>g</tex> - бывший родитель <tex>p</tex>.
Данная операция занимает <tex>O(d)</tex> времени, где <tex>d</tex> - длина пути от <tex>x</tex> до корня. В результате этой операции <tex>x</tex> становится корнем дерева, а расстояние до корня от каждой вершины сокращается примерно пополам, что связано с разделением случаев "zig-zig" и "zig-zag".
===Find(Tree, x)===
Эта операция выполняется как для обычного [http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0,_%D0%BD%D0%B0%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F бинарного дерева] , только после нее запускается операция Splay.
===Merge(Tree1, Tree2)===
У нас есть два дерева <tex>Tree1</tex> и <tex>Tree2</tex>, причём подразумевается, что все элементы первого дерева меньше элементов второго. Запускаем Splay от самого большого элемента в дереве <tex>Tree1</tex> (пусть это элемент <tex>i</tex>). После этого корень <tex>Tree1</tex> содержит элемент <tex>i</tex>, при этом у него нет правого ребёнка. Делаем <tex>Tree2</tex> правым поддеревом <tex>i</tex> и возвращаем полученное дерево.
===Split(Tree, x)===
Запускаем Splay от элемента <tex>x</tex> и возвращаем два дерева, полученные отсечением правого или левого поддерева от корня, в зависимости от того, содержит корень элемент больше или не больше, чем <tex>x</tex>.
===Add(Tree, x)===
Запускаем Split(Tree, x), который нам возвращает деревья <tex>Tree1</tex> и <tex>Tree2</tex>, их подвешиваем к <tex>x</tex> как левое и правое поддеревья соответственно.
===Remove(Tree, x)== =
Запускаем Splay от <tex>x</tex> элемента и возвращаем Merge от его детей.
==Анализ операции splay==
Амортизационный анализ сплей-дерева проводится с помощью метода потенциалов. Потенциалом рассматриваемого дерева назовём сумму рангов его вершин. Ранг вершины <tex>x</tex> — это величина, обозначаемая <tex>r(x)</tex> и равная <tex>\log_2 C(x)</tex>, где <tex>C(x)</tex> — количество вершин в поддереве с корнем в <tex>x</tex>.
}}
==Splay-деревья по неявному ключу==
Для операции split нам необходимо хранить число вершин в поддереве. Это число может меняться вовремя операции splay, но при этом его легко поддерживать: мы точно знаем, куда переместятся поддеревья. Тогда после операции мы просто пересчитываем число элементов для 2 (zig) или 3 (zig-zig, zig-zag) вершин. Остальные операции аналогичны [http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B5%D0%BA%D0%B0%D1%80%D1%82%D0%BE%D0%B2%D0%BE_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE_%D0%BD%D0%B5%D1%8F%D0%B2%D0%BD%D0%BE%D0%BC%D1%83_%D0%BA%D0%BB%D1%8E%D1%87%D1%83 декартову дереву по неявному ключу].
==Литература==
*[http://en.wikipedia.org/wiki/Splay_tree Википедия - Splay tree]