Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ФиЛП_материалы / Материалы / Prolog / ПособиеПролог.doc
Скачиваний:
55
Добавлен:
01.06.2015
Размер:
449.02 Кб
Скачать

8. Работа с деревьями и графами

8.1. Двоичные деревья

Двоичное дерево имеет следующее определение:

дерево либо пусто, либо содержит три компоненты:

узел;

левое поддерево;

правое поддерево.

Если узлом может служить любой объект, то поддеревьями могут быть только деревья, в том числе и пустые.

Широко применяемый способ представления двоичных деревьев в Прологе состоит в использовании структуры вида:

tree(узел, левое поддерево, правое_поддерево)

Введем для обозначения пустого дерева константу empty. Дерево на рис.3, имеющее 4 узла, может быть представлено так:

tree( d, tree (c, empty, emty), tree ( f, tree (e, empty, empty), empty).

Рис.3

Двоичное дерево – эффективная структура для организации данных в памяти ЭВМ, в частности, словарей. В упорядоченном словаре любой узел больше узла левого поддерева и меньше узла правого поддерева. Так как в машинном представлении значение символов c<d<f и f>e, то дерево на рис. 3 – упорядоченный двоичный словарь.

Запишем представление дерева, приведенного на рис. 4, в узлах которого записаны имена и числа. Определим узел как структуру node(имя, число)

.

Рис.4

tree (node(вера, 70),

tree(node(борис, 50),

tree(node(анна,24) empty,empty),

tree(node(валентин,26) empty,empty)),

tree(node(дмитрий, 48),

tree(node(владимир,23), empty,empty),

tree(node(федор, 19),empty,empty)))

Примем имя в качестве ключа, которому соответствует число в узле. Так как согласно лексикографическому порядку анна< борис >вера, игорь> борис, дмитрий>вера и владимир<дмитрий< федор, то дерево упорядочено по ключам.

Основными процедурами работы с деревьями вообще и с двоичными упорядоченными деревьями в частности являются следующие [1]:

определение принадлежности элемента дереву;

создание дерева;

обход дерева;

удаление и вставка узла в имеющееся дерево;

сортировка дерева.

Так как дерево – рекурсивная структура данных, то все процедуры, кроме первой, являются рекурсивными.

Рассмотрим процедуру принадлежности. Введем предикат

принадлежит(node(X, _), tree(node(K,_), Left, Right)).

Элемент Х принадлежит двоичному упорядоченному дереву, если:

Х равен ключу K или

если Х меньше ключа, то он находится в узлах левого поддерева или

если Х больше ключа, то он находится в узлах правого поддерева.

Программа поиска числа, ассоциированного с заданным именем, выглядит следующим образом:

DOMAINS

nodetype=node(symbol, integer) %тип узла

treetype = tree(nodetype, treetype, treetype);

empty

PREDICATES

nondeterm принадлежит (nodetype, treetype)

CLAUSES

принадлежит(node(X,V), tree(node(X,V),_,_)).

% терминальный предикат

принадлежит(node(X,V), tree(node(Y,_),L,_)):- X< Y, принадлежит(node(X,V),L).

принадлежит(node(X,V), tree(node(Y,_),_,R)):- X>Y, принадлежит(node(X,V),R).

GOAL

L= tree(node(вера, 70)

tree(node(борис, 50)

tree(node(анна,24) empty,empty)),

tree(node(валентин,26) empty,empty)),

tree(node(дмитрий, 48),

tree(node(владимир,23), empty,empty),

tree(node(федор, 19),empty,empty))),

принадлежит(node(федор,Y), L), write(Y).

/* федор – заданное имя */

Y=19

Для создания словаря необходимо сначала создать дерево из одного узла. Это делается предикатом

создать(node(K,N), tree(node(K,N), empty, empty)),

в котором второй аргумент – новое дерево из одного узла.

Далее следует последовательно вставлять левые или правые поддеревья в зависимости от результата сравнения:

если ключ поддерева меньше ключа дерева, то новое поддерево станет левым;

иначе оно станет правым.

вставить(node(NewK,NewN), tree(node(K,N), L,R),

tree(node(K,N), Left, Right))

В этом предикате второй аргумент является исходным деревом, первый – узлом дерева, которое станет для нового левым или правым поддеревом, третий аргумент – новое дерево.

вставить(node(NewK,NewN), tree(node(K,N), L,R),

tree(node(K,N), NewL, R)):-

NewK<K,!, вставить (node(NewK,NewN), L,NewL).

вставить(node(NewK,NewN), tree(node(K,N), L,R),

tree(node(K,N), L, NewR)):-

вставить(node(NewK,NewN), R,NewR).

Приведем алгоритм обхода дерева:

если дерево пусто, то не совершать никаких действий;

иначе обработать текущее значение;

затем перейти на левое поддерево;

затем перейти на правое поддерево.

Схема рекурсивной процедуры следующая:

обход(empty). % терминальный предикат

обход(tree(node(_,_), Left, Right)):-

/*обработка node*/,

обход (Left),

обход(Right).

Эта процедура наглядно демонстрирует поиск в глубину: спускаемся по левой ветви вниз до пустого поддерева, затем возвращаемся назад и начинаем обход правой ветви.

Вставка и удаление элементов дерева, а также его сортировка подробно рассмотрены в [1, 6].

Соседние файлы в папке Prolog