Материал: АиСД деревья

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

3.4. Обходы бинарных деревьев и леса

Многие алгоритмы работы с бинарными деревьями основаны на последовательной (в определенном порядке) обработке узлов дерева. В этом случае говорят об обходе (прохождении) бинарного дерева. Такой обход порождает определенный порядок перечисления узлов бинарного дерева. Выделяют несколько стандартных вариантов обхода. Будем именовать их в зависимости от того порядка, в котором при этом посещаются корень дерева и узлы левого и правого поддеревьев. Например, при КЛП-обходе сначала посещается корень, затем обходятся в КЛП-порядке последовательно левое и правое поддеревья. Приведем рекурсивные процедуры КЛП-, ЛКП- и ЛПК-обходов, прямо соответствующие их рекурсивным определениям (операция обработки узла обозначена как «посетить (узел)»):

procedure обходКЛП (b: BTree);

{прямой}

begin 

if not Null (b) then

begin

посетить (Root (b));

обходКЛП (Left (b));

обходКЛП (Right (b));

end

end{обходКЛП};

procedure обходЛКП (bBTree);

procedure обходЛПК (bBTree);

{обратный}

{концевой}

begin

begin

if not Null (b) then

if not Null (b) then

begin

begin

обходЛКП (Left (b));

обходЛПК (Left (b));

посетить (Root (b));

обходЛПК (Right (b));

обходЛКП (Right (b));

посетить ( Root (b));

end

end

end{обходЛКП};

end{обходЛПК}

Следует обратить внимание на то, что в литературе используется различная терминология при именовании видов обхода бинарного дерева. Перечислим наиболее популярные варианты терминологии (виды обходов перечислены во всех вариантах в одной и той же последовательности):

1) КЛП, ЛКП, ЛПК [14];

2) прямой, обратный, концевой [10];

3) прямой, симметричный, обратный [13];

4) сверху вниз, слева направо, снизу вверх [5], [6];

5) префиксный (PreOrder), инфиксный (InOrder), постфиксный

(PreOrder) [5], [6].

Иногда обход КЛП называют обходом в глубину. В некоторых случаях используется смешанная терминология [16]. Будем придерживаться далее вариантов терминологии 1 и 2. В варианте 2 названия обходам даны соответственно тому, что в КЛП-порядке корень посещается перед посещением узлов левого поддерева (прямой порядок), а в ЛКП-порядке корень посещается после обхода узлов левого поддерева (обратный порядок). В ЛПК-порядке корень посещается после обхода узлов левого и правого поддеревьев (концевой порядок). Такая терминология основана на важной роли левого поддерева в естественном соответствии бинарного дерева и леса (см. 3.3).

Терминология варианта 5 явно связана с обходом бинарного дерева, представляющего арифметическое выражение с бинарными операциями. Пусть, например, дано арифметическое выражение

(a + b) * c  d / (e + f * g) .

На рис. 3.8 представлено соответствующее ему бинарное дерево.

Error: Reference source not found

Рис. 3.8. Бинарное дерево, представляющее

арифметическое выражение (a + b) * cd / (e + f * g)

Тогда три варианта обхода этого дерева порождают три известные формы записи арифметического выражения:

1) КЛП  префиксную запись

 * + a b c / d + e * f g ;

2) ЛКП  инфиксную запись (без скобок, необходимых для задания последовательности выполнения операций)

a + b * cd / e + f * g ;

3) ЛПК  постфиксную запись

a b + c * d e f g * + /  .

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

Нерекурсивные процедуры обхода бинарных деревьев

Учитывая важность эффективной реализации обходов бинарных деревьев, рассмотрим нерекурсивные процедуры обходов. Общий нерекурсивный алгоритм для всех трех порядков обхода использует стек S для хранения упорядоченных пар (pn), где p  узел бинарного дерева, а n  номер операции, которую надо применить к p, когда пара (pn) будет выбрана из стека. Операции «посетить корень», «пройти левое поддерево», «пройти правое поддерево» нумеруются числами 1, 2, 3 в зависимости от порядка их выполнения в данном варианте обхода. В алгоритме эти операции будут реализованы следующим образом:

посетить корень – посетить (p);

пройти левое поддерево  if not Null (Left (p)) then S  (Left (p), 1);

пройти правое поддерево  if not Null (Right (p)) then S  (Right (p), 1).

