Двоичные деревья поиска. Декартово дерево
Декартово дерево — это двоичное дерево, в узлах которого хранятся: ссылки на правое и левое поддерево, ссылка на родительский узел (необязательно), ключи x и y, которые являются двоичным деревом поиска по ключу x и двоичной кучей по ключу y. А именно, для любого узла дерева n ключи x узлов правого (левого) поддерева больше (меньше либо равны) ключа x узла n, ключи y узлов правого и левого детей больше либо равны ключу y узла n.
Ссылка на родительский узел не обязательна, она желательна только для линейного алгоритма построения дерева.
Декартово дерево - это структура данных, объединяющая в себе бинарное дерево поиска и бинарную кучу (отсюда и второе её название: treap (tree+heap) и дерамида (дерево+пирамида).
type
Treap = ^TNode;
TNode = record
x,y : Integer; { x - ключ, y - приоритет (произвольные сравнимые типы данных) }
Left,Right : Treap; { Левое и правое поддерево }
end;
Merge (склеивание)


{ Слияние декартовых деревьев }
function Merge( L,R : Treap ):Treap;
begin
if L = nil then { Левого поддерева нет }
Result := R { => результат - правое поддерево }
else if R = nil then { Правого поддерева нет }
Result := L { => результат - левое поддерево }
else if L.y > R.y then begin { У левого приоритет выше }
{ => левое становится корнем }
{ Левое поддерево у него сохраняется }
L.Right := Merge(L.Right,R);
Result := L;
end else begin { У правого приоритет выше или равен }
{ Правое поддерево сохраняется }
{ Левое поддерево = результат слияния левого с
левым сыном правого поддерева }
R.Left := Merge(L,R.Left);
Result := R;
end;
end;
Split (разрезание)


{ Операция разрезания дерева T по ключу x на 2 поддерева L и R }
procedure Split( T:Treap; x:Integer; var L,R:Treap);
var Temp : Treap; { Временное дерево }
begin
Temp := nil;
if T = nil then begin { Если дерево пустое }
L := nil; { то результат разрезания пустой }
R := nil;
end else
if T.x <= x then begin
if T.Right = nil then
R := nil
else
Split(T.Right,x,Temp,R);
L := T;
L.Right := Temp;
end else begin
if T.Left = nil then
L := nil
else
Split(T.Left,x,L,Temp);
R := T;
R.Left := Temp;
end;
end;
Добавление новой вершины
{ Добавление вершины с ключём x в дерево T }
procedure Add( var T:Treap; x:integer );
var NV,L,R : Treap;
begin
new(NV); { Создаём новый узел }
NV.x := x; { У него ключ x }
NV.y := Random(High(Integer)); { И случайный приоритет }
NV.Left := nil; { Нет поддеревьев }
NV.Right := nil;
Split(T,x,L,R); { Разрезаем исходное дерево по ключу x }
{ Склеиваем 3 получившихся дерева }
T := Merge(Merge(L,NV),R);
end;
Удаление вершины с заданным ключом x
{ Удаление вершины }
procedure Delete( var T:Treap; x:integer );
var L,R,M,Rs : Treap;
begin
Split(T,x-1,L,R);
Split(R,x,M,Rs);
{ Удаляем одну верхнюю вершину в M }
if M <> nil then begin
if M.Left = nil then
M := M.Right
else
M := M.Left;
end;
{ Сливаем вместе оставшиеся куски }
T := Merge(L,Merge(M,Rs));
end;
Поиск минимума и максимума
{ Поиск минимума в дереве }
function MinT( T:Treap ):integer;
begin
if T = nil then { Если дерево пустое, то минимума нет }
MinT := High(integer)
else if T.Left = nil then { Левого поддерева нет => текущий элемент - минимум }
MinT := T.x
else
MinT := MinT(T.Left); { Минимум в левом поддереве }
end;
{ Поиск максимума в дереве - аналогично минимуму, только идём вправо }
function MaxT( T:Treap ):integer;
begin
if T = nil then
MaxT := Low(Integer)
else if T.Right = nil then
MaxT := T.x
else
MaxT := MaxT(T.Right);
end;
Поиск значения в дереве
{ Поиск значения в дереве }
function Find( T:Treap; x:integer ):boolean;
begin
if T = nil then
Find := false { Не найдено }
else if T.x = x then begin
Find := true; { x найдено в текущей вершине }
end else if x < T.x then
Find := Find(T.Left,x) { Поищем слева }
else
Find := Find(T.Right,x); { Поищем справа }
end;
Вычисление высоты дерева
{ Высота дерева }
function Height( T:Treap ):integer;
begin
if T = nil then
Result := 0
else
Result := max(Height(T.Left),Height(T.Right))+1;
end;
Тестирование всех операций
var
T : Treap = nil;
i,j : Integer;
begin
Randomize;
{ Проверяем что сейчас ничего в дереве нет }
for i := -10 to 10 do
assert( not Find(T,i) );
{ Добавляем вершины }
for i := 1 to 1000000 do begin
Add(T,i);
assert( not Find(T,-1) ); { Вершины -1 по-прежнему нет }
for j := 1 to min(10,i) do { Добавленные вершины есть }
assert( Find(T,j) );
assert( not Find(T,i+1) ); { Вершин с большим номером нет }
end;
Writeln('Высота дерева (оценка: 4*log(N)): ',Height(T));
assert( Find(T,10) );
Delete(T,10);
assert( not Find(T,10) );
end.
Решение задачи поиска чисел в массиве
var
T : Treap = nil;
N,M,x,i : Integer;
begin
Reset(Input,'find.in');
Rewrite(Output,'find.out');
Randomize;
Read(N,M);
for i := 1 to N do begin
Read(x);
Add(T,x);
end;
for i := 1 to M do begin
Read(x);
if Find(T,x) then
Writeln('YES')
else
Writeln('NO');
end;
end.