
5.3. Обход бд.
Существует достаточно много алгоритмов работы с древовидными структурами, в которых часто встречается понятие обхода (traversing) дерева или "прохода" по дереву. При таком методе исследования дерева каждый узел посещается только один раз, а полный обход задает линейное упорядочивание узлов, что позволяет упростить алгоритм, так как при этом можно использовать понятие "следующий" узел. т.е. узел стоящий после данного при выбранном порядке обхода.
Существует несколько принципиально разных способов обхода дерева:
Прямой (левый) обход ( TLR ): попасть в корень;
обойти левое поддерево;
обойти правое поддерево;
Обратный (симметричный) обход ( LTR ): обойти левое поддерево;
попасть в корень;
обойти правое поддерево;
Концевой (правый) обход ( LRT ): обойти левое поддерево;
обойти правое поддерево;
попасть в корень;
Обход в ширину: при обходе в ширину узлы
посещаются уровень за
уровнем(n-й уровень дерева
- множество узлов с высотой
n). Каждый уровень обходится
слева направо.
Для примера на рис.5.1. эти способы обходов дают следующую последовательность просмотра вершин:
а) прямой - A B D C E G F H J;
б) обратный - D B A E G C H F J;
в) концевой - D B G E H J F C A.
г) в ширину - A B C D E F G H J
Рассмотрим рекурсивную реализацию первых трех способов обхода деревьев.
Рrocedure Obhod (h:u); {способ обхода LTR}
Begin
If h<>nil Then Begin
{1} Obhod (h^. L); {обойти левое поддерево; }
{2} Write (h^. i); { попасть в корень; }
{3} Obhod (h^. R); { обойти правое поддерево;}
End;
End;
Замечания.
1. Все остальные способы обхода реализуются соответствующей перестановкой операторов 1, 2, 3 в процедуре Obhod.
2. Реализация обходов БД с помощью рекурсивной процедуры не вызывает затруднений. В некоторых случаях из соображений эффективности применение явной рекурсии оказывается нежелательным. Следующий очевидный алгоритм реализует наиболее популярный концевой обход LTR без рекурсии, но с использованием стека.
Начиная с корня продвигаемся по левым ссылкам и пройденные вершины запоминаются в стеке. При достижении текущим указателем значения Nil в качестве следующей берется вершина из стека, производятся действия в данной вершине диктуемые конкретной решаемой задачей и следует продвижение по правой ссылке. После обхода всех вершин стек становится пустым и происходит выход.
Пример программной реализации приводится ниже.
{ h – указатель на корень дерева;}
{ sр – указатель стека.}
Рrocedure Obhod ( h :u );
Var р :u;
Begin
р:=h; sр:=Nil;
While ( р < > Nil) Or Not Emрty (sр) Do
If р = Nil
Then Begin р:= Рoр (sр); Writeln (р^. i); р:= р^. R; End
Else Begin Рush (sр, р); р:= р^. L; End;
End;
Для реализации обхода в ширину требуется структура типа «очередь» с методами:
РushQ - поставить в очередь;
РoрQ - взять из очереди;
EmрtyQ - возвращает TRUE, если очередь пуста, иначе - FALSE.
Рrocedure Obhod_Q( h :u );
Var t:u;
Begin
РushQ(h); { корень в очередь}
While Not EmрtyQ Do Begin
t:=РoрQ; {взять из очереди}
Write(t^.i:3) {посетить t }
If t^.L<>Nil Then РushQ(t^.L); { левое поддерево в очередь}
If t^.R<>Nil Then РushQ(t^.R); { правое поддерево в очередь}
End;
End;