:: алгоритмы  и методы :: :: олимпиадные задачи :: :: связь :: :: о сайте ::
Путь: Структуры данных »
 



По книге Laszlo
"Вычислительная геометрия и компьютерная графика на С++"

Связанное двоичное дерево поиска с головным узлом. Связи ленты представлены дугами
Рис. 1: Связанное двоичное дерево поиска с головным узлом. Связи ленты представлены дугами

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

Связанное дерево поиска реализуем в виде класса BraidedSearchTree. Этот класс отличается от класса SearchTree из описания двоичных деревьев поиска по трем существенным признакам. Во-первых, объекты класса BraidedSearchTree обладают связным списком (лентой), который для каждого узла устанавливает ссылки на предшественника и последователя. Во-вторых, объекты класса BraidedSearchTree поддерживают окно, которое в любой момент времени располагается над каким-либо элементом в дереве. Окно служит той же цели, что и в классе List(используется класс Node из реализации кольцевого списка): существует целый ряд операций с окном или с элементом в окне. В-третьих, элемент root класса BraidedSearchTree указывает на головной узел — "псевдокорневой узел", правый потомок которого является фактическим корнем связанного дерева поиска. Если посмотреть на ленту, то узел, содержащий наименьший элемент в дереве, следует за головным узлом, а узел, содержащий самый большой элемент, предшествует головному узлу. Следовательно, головной узел соответствует головной позиции, которая располагается одновременно перед первой позицией и после последней (рис. 1).


  Класс BraidedNode



Узлы связанного дерева поиска являются объектами класса BraidedNode. Поскольку эти узлы действуют одновременно как узлы дерева и как узлы списка, то шаблон класса BraidedNode составим на основе классов TreeNode и Node:

template<class T>
class BraidedNode : public Node, public TreeNode<T> {
 public :
  BraidedNode (T);
  BraidedNode<T> *rchild(void);
  BraidedNode<T> *lchild (void);
  BraidedNode<T> *next (void);
  BraidedNode<T> *prev(void);
  friend class BraidedSearchTree<T>;
};

Конструктор класса BraidedNode явно инициализирует базовый класс TreeNode по своему списку инициализации, а базовый класс Node инициализируется неявно, поскольку его конструктор не имеет параметров:

template<class T> BraidedNode<T>::BraidedNode (Т val) : 
  TreeNode<T> (val)
{
}

Компонентные функции rchild, lchild, next и prev устанавливают четыре ссылки для текущего узла — первые два для дерева поиска, а последние два — для ленты:

template<class T>
BraidedNode<T> *BraidedNode<T>::rchild (void)
{
  return (BraidedNode<T> *)_rchild;
}

template<class T>
BraidedNode<T> *BraidedNode<T>::lchild (void)
{
  return (BraidedNode<T> *) )_lchild;
}

template<class T>
BraidedNode<T> *BraidedNode<T>::next (void)
{
  return (BraidedNode<T> *)_next;
}

template<class T>
BraidedNode<T> *BraidedNode<T>::prev (void)
{
  return (BraidedNode<T> *)_prev;
}

  Класс BraidedSearchTree



Шаблон класса BraidedSearchTree определяется следующим образом:

template<class T> class BraidedSearchTree {
 private:
  BraidedNode<T> *root;    // головной узел
  BraidedNode<T> *win;     // текущее окно
  int (*cmp) (T,T);        // функция сравнения
  void _remove(T, TreeNode<T> * &);
 public:
  BraidedSearchTree (int(*) (T,T) );
  ~BraidedSearchTree (void);
  Т next (void);
  Т prev (void);
  void inorder (void(*) (T) );
  Т val (void);
  bool isFirst (void);
  bool isLast (void);
  bool is Head (void);
  bool isEmpty (void);
  Т find (T);
  Т findMin (void);
  T insert (T);
  void remove (void);
  T removeMin (void);
};

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



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

template<class T>
BraidedSearchTree<T>::BraidedSearchTree (int (*с) (Т, Т) ) :
  cmp(c)
{
  win = root = new BraidedNode<T> (NULL);
}

Деструктор класса удаляет дерево целиком, обращаясь к деструктору головного узла:

template<class T>
BraidedSearchTree<T>::~BraidedSearchTree (void)
{
  delete root;
}

  Использование ленты



Элемент данных win описывает окно для дерева — т. е. элемент win указывает на узел, над которым располагается окно. Компонентные функции next и prew перемещают окно на следующую или предыдущую позиции. Если окно находится в головной позиции, то функция next перемещает его на первую позицию, а функция prew — на последнюю позицию. Обе функции возвращают ссылку на элемент в новой позиции окна:

template<class Т> Т BraidedSearchTree<T>::next (void)
{
  win = win->next();
  return win->val;
}

template<class T> Т BraidedSearchTree<T>::prev(void)
{
  win = win->prev();
  return win->val;
}

Компонентная функция val возвращает ссылку на элемент внутри окна. Если окно находится в головной позиции, то возвращается значение NULL.

template<class T> T BraidedSearchTree<T>::val (void)
{
  return win->val;
}

Симметричный обход выполняется путем прохода по ленте от первой позиции до последней, применяя функцию visit для каждого элемента на своем пути:

template<class T>
void BraidedSearchTree<T>::inorder (void (*visit) (Т) )
{
  BraidedNode<T> *n = root->next();
  while (n != root) {
    (*visit)(n->val);
    n = n->next();
  }
}