Здесь и далее для краткости и наглядности использованы следующие обозначения операций со стеком: S  e вместо := Push (eS) и  S вместо Pop2 (eS).

Тогда общий алгоритм имеет вид:

procedure обход (b: BTree);

var S: Stack of (BTree, operation);

p: BTree; op: operation {=1..3};

begin 

:= Create; S  (b, 1);

while not Null (S) do 

begin (p, op)  S;

if op = 1 then begin S  (p, 2); операцияend

else if op = 2 then begin S  (p, 3); операцияend 

else {op = 3} операция 

end{while}

end{обход}

В случае КЛП-обхода можно существенно упростить алгоритм, исключая лишние для этого варианта манипуляции со стеком. Здесь нет необходимости хранить в стеке номер операции. Итак, конкретизация (с упрощением) общего алгоритма для КЛП-обхода имеет вид:

procedure обход_КЛП (b: BTree); {прямой}

var S: Stack of BTree; p: BTree;

begin 

S:= Create; S  b;

while not Null (S) do 

begin 

p  S; посетить (p);

if not Null (Right (p)) then S  Right (p);

if not Null (Left (p)) then SLeft (p)

end{while}

end{обход_КЛП}

В случае ЛКП-обхода также возможно некоторое упрощение  в стеке сохраняются указания лишь на операции 1 или 2:

procedure обход_ЛКП (b: BTree); {обратный}

var S: Stack of (BTree, operation);

p: BTree; op: operation {=1..2};

begin S:= Create; S  (b , 1);

while not Null (S) do 

begin (p, op)  S;

if op = 1 then

begin S  (p, 2); if not Null (Left (p)) then S  (Left (p), 1)

end 

else {op=2}

begin

посетить (p); if not Null (Right (p)) then S  (Right (p), 1)

end 

end{while}

end{обход_ЛКП}

Конкретизация общего алгоритма для ЛПК-обхода (здесь нет упрощений) имеет вид:

procedure обход_ЛПК (b: BTree); {концевой}

var S: Stack of (BTree, operation);

p: BTree; op: operation {=1..3};

begin S:= Create; S  (b, 1);

while not Null (S) do

begin 

(p, op)  S;

if op = 1 then

begin S  (p, 2); if not Null (Left (p)) then S  (Left (p), 1)

end 

else if op = 2 then 

begin 

S  (p, 3); if not Null (Right (p)) then S  (Right (p), 1)

end else {op=3 } посетить (p)

end{while}

end{обход_ЛПК}

Обходы леса

Следуя естественному соответствию между бинарными деревьями и лесами, можно на основе КЛП-, ЛКП- и ЛПК-обходов бинарного дерева получить три соответствующих порядка прохождения леса (и, следовательно, произвольного дерева).

Прямой порядок: 

а) посетить корень первого дерева;

б) пройти поддеревья первого дерева (в прямом порядке);

в) пройти оставшиеся деревья (в прямом порядке).

Обратный порядок: 

а) пройти поддеревья первого дерева (в обратном порядке);

б) посетить корень первого дерева;

в) пройти оставшиеся деревья (в обратном порядке).

Концевой порядок: 

а) пройти поддеревья первого дерева (в концевом порядке);

б) пройти оставшиеся деревья (в концевом порядке);

в) посетить корень первого дерева.

Более формально обход леса в прямом порядке можно записать следующим образом:

procedure PreOrder (F: Forest); {прямой}

begin 

if not Null (F) then

begin

посетить (Root (Head (F));

PreOrder (Listing (Head (F)));

PreOrder (Tail (F));

end

end{PreOrder}

Аналогично записываются процедуры обхода леса в обратном и концевом порядках.

Если необходимо применить какой-либо из обходов к лесу (дереву), можно сначала построить бинарное дерево, представляющее этот лес, а затем применить соответствующий обход бинарного дерева.

3.5. Представления и реализации бинарных деревьев

Рассмотрим варианты представления и реализации структуры данных бинарного дерева. Пусть базовый тип узлов есть Elem.

Ссылочная реализация бинарного дерева в связанной памяти основана на представлении типа BT (Elem) рекурсивными типами BinT и Node:

type 

BinT = ^Node; {представление бинарного дерева}

Node = record {узел: }

Info: Elem; { содержимое}

LSub: BinT; { левое поддерево}

RSub: BinT { правое поддерево}

end {Node}

Источник: https://studfile.net/preview/15918212/