Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
УП Технология программирования.doc
Скачиваний:
20
Добавлен:
11.06.2015
Размер:
1.69 Mб
Скачать

Обработка деревьев. Рекурсия

Обработка деревьев часто связана с обходом дерева, т.е. с посещением всех его узлов в определённой последовательности. Наиболее популярны два метода обхода дерева – «в глубину» (сначала в глубину, потом в ширину) и « в ширину» (сначала в ширину, потом в глубину).

Глубиной вершины в дереве называется число её предков, т.е. количество шагов вверх от вершины до корня, так что сам корень имеет глубину 0. Все вершины с глубиной n образуют n-й уровень дерева (см. рис. 10). Число узлов n-го уровня называется его шириной.

Поддеревом с корнем в узле N называется часть дерева, состоящая из узла N и всех его потомков. Так, на рис. 10 поддерево с корнем «Пётр» состоит из узлов «Пётр», «Павел», «Мария» и «Алексей». Очевидно, дерево является своим собственным поддеревом, а всякое поддерево является деревом.

Обход дерева «в глубину»

Обход дерева «в глубину» начинается с корня дерева, который в этот момент считается текущим узлом. Для каждого текущего узла применяется следующий алгоритм (его описание приводится на псевдокоде).

Алгоритм обхода «в глубину» для узла N:

Если у N нет потомков, обработать этот узел и завершить алгоритм для него.

Иначе

  • выполнить предварительные действия для N;

  • применить алгоритм обхода «в глубину» для его первого дочернего узла; после завершения алгоритма выполнить следующий шаг;

  • выполнить промежуточные действия для N;

  • применить алгоритм обхода «в глубину» для его второго дочернего узла; после завершения алгоритма выполнить следующий шаг;

  • выполнить промежуточные действия для N;

  • применить алгоритм обхода «в глубину» для его последнего дочернего узла; после завершения алгоритма выполнить следующий шаг;

  • выполнить заключительные действия для N и завершить алгоритм для него.

Конец алгоритма.

Таким образом, обходя дерево с корнем N, алгоритм последовательно обходит поддерево каждого его дочернего узла, каждый раз спускаясь до самого нижнего уровня. Т.е. движение происходит сначала в глубину дерева, а потом в ширину.

Особенностью описанного алгоритма является то, что он обращается сам к себе. В данном случае алгоритм, применённый к узлу дерева, применяет сам себя к его дочерним узлам (и, косвенно, к каждому потомку). Такие алгоритмы называются рекурсивными (повторяющимися, возвращающимися к самому себе). В языках программирования для их описании используются рекурсивные функции (процедуры, методы), которые прямо или косвенно вызывают сами себя. В языке JavaScript для их использования не требуется никаких дополнительных средств.

В качестве примера приведём рекурсивную функцию, которая выводит на экран имена в генеалогическом дереве в порядке «сверху вниз – слева направо».

function inDepth(N) // N – узел дерева

{document.write(N.man+" ");

if (N.f!=null) inDepth(N.f);

if (N.m!=null) inDepth(N.m)

}

Для дерева на рис. 10 будет напечатано

Иван Пётр Павел Алексей Мария Ирина Елена

Нетрудно изменить эту программу, чтобы дерево печаталось «снизу вверх» или «справа налево». Достигается это лишь перестановкой операторов.

Обход дерева «в ширину»

При обходе дерева «в ширину» сначала последовательно посещаются все узлы 0-го уровня (один узел – корень дерева), затем все узлы 1-го уровня, затем 2-го и т.д. Достигается это с помощью дополнительной очереди, в которую сначала заносится корень дерева, а потом для каждого выбираемого из очереди узла дерева в неё заносятся его дочерние узлы. Таким образом, узлы следующего уровня всегда будут стоять в очереди после узлов предыдущего уровня. Обход дерева закончится, когда закончится очередь. Опишем этот алгоритм на псевдокоде.

Алгоритм обхода дерева «в ширину»:

Создать очередь из одного узла – корня дерева.

Пока очередь не пуста, выполнять тело цикла:

  • выбрать первый узел из очереди;

  • обработать его;

  • последовательно занести в очередь его дочерние узлы.

Конец тела цикла.

Конец алгоритма.

Как видим, этот алгоритм не рекурсивный. Его легко реализовать с помощью рассмотренных ранее функций для работы с двухсвязными списками и очередями. Для примера приведём функцию печати генеалоги­ческого дерева по уровням.

function inWidth(R)

{var Q = createQue([R]), N;

while (Q.next != null)

{N = outQue(Q).info;

document.write(N.man + " ");

if (N.f != null) inQue(N.f,Q);

if (N.m != null) inQue(N.m,Q);

}

};

Для дерева на рис. 10 будет напечатано

Иван Пётр Ирина Павел Мария Елена Алексей