Компонентные функции isFirst, isLast и isHead возвращают значение TRUE (истина), если окно находится в первой, последней или головной позициях соответственно.

template<class T> bool BraidedSearchTree<T>::isFirst (void)
{
  return (win == root->next() ) && (root != root->next ());
}

template<class T> bool BraidedSearchTree<T>::isLast (void)
{
  return (win == root->prev() ) && (root != root->next() );
}

template<class T> bool BraidedSearchTree<T>::isHead (void)
{
  return   (win == root);
}

Функция isEmpty возвращает значение TRUE (истина) только если текущее дерево пусто и состоит только из одиночного головного узла:

template<class T> bool BraidsdSearchTree<T>::isEmpty ()
{
  return   (root == root->next() );
}

  Поиск



Для поиска элемента используется компонентная функция find. Эта функция аналогична функции SearchTree::find за исключением того, что она начинает свой поиск с root->rchild(), фактического корня. Если элемент найден, то окно устанавливается на этом элементе.
template<class Т> Т BraidedSearchTree<T>::find (T val)
{
  BraidedNode<T> *n = root->rchild();
  while (n) {
    int result = (*cmp) (val, n->val);
    if (result < 0)
      n = n->lchild();
    else if (result > 0)
	  n = n->rchild();
	else {
      win=n;
      return n->val;
    }
  }
  return NULL;
}

Наименьший элемент в связанном дереве поиска располагается в первой позиции ленты. Компонентная функция findMin перемещает окно на этот наименьший элемент и возвращает указатель на элемент. Если дерево поиска пусто, то возвращается NULL :

template<class Т> Т BraidedSearchTree<T>::findMin (void)
{
  win = root->next ();
  return win->val;
}

  Включение элементов



Новый элемент должен быть внесен в его правильной позиции как в дерево поиска, так и в ленту. Если новый элемент становится левым потомком, то тем самым он становится предшественником предка в ленте. Если элемент становится правым потомком, то он становится последователем предка в ленте. Реализация функции insert одинакова с функцией SearchTree: : insert за исключением дополнительного включения нового узла в ленту. Однако следует заметить, что в функции insert нет необходимости проверки включения в пустое дерево, поскольку головной узел присутствует всегда. Функция располагает окно над только что внесенным элементом и возвращает указатель нa элемент, если включение произошло успешно.

template<class T> T BraidedSearchTree<T>::insert(T val)
{
  int result = 1;
  BraidedNode<T> *p = root;
  BraidedNcde<T> *n = root->rchild();
  while (n) { 
    p = n;
    result = (*cmp) (val, p->val);
    if (result < 0)
      n = p->lchild();
    else if (result > 0)
      n = p->rchild();
    else
      return NULL;
  }
  win = new BraidedNode<T>(val);
  if (result < 0) {
    p->_lchild = win;
    p->prev()->Node::insert(win);
  } else {
    p->_rchild = win;
    p->Node::insert(win);
  }
  return val;
}

  Удаление элементов



Компонентная функция remove удаляет элемент, находящийся в окне и перемещает окно в предыдущую позицию:

template<class T> void BraidedSearchTree<T>::remove(void)
{
  if (win != root)
    remove(win->val, root->_rchild);
}

Элемент val, подлежащий удалению, и указатель n на корень дерева поиска, в котором находится этот элемент, передаются в качестве параметров собственной функции _remove. Эта функция работает подобно своему аналогу с тем же именем в классе SearchTree. Однако при работе со связанным деревом поиска элемент, конечно, должен быть удален как из дерева поиска, так и из ленты:

template<class T>
void BraidedSearchTree<T>::remove(T val, TreeNode<T>* &n)
{
  int result = (*cmp) (val, n->val);
  if (result < 0)
    _remove(val, n->_lchild);
  else if (result > 0)
    _remove(val, n->_rchild);
  else {                         // случай 1
    if (n->_lchild == NULL) {
      BraidedNode<T> *old = (BraidedNode<T>*)n;
      if (win == old)
        win = old->prev();
      n = old->rchild();
      old->Node::remove();
      delete old;
    }
    else if (n->_rchild == NULL) { // случай 2
      BraidedNode<T> *old = (BraidedNode<T>*)n;
      if (win == old)
        win = old->prev();
      n = old->lchild();
      old->Node::remove();
      delete old;
    }
    else  {                       // случай 3
      BraidedNode<T> *m = ( (BraidedNode<T>*)n)->next();
      n->val = m->val;
      _remove(m->val, n->_rchild);
    }
  }
}

Заметим, что функция _remove использует ленту для поиска последователя узла n в случае 3, когда подлежащий удалению узел имеет два непустых потомка. Отметим также, что параметр n является ссылкой на тип TreeNode*, а не на тип BraidedNode*. Это происходит потому, что n указывает на ссылку, хранящуюся в предке узла, подлежащего удалению, а эта ссылка имеет тип TreeNode*. Если бы вместо этого параметру n был присвоен тип BraidedNode*, то это была бы ошибочная ссылка на анонимный объект. При этом поле ссылки в узле предка было бы недоступно.

Компонентная функция removeMin удаляет из дерева наименьший элемент и возвращает указатель на него, функция возвращает значение NULL, если дерево пусто. Если окно содержало удаляемый наименьший элемент, то окно перемещается в головную позицию, в ином случае оно остается без изменения:

template<class T> T BraidedSearchTree<T>::removeMin(void)
{
  Т val = root->next()->val;
  if (root != root->next() )
    _remove(val, root->_rchild);
  return val;
}