Сибирский университет потребительской кооперации
Опубликован: 04.05.2005 | Доступ: свободный | Студентов: 4131 / 1274 | Оценка: 4.45 / 4.22 | Длительность: 12:28:00
ISBN: 978-5-9556-0034-5
Лекция 10:

Деревья

< Лекция 9 || Лекция 10: 12345 || Лекция 11 >

Пример. Далее логично заняться предикатом, который будет удалять заданное значение из двоичного справочника. У него будет три параметра. Два входных (удаляемое значение и исходное дерево ) и результат удаления первого параметра из второго.

Реализовать этот предикат оказывается не так просто, как хотелось бы. Без особых проблем можно написать базисы рекурсии для случая, когда удаляемое значение является корневым, а левое или правое поддерево пусты. В этом случае результатом будет, соответственно, правое или левое поддерево. Шаг рекурсии для случая, когда значение, содержащееся в корне дерева, отличается от удаляемого значения, также реализуется без проблем. Нам нужно выяснить, меньше удаляемое значение корневого или больше. В первом случае перейти к удалению данного значения из левого поддерева, во втором - к удалению этого значения из правого поддерева. Проблема возникает, когда нам нужно удалить корневую вершину в дереве, у которого и левое, и правое поддеревья не пусты.

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

Для удаления из двоичного справочника вершины, содержащей минимальный элемент, нам понадобится отдельный предикат. Его реализация будет состоять из двух предложений. Первое будет задавать базис рекурсии и срабатывать в случае, когда левое поддерево пусто. В этой ситуации минимальным элементом дерева является значение, находящееся в корне, потому что, по определению двоичного справочника, все значения, находящиеся в вершинах правого поддерева, больше значения, находящегося в корневой вершине. И, значит, нам достаточно удалить корень, а результатом будет правое поддерево.

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

Запишем оба эти предиката.

Начнем со вспомогательного предиката, удаляющего минимальный элемент двоичного справочника.

tree_del_min(tr(X,empty,R), R, X). 
        /* Если левое поддерево пусто, 
         то минимальный элемент - корень, 
         а дерево без минимального 
         элемента - это правое поддерево.*/
tree_del_min(tr(K,L,R), tr(K,L1,R), X):- 
        tree_del_min(L, L1, X). 
        /* Левое поддерево не пусто, 
         значит, оно содержит минимальное 
         значениевсего дерева, 
         которое нужно удалить */

Основной предикат, выполняющий удаление вершины из дерева, будет выглядеть следующим образом.

tree_delete(X,tr(X,empty,R), R):-!. 
        /* X совпадает с корневым 
         значением исходного дерева, 
          левое поддерево пусто */
tree_delete (X,tr(X,L,empty), L):-!. 
        /* X совпадает с корневым 
         значением исходного дерева,
         правое поддерево пусто */
tree_delete (X,tr(X,L,R), tr(Y,L,R1)):- 
        tree_del_min(R,R1, Y). 
          /* X совпадает с корневым 
            значением исходного 
            дерева, причем ни левое, ни 
            правое поддеревья не пусты */
tree_delete (X,tr(K,L,R), tr(K,L1,R)):-
        X<K,!, 
        tree_delete (X,L,L1). 
          /* X меньше корневого значения 
            дерева */
tree_delete (X,tr(K,L,R), tr(K,L,R1)):-
        tree_delete (X,R,R1). 
          /* X больше корневого значения 
            дерева */

Пример. Создадим предикат, который будет преобразовывать произвольный список в двоичный справочник. Предикат будет иметь два аргумента. Первый (входной) - произвольный список, второй (выходной) - двоичный справочник, построенный из элементов первого аргумента.

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

То же самое на Прологе:

list_tree([],empty). /* Пустому списку 
         соответствует пустое дерево */ 
list_tree([H|T],Tr):-
        list_tree(T,Tr1), 
          /* Tr1 - дерево, построенное 
            из элементов хвоста 
            исходного списка */
        tree_insert(H,Tr1,Tr). 
          /* Tr - дерево, полученное 
            в результате вставки 
            головы списка в дерево Tr1 */

Пример. Создадим обратный предикат, который будет "сворачивать" двоичный справочник в список с сохранением порядка элементов. Предикат будет иметь два аргумента. Первый (входной) - произвольный двоичный справочник, второй (выходной) - список, построенный из элементов первого аргумента.

tree_list(empty,[]). /* Пустому дереву 
         соответствует пустой список */
tree_list(tr(K,L,R),S):-
        tree_list(L,T_L), 
           /* T_L - список, 
            построенный из элементов 
            левого поддерева */
        tree_list(R,T_R), 
           /* T_L - список, 
            построенный из элементов 
            правого поддерева */
        conc(T_L,[K|T_R],S). 
           /* S - список, полученный 
            соединением списков T_L 
            и [K|T_R] */

Заметьте, что, используя предикаты list_tree и tree_list, можно отсортировать список, состоящий из различных элементов, переписав его в двоичный справочник, а затем переписав двоичный справочник обратно в список.

Запишем предикат, выполняющий сортировку списка, переписывая его в двоичный список и обратно.

sort_listT(L,L_S):-
      list_tree(L,T), 
        /* T- двоичный справочник, 
         построенный из элементов 
         исходного списка L */
      tree_list(T,L_S). 
        /* L_S - список, построенный из 
         элементов  двоичного 
         справочника T */

Так как в двоичном справочнике все элементы различны, при переписывании списка в двоичный справочник и обратно повторяющиеся элементы будут из него удалены. Неизменным останется количество элементов списка, только если все его элементы были различны.

< Лекция 9 || Лекция 10: 12345 || Лекция 11 >
Виктор Бондарь
Виктор Бондарь

После приведения формулы вида ПНФ к виду ССФ вы получаете формулу, в безквантовой матрице которой дизъюнкт содержит оба контранрных атома:. Как тогда проводить его унификацию, если в случае замены x на f(x) весь дизъюнкт обратится в единицу?

Ольга Потапенко
Ольга Потапенко

никак не могу увидеть тексты самих лекций.