Данная лекция будет посвящена изучению и реализации на Прологе такой структуры данных, как
Начнем с маленького введения из теории
Две вершины ориентированного
Нам будет удобно использовать следующее рекурсивное определение бинарного
В вершинах
DOMAINS
tree=empty;tr(i,tree,tree)
/* дерево либо пусто, либо
состоит из корня (целого числа),
левого и правого поддеревьев,
также являющихся деревьями */
Заметим, что идентификатор empty не является зарезервированным словом Пролога. Вместо него вполне можно употреблять какое-нибудь другое обозначение для пустого nil, как в Лиспе, или void, как в Си. То же самое относится и к имени домена (и имени tree (tr) можно использовать любой другой идентификатор.
Например,

можно задать следующим образом:
tr(2,tr(7,empty, empty),tr(3,tree(4,empty,empty), tr(1,empty,empty))).
Теперь займемся написанием предикатов для реализации операций на бинарных
Пример. Начнем с реализации предиката, который будет проверять принадлежность значения
Следуя рекурсивному определению
Запишем это рассуждение на Прологе.
tree_member(X,tr(X,_,_)):-!. /* X - является корнем
дерева */
tree_member(X,tr(_,L,_)):-
tree_member(X,L),!. /* X принадлежит
левому поддереву */
tree_member(X,tr(_,_,R)):-
tree_member(X,R). /* X принадлежит
правому поддереву */
Пример. Разработаем предикат, который будет заменять в
Базис рекурсивного решения будет следующий. Из пустого
tree_replace(_,_,empty,empty). /* пустое дерево
остается пустым деревом*/
tree_replace(X,Y,tr(X,L,R),tr(Y,L1,R1)):-
/* корень содержит заменяемое
значение X*/
!,tree_replace(X,Y,L,L1),
/* L1 - результат замены
в дереве L всех вхождений X
на Y */
tree_replace(X,Y,R,R1).
/* R1 - результат замены
в дереве R всех вхождений X
на Y */
tree_replace(X,Y,tr(K,L,R),tr(K,L1,R1)):-
/* корень не содержит
заменяемое значение X */
tree_replace(X,Y,L,L1),
/* L1 - результат замены
в дереве L всех вхождений X
на Y */
tree_replace(X,Y,R,R1).
/* R1 - результат замены
в дереве R всех вхождений X
на Y */
Пример. Напишем предикат, подсчитывающий общее количество вершин
Как всегда, пользуемся рекурсией. Базис: в пустом
Пишем:
tree_length (empty,0). /* В пустом дереве
нет вершин */
tree_length(tr(_,L,R),N):-
tree_length (L,N1),
/* N1 - число вершин
левого поддерева */
tree_length (R,N2),
/* N2 - число вершин
правого поддерева */
N=N1+N2+1. /* число вершин
исходного дерева
получается сложением
N1, N2 и единицы */
Пример. Решим еще одну подобную задачу. Разработаем предикат, подсчитывающий не общее количество вершин
Понятно, что, так как в пустом
Запишем:
tree_leaves(empty,0). /* в пустом дереве
листьев нет */
tree_leaves(tr(_,empty,empty),1):-!.
/* в дереве с одним корнем -
один лист */
tree_leaves(tr(_,L,R),N):-
tree_leaves(L,N1),
/* N1 - количество листьев
в левом поддереве */
tree_leaves(R,N2),
/* N2 - количество листьев
в правом поддереве */
N=N1+N2.
Пример. Создадим предикат, находящий сумму чисел, расположенных в вершинах
Идея реализации будет очень простой и немного похожей на подсчет количества вершин. Базис рекурсии: сумма элементов пустого
На Прологе это записывается следующим образом:
tree_sum (empty,0). /* В пустом дереве
вершин нет */
tree_sum(tr(X,L,R),N):-
tree_sum (L,N1),
/* N1 - сумма элементов
левого поддерева */
tree_sum (R,N2),
/* N2 - сумма элементов
правого поддерева */
N=N1+N2+X. /* складываем N1, N2
и корневое значение */
Пример. Создадим предикат, позволяющий вычислить
Базис рекурсии будет основан на том, что max (или max2 ), вычисляющий максимум из двух элементов, был разработан нами еще в третьей лекции. Мы воспользуемся им при вычислении
Получается следующее.
tree_height(empty,0). /* Высота пустого
дерева равна нулю */
tree_height(tr(_,L,R),D) :-
tree_height(L,D1),
/* D1 - высота левого
поддерева */
tree_height(R,D2),
/* D2 - высота правого
поддерева */
max(D1,D2,D_M),
/* D_M - максимум из высот
левого и правого поддеревьев */
D=D_M+1.
/* D - высота дерева получается
путем увеличения числа D_M
на единицу*/
Существует особый вид бинарных
Пример. Усовершенствуем предикат tree_member для проверки принадлежности значения
Модифицированный предикат будет выглядеть следующим образом:
tree_member2(X,tr(X,_,_)):-!. /* X - корень
дерева */
tree_member2(X,tr(K,L,_)):-
X<K,!,
tree_member2(X,L).
/* X - принадлежит
левому поддереву */
tree_member2(X,tr(K,_,R)):-
X>K,!,
tree_member2(X,R).
/* X - принадлежит
правому поддереву */
Пример. Создадим предикат, позволяющий добавить в
Решение, конечно, будет рекурсивным. На чем будет основано наше решение? Наша рекурсия будет основана на двух базисах и двух правилах. Первый базис: если вставлять любое значение в пустое
Запишем на Прологе реализацию этих рассуждений.
tree_insert(X,empty,tr(X,empty,empty)).
/* вставляем X в пустое дерево,
получаем дерево с X в корневой
вершине,пустыми левым и
правым поддеревьями */
tree_insert(X,tr(X,L,R),tr(X,L,R)):-!.
/* вставляем X в дерево
со значением X в корневой
вершине, оставляем исходное
дерево без изменений */
tree_insert(X,tr(K,L,R),tr(K,L1,R)):-
X<K,!,
tree_insert(X,L,L1).
/* вставляем X в дерево
с большим X элементом в
корневой вершине, значит,
нужно вставить X в левое
поддерево исходногодерева */
tree_insert(X,tr(K,L,R),tr(K,L,R1)):-
tree_insert(X,R,R1).
/* вставляем X в дерево
с меньшим X элементом
в корневой вершине, значит,
нужно вставить X в правое
поддерево исходного дерева */
Можно обратить внимание на две особенности работы данного предиката. Во-первых, вершина, содержащая новое значение, будет добавлена в качестве нового
Пример. Создадим предикат, генерирующий
Как можно было заметить, записывать
Предикат будет иметь два аргумента. Первый, входной, будет задавать требуемое количество элементов. Второй, выходной, будет равен сгенерированному
Решение будет, естественно, рекурсивным. Рекурсия по количеству вершин random, рассмотренного в пятой лекции) сгенерировать случайное значение, построить tree_insert.
tree_gen(0,empty):-!. /* ноль вершин
соответствует пустому дереву */
tree_gen (N,T):-
random(100,X),
/* X - случайное число
из промежутка [0,100) */
N1= N-1,
tree_gen (N1,T1),
/* T1 - дерево, имеющее
N-1 вершин */
tree_insert(X,T1,T). /* вставляем X
в дерево T1 */
Обратите внимание на то, что, на самом деле, tree_insert, то можно обратить внимание на то, что в ситуации, когда вставляемое значение уже содержится в random, уже содержится в некоторой вершине
Если нам обязательно нужно по какой-то причине получить
Первый вариант: можно модифицировать предикат, осуществляющий добавление значения в
Другой вариант: можно поменять местами вызов предикатов random и tree_gen и после генерации случайного числа проверять с помощью предиката tree_member2, не содержится ли это значение в уже построенном tree_insert. Если же это значение уже содержится в одной из вершин
Надо заметить, что если задать требуемое количество вершин random (количество различных случайных чисел, генерируемых этим предикатом), мы получим зацикливание. Например, в приведенном выше примере вызывается предикат random(100,X). Этот предикат будет возвращать целые случайные числа из промежутка от 0 до 99. Различных чисел из этого промежутка всего сто. Следовательно, и
Эту проблему можно обойти, если сделать первый аргумент предиката random зависящим от заказанного числа вершин
Пример. Далее логично заняться предикатом, который будет удалять заданное значение из
Реализовать этот предикат оказывается не так просто, как хотелось бы. Без особых проблем можно написать базисы рекурсии для случая, когда удаляемое значение является корневым, а левое или правое поддерево пусты. В этом случае результатом будет, соответственно, правое или левое поддерево. Шаг рекурсии для случая, когда значение, содержащееся в
Есть несколько вариантов разрешения возникшей проблемы. Один из них заключается в следующем. Можно удалить из правого поддерева минимальный элемент (или из левого
Для удаления из
Второе предложение будет задавать шаг рекурсии и выполняться, когда левое поддерево не пусто. В этой ситуации минимальный элемент находится в левом поддереве и его нужно оттуда удалить. Так как минимальное значение нам потребуется, чтобы вставить его в корневую вершину, у этого предиката будет не два аргумента, как можно было бы ожидать, а три. Третий (
Запишем оба эти предиката.
Начнем со вспомогательного предиката, удаляющего минимальный элемент
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 */
Так как в
Данная лекция будет посвящена изучению и реализации на Прологе такой структуры данных, как
Начнем с маленького введения из теории
Две вершины ориентированного
Нам будет удобно использовать следующее рекурсивное определение бинарного
В вершинах
DOMAINS
tree=empty;tr(i,tree,tree)
/* дерево либо пусто, либо
состоит из корня (целого числа),
левого и правого поддеревьев,
также являющихся деревьями */
Заметим, что идентификатор empty не является зарезервированным словом Пролога. Вместо него вполне можно употреблять какое-нибудь другое обозначение для пустого nil, как в Лиспе, или void, как в Си. То же самое относится и к имени домена (и имени tree (tr) можно использовать любой другой идентификатор.
Например,

можно задать следующим образом:
tr(2,tr(7,empty, empty),tr(3,tree(4,empty,empty), tr(1,empty,empty))).
Теперь займемся написанием предикатов для реализации операций на бинарных
Пример. Начнем с реализации предиката, который будет проверять принадлежность значения
Следуя рекурсивному определению
Запишем это рассуждение на Прологе.
tree_member(X,tr(X,_,_)):-!. /* X - является корнем
дерева */
tree_member(X,tr(_,L,_)):-
tree_member(X,L),!. /* X принадлежит
левому поддереву */
tree_member(X,tr(_,_,R)):-
tree_member(X,R). /* X принадлежит
правому поддереву */
Пример. Разработаем предикат, который будет заменять в
Базис рекурсивного решения будет следующий. Из пустого
tree_replace(_,_,empty,empty). /* пустое дерево
остается пустым деревом*/
tree_replace(X,Y,tr(X,L,R),tr(Y,L1,R1)):-
/* корень содержит заменяемое
значение X*/
!,tree_replace(X,Y,L,L1),
/* L1 - результат замены
в дереве L всех вхождений X
на Y */
tree_replace(X,Y,R,R1).
/* R1 - результат замены
в дереве R всех вхождений X
на Y */
tree_replace(X,Y,tr(K,L,R),tr(K,L1,R1)):-
/* корень не содержит
заменяемое значение X */
tree_replace(X,Y,L,L1),
/* L1 - результат замены
в дереве L всех вхождений X
на Y */
tree_replace(X,Y,R,R1).
/* R1 - результат замены
в дереве R всех вхождений X
на Y */
Пример. Напишем предикат, подсчитывающий общее количество вершин
Как всегда, пользуемся рекурсией. Базис: в пустом
Пишем:
tree_length (empty,0). /* В пустом дереве
нет вершин */
tree_length(tr(_,L,R),N):-
tree_length (L,N1),
/* N1 - число вершин
левого поддерева */
tree_length (R,N2),
/* N2 - число вершин
правого поддерева */
N=N1+N2+1. /* число вершин
исходного дерева
получается сложением
N1, N2 и единицы */
Пример. Решим еще одну подобную задачу. Разработаем предикат, подсчитывающий не общее количество вершин
Понятно, что, так как в пустом
Запишем:
tree_leaves(empty,0). /* в пустом дереве
листьев нет */
tree_leaves(tr(_,empty,empty),1):-!.
/* в дереве с одним корнем -
один лист */
tree_leaves(tr(_,L,R),N):-
tree_leaves(L,N1),
/* N1 - количество листьев
в левом поддереве */
tree_leaves(R,N2),
/* N2 - количество листьев
в правом поддереве */
N=N1+N2.
Пример. Создадим предикат, находящий сумму чисел, расположенных в вершинах
Идея реализации будет очень простой и немного похожей на подсчет количества вершин. Базис рекурсии: сумма элементов пустого
На Прологе это записывается следующим образом:
tree_sum (empty,0). /* В пустом дереве
вершин нет */
tree_sum(tr(X,L,R),N):-
tree_sum (L,N1),
/* N1 - сумма элементов
левого поддерева */
tree_sum (R,N2),
/* N2 - сумма элементов
правого поддерева */
N=N1+N2+X. /* складываем N1, N2
и корневое значение */
Пример. Создадим предикат, позволяющий вычислить
Базис рекурсии будет основан на том, что max (или max2 ), вычисляющий максимум из двух элементов, был разработан нами еще в третьей лекции. Мы воспользуемся им при вычислении
Получается следующее.
tree_height(empty,0). /* Высота пустого
дерева равна нулю */
tree_height(tr(_,L,R),D) :-
tree_height(L,D1),
/* D1 - высота левого
поддерева */
tree_height(R,D2),
/* D2 - высота правого
поддерева */
max(D1,D2,D_M),
/* D_M - максимум из высот
левого и правого поддеревьев */
D=D_M+1.
/* D - высота дерева получается
путем увеличения числа D_M
на единицу*/
Существует особый вид бинарных
Пример. Усовершенствуем предикат tree_member для проверки принадлежности значения
Модифицированный предикат будет выглядеть следующим образом:
tree_member2(X,tr(X,_,_)):-!. /* X - корень
дерева */
tree_member2(X,tr(K,L,_)):-
X<K,!,
tree_member2(X,L).
/* X - принадлежит
левому поддереву */
tree_member2(X,tr(K,_,R)):-
X>K,!,
tree_member2(X,R).
/* X - принадлежит
правому поддереву */
Пример. Создадим предикат, позволяющий добавить в
Решение, конечно, будет рекурсивным. На чем будет основано наше решение? Наша рекурсия будет основана на двух базисах и двух правилах. Первый базис: если вставлять любое значение в пустое
Запишем на Прологе реализацию этих рассуждений.
tree_insert(X,empty,tr(X,empty,empty)).
/* вставляем X в пустое дерево,
получаем дерево с X в корневой
вершине,пустыми левым и
правым поддеревьями */
tree_insert(X,tr(X,L,R),tr(X,L,R)):-!.
/* вставляем X в дерево
со значением X в корневой
вершине, оставляем исходное
дерево без изменений */
tree_insert(X,tr(K,L,R),tr(K,L1,R)):-
X<K,!,
tree_insert(X,L,L1).
/* вставляем X в дерево
с большим X элементом в
корневой вершине, значит,
нужно вставить X в левое
поддерево исходногодерева */
tree_insert(X,tr(K,L,R),tr(K,L,R1)):-
tree_insert(X,R,R1).
/* вставляем X в дерево
с меньшим X элементом
в корневой вершине, значит,
нужно вставить X в правое
поддерево исходного дерева */
Можно обратить внимание на две особенности работы данного предиката. Во-первых, вершина, содержащая новое значение, будет добавлена в качестве нового
Пример. Создадим предикат, генерирующий
Как можно было заметить, записывать
Предикат будет иметь два аргумента. Первый, входной, будет задавать требуемое количество элементов. Второй, выходной, будет равен сгенерированному
Решение будет, естественно, рекурсивным. Рекурсия по количеству вершин random, рассмотренного в пятой лекции) сгенерировать случайное значение, построить tree_insert.
tree_gen(0,empty):-!. /* ноль вершин
соответствует пустому дереву */
tree_gen (N,T):-
random(100,X),
/* X - случайное число
из промежутка [0,100) */
N1= N-1,
tree_gen (N1,T1),
/* T1 - дерево, имеющее
N-1 вершин */
tree_insert(X,T1,T). /* вставляем X
в дерево T1 */
Обратите внимание на то, что, на самом деле, tree_insert, то можно обратить внимание на то, что в ситуации, когда вставляемое значение уже содержится в random, уже содержится в некоторой вершине
Если нам обязательно нужно по какой-то причине получить
Первый вариант: можно модифицировать предикат, осуществляющий добавление значения в
Другой вариант: можно поменять местами вызов предикатов random и tree_gen и после генерации случайного числа проверять с помощью предиката tree_member2, не содержится ли это значение в уже построенном tree_insert. Если же это значение уже содержится в одной из вершин
Надо заметить, что если задать требуемое количество вершин random (количество различных случайных чисел, генерируемых этим предикатом), мы получим зацикливание. Например, в приведенном выше примере вызывается предикат random(100,X). Этот предикат будет возвращать целые случайные числа из промежутка от 0 до 99. Различных чисел из этого промежутка всего сто. Следовательно, и
Эту проблему можно обойти, если сделать первый аргумент предиката random зависящим от заказанного числа вершин
Пример. Далее логично заняться предикатом, который будет удалять заданное значение из
Реализовать этот предикат оказывается не так просто, как хотелось бы. Без особых проблем можно написать базисы рекурсии для случая, когда удаляемое значение является корневым, а левое или правое поддерево пусты. В этом случае результатом будет, соответственно, правое или левое поддерево. Шаг рекурсии для случая, когда значение, содержащееся в
Есть несколько вариантов разрешения возникшей проблемы. Один из них заключается в следующем. Можно удалить из правого поддерева минимальный элемент (или из левого
Для удаления из
Второе предложение будет задавать шаг рекурсии и выполняться, когда левое поддерево не пусто. В этой ситуации минимальный элемент находится в левом поддереве и его нужно оттуда удалить. Так как минимальное значение нам потребуется, чтобы вставить его в корневую вершину, у этого предиката будет не два аргумента, как можно было бы ожидать, а три. Третий (
Запишем оба эти предиката.
Начнем со вспомогательного предиката, удаляющего минимальный элемент
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 */
Так как в
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.