Для облегчения более эффективной реализация отношения принадлежности применяют различные древовидные структуры. В настоящем разделе мы рассмотрим двоичные деревья.
Двоичное дерево либо пусто, либо состоит из следующих трех частей:
корень
левое поддерево
правое поддерево
Корень может быть чем угодно, а поддеревья должны сами быть двоичными деревьями. На рис. 9.4 показано представление множества [а, b, с, d] двоичным деревом. Элементы множества хранятся в виде вершин дерева. Пустые поддеревья на рис. 9.4 не показаны. Например, вершина b имеет два поддерева, которые оба пусты.
Существует много способов представления двоичных деревьев на Прологе. Одна из простых возможностей - сделать корень главным функтором соответствующего терма, а поддеревья - его аргументами. Тогда дерево рис. 9.4 примет вид
а( b, с( d) )
Такое представление имеет среди прочих своих недостатков то слабое место, что для каждой вершины дерева нужен свой функтор. Это может привести к неприятностям, если вершины сами являются структурными объектами.

Рис. 9. 4. Двоичное дерево.
Существует более эффективный и более привычный способ представления двоичных деревьев: нам нужен специальный символ для обозначения пустого дерева и функтор для построения непустого дерева из трех компонент ( корня и двух поддеревьев). Относительно функтора и специального символа сделаем следующий выбор:
Пусть атом nilпредставляет пустое дерево.
В качестве функтора примем дер, так что дерево с корнем X, левым поддеревом Lи правым поддеревом Rбудет иметь вид терма дер( L, X, R)(см. рис. 9.5).
В этом представлении дерево рис. 9.4 выглядит как
дер( дер( nil, b, nil), a,
дер( дер( nil, d, nil), с, nil) ).
Теперь рассмотрим отношение принадлежности, которое будем обозначать внутри. Цель
внутри( X, Т)
истинна, если Хесть вершина дерева Т. Отношение внутриможно определить при помощи следующих правил:
Х есть вершина дерева Т, если
корень дерева Т совпадает с X, или
Х - это вершина из левого поддерева, или
Х - это вершина из правого поддерева.

Рис. 9. 5. Представление двоичных деревьев.
Эти правила непосредственно транслируются на Пролог следующим образом:
внутри( X, дер( -, X, -) ).
внутри( X, дер( L, -, -) ) :-
внутри( X, L).
внутри( X, дер( -, -, R) ) :-
внутри( X, R).
Очевидно, что цель
внутри( X, nil)
терпит неудачу при любом X.
Посмотрим, как ведет себя наша процедура. Рассмотрим рис. 9.4. Цель
внутри( X, Т)
используя механизм возвратов, находит все элементы данных, содержащиеся в множестве, причем обнаруживает их в следующем порядке:
Х = а; Х = b; Х = с; X = d
Теперь рассмотрим вопрос об эффективности. Цель
внутри( а, Т)
достигается сразу же после применения первого предложения процедуры внутри. С другой стороны, цель
внутри( d, Т)
будет успешно достигнута только после нескольких рекурсивных обращений. Аналогично цель
внутри( е, Т)
потерпит неудачу только после того, как будет просмотрено все дерево в результате рекурсивного применения процедуры внутрико всем поддеревьям дерева Т.
В этом последнем случае мы видим такую же неэффективность, как если бы мы представили множество просто списком. Положение можно улучшить, если между элементами множества существует отношение порядка. Тогда можно упорядочить данные в дереве слева направо в соответствии с этим отношением.

Рис. 9. 6. Двоичный справочник. Элемент 6 найден после прохода по отмеченному пути 5-->8-->6.
Будем говорить, что непустое дерево дер( Лев, X, Прав)упорядочено слева направо, если
(1) все вершины левого поддерева Левменьше X;
(2) все вершины правого поддерева Правбольше X;
(3) оба поддерева упорядочены.
Будем называть такое двоичное дерево двоичным справочником . Пример показан на рис. 9.6.
Читать дальше