Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Алгоритмы / Структуры данных и хэширование / Словари для произвольных данных / Двоичные деревья /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Двоичные деревья поиска: начальные сведения - Программирование от RIN.RU
Двоичные деревья поиска: начальные сведения



Класс SearchTree (дерево поиска)





Определим шаблон нового класса SearchTree для представления двоичного дерева поиска. Класс содержит элемент данных root, который указывает на корень двоичного дерева поиска (объект класса TreeNode) и элемент данных cmp, который указывает на функцию сравнения.


template class SearchTree {
private:
TreeNode *root;
int (*) (T,T) cmp;
TreeNode *_findMin(TreeNode *);
void _remove(T, TreeNode * &);
void _inorder(TreeNode *, void (*) (T) );
public:
SearchTree (int(*) (Т, Т) );
~SearchTree (void);
int isEmpty (void);
Т find(T);
Т findMin(void);
void inorder (void(*) (T) );
void insert(T);
void remove(T);
T removeMin (void);
};


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


Конструкторы и деструкторы


Конструктор SearchTree инициализирует элементы данных cmp для функции сравнения и root для пустого дерева поиска:


template SearchTree::SearchTree (int (*с) (Т, Т) ) :
cmp(с), root (NULL)
{
}


Дерево поиска пусто только, если в элементе данных root содержится нуль (NULL) вместо разрешенного указателя:


template int SearchTree::isEmpty (void)
{
return (root == NULL);
}


Деструктор удаляет все дерево путем обращения к деструктору корня:


template SearchTree::~SearchTree (void)
{
if (root) delete root;
}




Поиск


Чтобы найти заданный элемент val, мы начинаем с корня и затем следуем вдоль ломаной линии уникального пути вниз до узла, содержащего val. В каждом узле n вдоль этого пути используем функцию сравнения для данного дерева на предмет сравнения val с элементом n->val, записанном в n. Если val меньше, чем n->val, то поиск продолжается, начиная с левого потомка узла n, если val больше, чем n->val, то поиск продолжается, начиная с правого потомка n, в противном случае возвращается значение n->val (и задача решена). Путь от корневого узла вниз до val называется путем поиска для val.


Этот алгоритм поиска реализуется в компонентной функции find, которая возвращает обнаруженный ею указатель на элемент или NULL, если такой элемент не существует в дереве поиска.



template T SearchTree::find (T val)
{
TreeNode *n = root;
while (n) {
int result = (*cmp) (val, n->val);
if (result < 0)
n = n->_lchild;
else if (result > 0)
n = n->_rchild;
else
return n->val;
}
return NULL;
}


Этот алгоритм поиска можно сравнить с турниром, в котором участвуют некоторые кандидаты. В начале, когда мы начинаем с корня, в состав кандидатов входят все элементы в дереве поиска. В общем случае для каждого узла n в состав кандидатов входят все потомки n. На каждом этапе производится сравнение val с n->val. Если val меньше, чем n->val, то состав кандидатов сужается до элементов, находящихся в левом поддереве, а элементы в правом поддереве n, как и сам элемент n->val, исключаются из соревнования. Аналогичным образом, если val больше, чем n->val, то состав кандидатов сужается до правого поддерева n. Процесс продолжается до тех пор, пока не будет обнаружен элемент val или не останется ни одного кандидата, что означает, что элемент val не существует в дереве поиска.


Для нахождения наименьшего элемента мы начинаем с корня и прослеживаем связи с левым потомком до тех пор, пока не достигнем узла n, левый потомок которого пуст - это означает, что в узле n содержится наименьший элемент. Этот процесс также можно уподобить турниру. Для каждого узла n состав кандидатов определяется потомками узла n. На каждом шаге из состава кандидатов удаляются те элементы, которые больше или равны n->val и левый потомок n будет теперь выступать в качестве нового n. Процесс продолжается до тех пор, пока не будет достигнут некоторый узел n с пустым левым потомком и, полагая, что не осталось кандидатов меньше, чем n->val, и будет возвращено значение n->val.


Компонентная функция findMin возвращает наименьший элемент в данном дереве поиска, в ней происходит обращение к компонентной функции _findMin, которая реализует описанный ранее алгоритм поиска, начиная с узла n :


template T SearchTree::findMin (void)
{
TreeNode *n = _findMin (root);
return (n ? n->val : NULL);
}


template
TreeNode *SearchTree::_findMin (TreeNode *n)
{
if (n == NULL)
return NULL;
while (n->_lchild)
n = n->_lchild;
return n;
}


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


Симметричный обход


Обход двоичного дерева - это процесс, при котором каждый узел посещается точно только один раз. Компонентная функция inorder выполняет специальную форму обхода, известную как симметричный обход. Стратегия заключается сначала в симметричном обходе левого поддерева, затем посещения корня и потом в симметричном обходе правого поддерева. Узел посещается путем применения функции обращения к элементу, записанному в узле.


Компонентная функция inorder служит в качестве ведущей функции. Она обращается к собственной компонентной функции _inorder, которая выполняет симметричный обход от узла n и применяет функцию visit к каждому достигнутому узлу.


template void SearchTree::inorder (void (*visit) (Т) )
{
_inorder (root, visit);
}


template
void SearchTree::inorder (TreeNode *n, void(*visit) (T)
{
if (n) {
_inorder (n->_lchild, visit);
(*visit) (n->val);
_inorder (n->_rchild, visit);
}
}


При симметричном обходе каждого из двоичных деревьев поиска, показанных на рис. 2, узлы посещаются в возрастающем порядке: 2, 3, 5, 7, 8. Конечно, при симметричном обходе любого двоичного дерева поиска все его элементы посещаются в возрастающем порядке. Чтобы выяснить, почему это так, заметим, что при выполнении симметричного обхода в некотором узле n элементы меньше, чем n->val посещаются до n, поскольку они принадлежат к левому поддереву n, а элементы больше, чем n->val посещаются после n, поскольку они принадлежат правому поддереву n. Следовательно, узел n посещается в правильной последовательности. Поскольку n - произвольный узел, то это же правило соблюдается для каждого узла.


Компонентная функция inorder обеспечивает способ перечисления элементов двоичного дерева поиска в отсортированном порядке. Например, если а является деревом поиска SearchTree для строк, то эти строки можем напечатать в лексикографическом порядке одной командой а.inorder(printstring). Для этого функция обращения printstring может быть определена как:


void printstring(char *s)
{
cout << s << "\n";
}


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


<<<  НазадВперед  >>>
 1  2  3  4 


 8  Комментарии к статье  8 8  Обсудить в чате

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь
унф 1с купить