На этом шаге мы рассмотрим задачи с использованием бинарных деревьев.
Hапомним, что:
"На языке LISP" бинарное дерево поиска состоит из узлов вида:
(Элемент Левое-поддерево Правое-поддерево)
В каждом узле дерева выполнено следующее условие: все элементы из узлов его левого поддерева "в некотором упорядочении" (например, по числовой величине или в алфавитном порядке) предшествуют элементу из узла и соответственно элементы из узлов правого поддерева следуют за ними.
Заметим, что если TREE имеет представление вида
(Корень Левое-поддерево Правое-поддерево)
( Корень Левое-поддерево Правое-поддерево ) ------ --------------- ---------------- ^ ^ ^ ¦ ¦ ¦ (CAR TREE) (CADR TREE) (CADDR TREE)
(DEFUN TEST (LAMBDA NIL (PRINT "Построим дерево со счетчиком повторяющихся элементов!") (SETQ TREE NIL) (LOOP (PRINT "Введите очередной элемент дерева:") (SETQ A (READ)) ( (EQ A '!) ) (PRINT (SETQ TREE (ADDTREE1 A TREE))) (PRINT "---------------------------") ) (PRINT "----------------------------------") (PRINT "Построение дерева: ") (SETQ TREE NIL) (LOOP (PRINT "Введите очередной элемент дерева:") (SETQ A (READ)) ( (EQ A '!) ) (PRINT (SETQ TREE (ADDTREE A TREE))) ) (PRINT "-----------------------------") (PRIN1 "Корень дерева: ") (PRINT (ROOT TREE)) (PRIN1 "Левое поддерево: ") (PRINT (LEFT TREE)) (PRIN1 "Правое поддерево: ") (PRINT (RIGHT TREE)) (PRIN1 "Обход дерева в ширину: ") (PRINT (REMBER NIL (LISTATOMS (UNTREE (TOP TREE) TREE)))) (PRIN1 "Левосторонний обход дерева: ") (PRINT (UNTREE1 TREE)) (PRIN1 "Число уровней в дереве: ") (PRINT (TOP TREE)) (PRIN1 "Количество листьев в дереве: ") (PRINT (NLIST TREE)) (PRIN1 "Копия дерева: ") (PRINT (TCOPY TREE)) (PRINT "-----------------------------") (PRINT "Приступим к поиску элемента в дереве!") (LOOP (PRINT "Введите искомый элемент дерева:") (SETQ A (READ)) ( (EQ A '!) ) (PRINT (SEARCH A TREE)) ) (PRINT "------------------------------") (PRINT "Приступим к удалению элемента!") (LOOP (PRINT "Введите удаляемый элемент дерева:") (SETQ A (READ)) ( (EQ A '!) ) (PRINT (SETQ TREE (DELETE A TREE))) ) (PRINT "------------------------------") (PRINT "Приступим к удалению элемента другим способом!") (LOOP (PRINT "Введите удаляемый элемент дерева:") (SETQ A (READ)) ( (EQ A '!) ) (PRINT (SETQ TREE (DELETE1 A TREE))) ) (PRINT "----------------------------------") (PRINT "Приступим к выделению поддеревьев!") (LOOP (PRINT "Введите какой-либо элемент дерева:") (SETQ A (READ)) ( (EQ A '!) 'END ) (PRINT (PRETREE A TREE)) (PRINT (POSTTREE A TREE)) (PRINT (UNITETREE (PRETREE A TREE) (POSTTREE A TREE))) (PRINT "---------------------------") ) )) ; ------------------------------ (DEFUN ADDTREE1 (LAMBDA (A TREE) ; Функция ADDTREE добавляет в дерево поиска TREE элемент A ; с подсчетом количества поторений элемента A при вводе (COND ( (NULL TREE) (LIST (CONS A 0) NIL NIL) ) ( (EQUAL A (CAAR TREE)) (LIST (CONS A (+ (CDAR TREE) 1)) (CADR TREE) (CADDR TREE)) ) ( (< A (CAAR TREE)) (LIST (CAR TREE) (ADDTREE1 A (CADR TREE)) (CADDR TREE)) ) ( T (LIST (CAR TREE) (CADR TREE) (ADDTREE1 A (CADDR TREE))) ) ) )) ; ----------------------------- (DEFUN ADDTREE (LAMBDA (A TREE) ; Функция ADDTREE добавляет в дерево поиска TREE элемент A (COND ( (NULL TREE) (LIST A NIL NIL) ) ( (EQUAL A (CAR TREE)) TREE ) ( (< A (CAR TREE)) (LIST (CAR TREE) (ADDTREE A (CADR TREE)) (CADDR TREE)) ) ( T (LIST (CAR TREE) (CADR TREE) (ADDTREE A (CADDR TREE))) ) ) )) ; ------------------------- (DEFUN CONSTR (LAMBDA (LST) ; Функция CONSTR строит дерево из списка LST в обратном порядке (COND ( (NULL LST) NIL ) ( T (ADDTREE (CAR LST) (CONSTR (CDR LST))) ) ) )) ; --------------------------- (DEFUN CONSTREE (LAMBDA (LST) ; Функция CONSTR строит дерево из списка LST в прямом порядке (CONSTR (REVERSE LST)) )) ; ---------------------------- (DEFUN SEARCH (LAMBDA (A TREE) ; Функция SEARCH ищет в дереве TREE элемент A. ; В случае успеха функция возвращает поддерево дерева ; TREE, в котором элемент A является корнем; в случае ; неудачного поиска функция возвращает NIL (COND ( (NULL TREE) NIL ) ( (EQUAL A (CAR TREE)) TREE ) ( (< A (CAR TREE)) (SEARCH A (CADR TREE)) ) ( T (SEARCH A (CADDR TREE)) ) ) )) ; ---------------------------------- (DEFUN REPLACE (LAMBDA (OLD NEW LST) ; Замена в списке LST подсписка OLD на подсписок NEW (COND ( (ATOM LST) LST ) ( (EQUAL OLD LST) NEW ) ( T (CONS (REPLACE OLD NEW (CAR LST)) (REPLACE OLD NEW (CDR LST))) ) ) )) ; ------------------------ (DEFUN ROOT (LAMBDA (TREE) ; Функция ROOT возвращает корень дерева TREE (CAR TREE) )) ; ------------------------ (DEFUN LEFT (LAMBDA (TREE) ; Функция возвращает левое поддерево дерева TREE (CADR TREE) )) ; ------------------------- (DEFUN RIGHT (LAMBDA (TREE) ; Функция возвращает правое поддерево дерева TREE (CADDR TREE) )) ; ----------------------------- (DEFUN RIGHTLIST (LAMBDA (TREE) ; Возвращает самый правый лист дерева TREE (COND ( (NULL (RIGHT TREE)) (CAR TREE) ) ( T (RIGHTLIST (RIGHT TREE)) ) ) )) ; ---------------------------- (DEFUN LEFTLIST (LAMBDA (TREE) ; Возвращает самый левый лист дерева TREE (COND ( (NULL (LEFT TREE)) (CAR TREE) ) ( T (LEFTLIST (LEFT TREE)) ) ) )) ; ------------------------------ (DEFUN DELETE (LAMBDA (ATM TREE) ; Удаление узла ATM из дерева TREE ; (нерекурсивный вариант удаления) (SETQ SUBTREE (SEARCH ATM TREE)) (COND ( (NULL SUBTREE) (PRINT "Узла в дереве нет!") ) ( T ; Узел ATM в дереве TREE найден (COND ( (EQUAL SUBTREE (LIST ATM NIL NIL)) ; Найденный узел - лист (REPLACE SUBTREE NIL TREE) ) ( (AND (NOT (NULL (LEFT SUBTREE))) (NOT (NULL (RIGHT SUBTREE)))) ; Найденный узел имеют оба поддерева (SETQ UZEL (RIGHTLIST (LEFT SUBTREE))) (RPLACA SUBTREE UZEL) (COND ( (NULL (RIGHT (LEFT SUBTREE))) (REPLACE (LEFT SUBTREE) (CADR (LEFT SUBTREE)) TREE) ) ( (NULL (LEFT (LEFT SUBTREE))) (REPLACE (LEFT SUBTREE) (CADDR (LEFT SUBTREE)) TREE) ) ( T (REPLACE (LIST UZEL NIL NIL) NIL TREE) ) ) ) ( (NULL (RIGHT SUBTREE)) ; У найденного узла - только левое поддерево (REPLACE SUBTREE (CADR SUBTREE) TREE) ) ( (NULL (LEFT SUBTREE)) ; У найденного узла - только правое поддерево (REPLACE SUBTREE (CADDR SUBTREE) TREE) ) ) ) ) )) ; ------------------------------- (DEFUN DELETE1 (LAMBDA (ATM TREE) ; Удаление узла ATM из дерева TREE ; (рекурсивный вариант удаления) (COND ( (NULL TREE) NIL ) ( (< ATM (ROOT TREE)) (LIST (CAR TREE) (DELETE1 ATM (LEFT TREE)) (RIGHT TREE)) ) ( (> ATM (ROOT TREE)) (LIST (CAR TREE) (LEFT TREE) (DELETE1 ATM (RIGHT TREE))) ) ( T (COND ( (NULL (RIGHT TREE)) (LEFT TREE) ) ( (NULL (LEFT TREE)) (RIGHT TREE) ) ( T (LIST (UD (LEFT TREE)) (DELETE1 (UD (LEFT TREE)) (LEFT TREE)) (RIGHT TREE)) )) ) ) )) ; ---------------------- (DEFUN UD (LAMBDA (TREE) ; Вспомогательныя функция для функции DELETE1 (COND ( (NULL (RIGHT TREE)) (CAR TREE) ) ( T (UD (RIGHT TREE)) ) ) )) ; ----------------------- (DEFUN TOP (LAMBDA (TREE) ; Функция TOP возвращает число уровней в дереве TREE ; (корень дерева расположен на нулевом уровне) (COND ( (NULL TREE) -1 ) ( T (+ 1 (MAX (TOP (LEFT TREE)) (TOP (RIGHT TREE)))) ) ) )) ; ---------------------- (DEFUN MAX (LAMBDA (M N) ; Функция MAX возвращает большее из чисел M и N (COND ( (> M N) M ) ( T N ) ) )) ; ------------------------- (DEFUN NLIST (LAMBDA (TREE) ; Функция NLIST возвращает количество листьев дерева TREE (COND ( (NULL TREE) 0 ) ( (EQUAL (CDR TREE) (LIST NIL NIL)) 1 ) ( T (+ (NLIST (LEFT TREE)) (NLIST (RIGHT TREE))) ) ) )) ; ------------------------- (DEFUN TCOPY (LAMBDA (TREE) ; Функция TCOPY возвращает копию дерева TREE (COND ( (ATOM TREE) TREE ) ( T (CONS (TCOPY (CAR TREE)) (TCOPY (CDR TREE))) ) ) )) ; ----------------------------- (DEFUN PRETREE (LAMBDA (A TREE) ; Функция PRETREE выделяет в отдельное дерево из дерева ; TREE все узлы, предшествующие данному элементу A (COND ( (NULL TREE) NIL ) ( (< (CAR TREE) A) (LIST (CAR TREE) (CADR TREE) (PRETREE A (CADDR TREE))) ) ( T (PRETREE A (CADR TREE)) ) ) )) ; ------------------------------ (DEFUN POSTTREE (LAMBDA (A TREE) ; Функция POSTTREE выделяет в отдельное дерево из дерева ; TREE все узлы, следующие за данным элементом A (COND ( (NULL TREE) NIL ) ( (< (CAR TREE) A) (POSTTREE A (CADDR TREE)) ) ( T (LIST (CAR TREE) (POSTTREE A (CADR TREE)) (CADDR TREE)) ) ) )) ; ------------------------------------ (DEFUN UNITETREE (LAMBDA (TREE1 TREE2) ; Функция UNITETREE объединяет два дерева поиска ; TREE1 и TREE2 в одно дерево поиска (COND ( (NULL TREE1) TREE2 ) ( (NULL TREE2) TREE1 ) ( T (LIST (CAR TREE1) (UNITETREE (PRETREE (CAR TREE1) TREE2) (CADR TREE1)) (UNITETREE (POSTTREE (CAR TREE1) TREE2) (CADDR TREE1))) ) ) )) ; ---------------------------- (DEFUN UNTREE (LAMBDA (M TREE) ; "Грязный" обход дерева TREE в "ширину", начиная ; с 0-го уровня и кончая M-м уровнем (COND ( (EQ M 0) (CAR TREE) ) ( T (LIST (UNTREE (- M 1) TREE) (SEE M TREE)) ) ) )) ; --------------------------- (DEFUN UNTREE1 (LAMBDA (TREE) ; Левосторонний обход дерева TREE (REMBER NIL (LISTATOMS TREE)) )) ; ------------------------- (DEFUN SEE (LAMBDA (N TREE) ; Обход дерева TREE в "ширину" и создание "грязного" ; списка, содержащего вершины N-го уровня дерева (COND ( (EQ N 0) (CAR TREE) ) ( (EQ N 1) (LIST (CAR (CADR TREE)) (CAR (CADDR TREE))) ) ( T (LIST (SEE (- N 1) (CADR TREE)) (SEE (- N 1) (CADDR TREE))) ) ) )) ; ----------------------------- (DEFUN LISTATOMS (LAMBDA (TREE) ; Функция LISTATOMS возвращает список, составленный из ; элементов (включая NIL !), входящих в дерево поиска TREE (COND ( (NULL TREE) NIL) ( (ATOM (CAR TREE)) (CONS (CAR TREE) (LISTATOMS (CDR TREE))) ) ( T (APPEND (LISTATOMS (CAR TREE)) (LISTATOMS (CDR TREE))) ) ) )) ; ----------------------------- (DEFUN REMBER (LAMBDA (ATM LST) ; Функция REMBER возвращает список, в котором удалены ; все вхождения элемента ATM в список LST (COND ( (NULL LST) NIL ) ( (EQ ATM (CAR LST)) (REMBER ATM (CDR LST)) ) ( T (CONS (CAR LST) (REMBER ATM (CDR LST))) ) ) )) ; ------------------------------- (DEFUN APPEND (LAMBDA (LST1 LST2) ; Функция APPEND возвращает список, состоящий из ; элементов списка LST1, добавленных к списку LST2 (COND ( (NULL LST1) LST2 ) ( (NULL LST2) LST1 ) ( T (CONS (CAR LST1) (APPEND (CDR LST1) LST2)) ) ) ))
При работе с двоичными деревьями необходимо уметь:
На следующем шаге мы приведем задачи на использование императивного стиля программирования.