Опубликован: 08.04.2009 | Доступ: свободный | Студентов: 485 / 0 | Длительность: 17:26:00
Специальности: Программист
Лекция 14:

Представление множеств. Деревья. Сбалансированные деревья.

14.1.5. Составить программу удаления элемента t из множества, представленного упорядоченным деревом (если его там нет, ничего делать не надо).

Решение.

if root = null then begin
| {дерево пусто, ничего делать не надо}
end else begin
| x := root;
| {осталось удалить t из поддерева с корнем в x; поскольку
|  это может потребовать изменений в отце x, введем
|  переменные  father: 1..n и direction: (l, r);
|  поддерживаем такой инвариант: если x не корень, то father
|  - его отец, а direction равно l или r в зависимости от
|  того, левым или правым сыном является x}
| while ((t < val [x]) and (left [x] <> null)) or
| |     ((t > val [x]) and (right [x] <> null)) do begin
| | if t < val [x] then begin
| | | father := x; direction := l;
| | | x := left [x];
| | end else begin {t > val [x]}
| | | father := x; direction := r;
| | | x := right [x];
| | end;
| end;
| {t = val [x] или t нет в дереве}
| if t = val [x] then begin
| | ..удаление вершины x с отцом father и
| |   направлением direction
| end;
end;

Удаление вершины использует процедуру

procedure make_free (i: integer);
begin
| left [i] := free;
| free := i;
end;

Она включает число i в список свободных. При удалении различаются 4 случая в зависимости от наличия или отсутствия сыновей у удаляемой вершины.

if (left [x] = null) and (right [x] = null) then begin
| {x - лист, т.е. не имеет сыновей}
| make_free (x);
| if x = root then begin
| | root := null;
| end else if direction = l then begin
| | left [father] := null;
| end else begin {direction = r}
| | right [father] := null;
| end;
end else if (left[x]=null) and (right[x] <> null) then begin
| {x удаляется, а right [x] занимает место x}
| make_free (x);
| if x = root then begin
| | root := right [x];
| end else if direction = l then begin
| | left [father] := right [x];
| end else begin {direction = r}
| | right [father] := right [x];
| end;
end else if (left[x] <> null) and (right[x]=null) then begin
| ..симметрично
end else begin {left [x] <> null, right [x] <> null}
| ..удалить вершину с двумя сыновьями
end;

Удаление вершины с двумя сыновьями нельзя сделать просто так, но ее можно предварительно поменять с вершиной, пометка на которой является непосредственно следующим (в порядке возрастания) элементом за пометкой на x.

y := right [x];
father := x; direction := r;
{теперь father и direction относятся к вершине y}
while left [y] <> null do begin
| father := y; direction := l;
| y := left [y];
end;
{val [y] - минимальная из пометок, больших val [x],
 y не имеет левого сына}
val [x] := val [y];
..удалить вершину y (как удалять вершину, у которой нет
  левого сына, мы уже знаем)

14.1.6. Упростить программу удаления, заметив, что некоторые случаи (например, первые два из четырех) можно объединить.

14.1.7. Использовать упорядоченные деревья для представления функций, область определения которых - конечные множества значений типа T, а значения имеют некоторый тип U. Операции: вычисление значения на данном аргументе, изменение значения на данном аргументе, доопределение функции на данном аргументе, исключение элемента из области определения функции.

Решение. Делаем как раньше, добавив еще один массив

func\_val: array [1..n] of U;

если val[x] = t, func\_val[x] = u, то значение хранимой функции на t равно u.

14.1.8. Предположим, что необходимо уметь также отыскивать k -ый элемент множества (в порядке возрастания), причем количество действий должно быть не более C\cdot(\text{высота дерева}). Какую дополнительную информацию надо хранить в вершинах дерева?

Решение. В каждой вершине будем хранить число всех ее потомков. Добавление и исключение вершины требует коррекции лишь на пути от корня к этой вершине. В процессе поиска k -ой вершины поддерживается такой инвариант: искомая вершина является s -ой вершиной поддерева с корнем в x (здесь s и x - переменные).

Оценка количества действий

Для каждой из операций (проверки, добавления и исключения) количество действий не превосходит C\cdot(\text{высота
дерева}). Для "ровно подстриженного" дерева (когда все листья на одной высоте) высота по порядку величины равна логарифму числа вершин. Однако для кривобокого дерева все может быть гораздо хуже: в наихудшем случае все вершины образуют цепь и высота равна числу вершин. Так случится, если элементы множества добавляются в возрастающем или убывающем порядке. Можно доказать, однако, что при добавлении элементов "в случайном порядке" средняя высота дерева будет не больше C\log(\text{число вершин}). Если этой оценки "в среднем" мало, необходимы дополнительные действия по поддержанию "сбалансированности" дерева. Об этом смотри в следующем пункте.