Деревья
9.2. Произвольное дерево
В произвольном корневом дереве каждая вершина может иметь любое количество поддеревьев (рис. 9.1 (b)).
Полиморфный домен таких деревьев так же, как и в предыдущем случае, определяется рекурсивно:
domains tree{Elem} = t(Elem, tree{Elem}*).
Бинарная структура хранит элемент дерева и список его поддеревьев.
В следующей программе рекурсивно строится дерево потомков человека. Дерево строит предикат createTree/1. Определение этого предиката содержит всего одно правило. Список поддеревьев получается как результат применения предиката высшего порядка list::map/2 к списку "детей" корня дерева. Он строит для каждой вершины поддерево с корнем в этой вершине.
Дерево выводится на печать (предикат print/1), при этом корень дерева располагается слева, а поддеревья справа. Вершины одного уровня находятся на одинаковом расстоянии от левой границы окна консоли. В определении этого предиката участвует предикат второго порядка list::forAll/2.
Построить дерево и вывести его на печать нетрудно и без применения предикатов высшего порядка. Для этого следует ввести дополнительные предикаты, которые обрабатывают списки деревьев:
class predicates createTree: (string Name) -> tree{string}. createTreeList: (string Name*) -> tree{string}*. clauses createTree(X) = t(X, createTreeList([Y || parent(X, Y)])). createTreeList([X | L]) = [createTree(X) | createTreeList(L)]. createTreeList([]) = []. class predicates print: (tree{Elem}). print: (tree{Elem}, charCount). printTreeList: (tree{Elem}*, charCount). clauses print(Tree):- print(Tree, 0). print(t(X, TreeList), N):- write(string::create(N, "\t"), X), nl, printTreeList(TreeList, N + 1). printTreeList([T | L], N):- print(T, N), printTreeList(L, N). printTreeList([], _).
В программе также строится список вершин дерева. Предикат get_nd/1 недетерминированно возвращает произвольную вершину дерева.
class facts - rel parent: (string Родитель, string Ребенок). male: (string). female: (string). domains tree{Elem} = t(Elem, tree{Elem}*). class predicates % построение дерева потомков createTree: (string Name) -> tree{string}. clauses createTree(X) = t(X, list::map([Y || parent(X, Y)], {(Z) = createTree(Z)})). class predicates % печать дерева print: (tree{Elem}). print: (tree{Elem}, charCount). clauses print(Tree):- print(Tree, 0). print(t(X, TreeList), N):- write(string::create(N, "\t"), X), nl, list::forAll(TreeList, {(T):- print(T, N + 1)}). class predicates % обход дерева get_nd: (tree{Elem}) -> Elem multi. clauses get_nd(t(A, _)) = A. get_nd(t(_, TreeList)) = get_nd(list::getMember_nd(TreeList)). run():- file::consult("fam.txt", rel), Tree = createTree("Иван"), print(Tree), nl, write([Elem || Elem = get_nd(Tree)]), _ = readLine().Пример 9.4. Дерево потомков
Ниже реализуются операции над произвольными деревьями. Находится высота дерева, заданное поколение вершин дерева, сумма четных элементов дерева и количество вершин дерева. Выполняется замена элементов дерева другими элементами.
open core, console, list domains tree{Elem} = t(Elem, tree{Elem}*). class facts arc: (unsigned, unsigned). clauses arc(1, 2). arc(1, 3). arc(1, 4). arc(2, 5). arc(2, 6). arc(3, 7). arc(7, 8). arc(7, 9). arc(7, 10). arc(9, 11). class predicates createTree: (unsigned) -> tree{unsigned}. print: (tree{Elem}). print: (tree{Elem}, charCount). clauses createTree(X) = t(X, map([Y || arc(X, Y)], {(Z) = createTree(Z)})). print(Tree):- print(Tree, 0). print(t(X, TreeList), N):- write(string::create(N, "\t"), X), nl, forAll(TreeList, {(T):- print(T, N + 1)}). class predicates % вершины заданного уровня get_nd: (tree{Elem}, positive) -> Elem nondeterm. clauses get_nd(t(A, _), 0) = A:- !. get_nd(t(_, TrList), N) = get_nd(getMember_nd(TrList), N - 1). class predicates % высота дерева height: (tree{Elem}) -> integer. height_nd: (tree{Elem}, integer) -> integer nondeterm. clauses height(Tree) = maximum([N || N = height_nd(Tree, 0)]). height_nd(t(_, []), N) = N. height_nd(t(_, TreeList), N) = height_nd(getMember_nd(TreeList), N + 1). class predicates % подсчеты sum: (tree{unsigned}) -> unsigned. count: (tree{Elem}) -> positive. clauses % сумма четных вершин sum(t(A, TrList)) = C + fold(TrList, {(T, S) = sum(T) + S}, 0):- C = if A mod 2 = 0 then A else 0 end if. % количество всех вершин count(t(_, TrList)) = 1 + fold(TrList, {(T, S) = count(T) + S}, 0). class predicates % замена заданных вершин replace: (tree{unsigned}, unsigned, unsigned) -> tree{unsigned}. clauses replace(t(V, TreeList), A, B) = t(C, map(TreeList, {(T) = replace(T, A, B)})):- C = if V = A then B else V end if. run():- Tree = createTree(1), print(Tree), write("\n\nПоколение 3: ", [Elem || Elem = get_nd(Tree, 3)]), write("\nВысота дерева: ", height(Tree)), write("\nСумма четных элементов дерева: ", sum(Tree)), write("\nКоличество вершин дерева: ", count(Tree)), write("\nЗамена 9 на 100:\n\n"), Tree1 = replace(Tree, 9, 100), print(Tree1), _ = readLine().Пример 9.5. Обработка произвольных деревьев
Упражнение 2. Определите предикаты sum/1, count/1 и replace/3, которые предназначены для вычисления суммы четных элементов дерева, количества всех вершин дерева и замены одного элемента другим, не используя предикаты высшего порядка fold и map класса list. Введите дополнительные предикаты, которые обрабатывают списки деревьев.
В языке Visual Prolog имеются реализации красно-черных деревьев (класс redBlackTree), цифровых деревьев (класс radixTree) и др.
Упражнения
- Замените элементы двоичного дерева заданного уровня на заданный элемент.
- Замените элементы произвольного дерева заданного уровня на заданный элемент.
- Найдите количество всех вершин двоичного дерева.
- Вычислите сумму четных вершин произвольного дерева.
- Найдите количество терминальных вершин двоичного дерева.
- Составьте список терминальных вершин произвольного дерева.
- Вставьте заданное поддерево к заданной вершине произвольного дерева.
- Удалите поддеревья у всех вершин произвольного дерева, равных заданному элементу.
- Найдите путь от одной вершины к другой в дереве, вершины которого хранят попарно различные элементы.
- Вычислите сумму целых чисел, хранящихся в вершинах красного цвета в красно-черном дереве.
- Определите "черную" высоту красно-черного дерева.
- Выведите на печать красно-черное дерево (см. класс redBlackTree) так, чтобы вершины красного цвета печатались красным цветом.