7. Дървета - втора част

Двоични дървета

Двоично дърво е наредено дърво, в което всеки възел има най-много две деца (наследници). Двоичното дърво е правилно, ако всеки вътрешен възел има точно две деца.

  • Двоично дърво е дърво със следните свойства:
    • Всеки вътрешен възел има две деца.
    • Децата на възел са наредена двойка.
  • Ще наричаме децата на вътрешен възел ляво дете и дясно дете.
  • Алтернативна рекурсивна (рекурентна) дефиниция: двоично дърво е или
    • дърво, състояща се от един възел или
    • дърво, чийто корен има наредена двойка наследници, всеки от които е двоично дърво.

Примери: аритметичен израз, дърво на решенията

Дърво за аритметичен израз


  • Двоично дърво, свързано с аритметичен израз:
    • вътрешни възли - операции;
    • външни възли - операнди (аргументи).
  • Пример: дърво за аритметичния израз
    2 * (a −1) + (3 * b)

Дърво на решенията

Двоично дърво АТД

Интерфейс за двоично дърво

html-6.14a (InspectableBinaryTree)
html-6.14b (BinaryTree)

Свойства на двоичните дървета

Множеството от всички възли на двоичното дърво T, които имат една и съща дълбочина d, се нарича ниво d на T.
Нека T е правилно двоично дърво с n възли и нека h е височината на T. Тогава T има следните свойства:
  1. Броят на външните възли e на T е: h + 1 e 2h
  2. Броят на вътрешните възли i на T е: h i 2h 1
  3. Общият брой на възлите n на T е: 2h + 1 n 2h+1 1
  4. Височината h на T е: log(n + 1) 1 h (n 1)/2
В правилното двоично дърво T, броят на външните възли e е с 1 повече от броя на вътрешните възли i, т.е. e = i + 1.



  • Означения:
    n - брой на възлите;
    e - брой на външните възли;
    i - брой на вътрешните възли;
    h - височина на дървото.
  • Свойства:
    • e = i + 1
    • n = 2e − 1
    • h ≤ i
    • h ≤ (n − 1)/2
    • e ≤ 2h
    • h ≥ log2e
    • h ≥ log2(n + 1) − 1

Обхождане на двоично дърво

Preorder обхождане на двоично дърво
(корен, ляво поддърво, дясно поддърво - клд)
void binaryPreorderPrint(const Tree& T, const Position& v) 
{ cout << v.element(); // print element
if (isInternal(v)) // visit children
{ cout << " ";
binaryPreorderPrint(T, T.leftChild(v));
binaryPreorderPrint(T, T.rightChild(v));
}
}
Postorder обхождане на двоично дърво
(ляво поддърво, дясно поддъво, корен - лдк)
void binaryPostorderPrint(const Tree& T, const Position& v) 
{ if (isInternal(v)) // visit children
{ cout << " ";
binaryPostorderPrint(T, T.leftChild(v));
binaryPostorderPrint(T, T.rightChild(v));
}
cout << v.element(); // print element
}
  Изчисляване на аритметичен израз


  • Специализация на postorder обхождане:
    • рекурсивен метод, връщаш стойността на поддървото;
    • при посещение на вътрешния възел се комбинират стойностите на поддървета.
  • Време за изпълнение O(n) (O(n) time algorithm).

Algorithm evalExpr(v)
      if isExternal (v)
            return v.element ()
      else
          xevalExpr(leftChild (v))
          yevalExpr(rightChild (v))
          ◊ ← operator stored at v
          return xy

Inorder обхождане на двоично дърво

(ляво поддърво, корен, дясно поддърво - лкд)

  • При Inorder обхождане възелът се посещава след лявото му поддърво и преди дясното му поддърво.
  • Приложение: чертаене на двоично дърво
    • x(v) = inorder ранг на v
    • y(v) = дълбочина на v
  • Посещения отляво-надясно.

Algorithm inOrder(v)
   if isInternal (v)
       inOrder(leftChild (v))
   visit(v)
   if isInternal (v)
      inOrder(rightChild (v))

void binaryInorderPrint(const Tree& T, const Position& v) 
{ if (isInternal(v)) // visit left child
binaryInorderPrint(T, T.leftChild(v));
cout << v.element(); // print element
if (isInternal(v)) // visit right child
binaryInorderPrint(T, T.rightChild(v));
}
Двоични дървета за търсене (търсещи двоични дървета)

Двоично дърво за търсене е двоично дърво, за което всеки вътрешен възел v съдържа елемент e, такъв, че:
Position searchBinaryTree(const Tree& T, const Position& v, const Object& e)
{ if (isInternal(v))
if (v.element() == e) return v; // found!
else if (v.element() < e)
searchBinaryTree(T, T.leftChild(v), e);
// search left subtree
else
searchBinaryTree(T, T.rightChild(v), e); // search right subtree
else return ... // not found!
}
Времето t за търсене в двоичното дърво T е пропорционално на височината на T, т.е. t >= O(log n) и  t <= Omega(n).

Унифицирано обхождане

Ойлерово обхождане на двоично дърво


  • Обобщено обхождане на двоично дърво.
  • Съдържа като специални случи preorder, postorder и inorder обхождания.
  • Разходка около дървото с посещение на всеки възел три пъти::
    • отляво (preorder)
    • отдолу (inorder)
    • отдясно (postorder)


Структури от данни за представяне на дървета

Вектор-базирана структура за двоични дървета

Свързана структура за двоични дървета



Възли и позиции на двоично дърво
html-6.27 (Node)
html-6.28 (Position)

Функции за обновяване на двоично дърво


Още две функции:
html-6.29 (LinkedBinaryTree1)
html-6.30 (LinkedBinaryTree2)

LinkedBinaryTree.cpp

Свързана структура за дървета


Представяне на дърво с двоично дърво

Представянето на общо (наредено) дърво T  се получава с трансформация на T  в двоично дърво T '. Трансформацията е следната:




Допълнителни връзки:
http://www.cs.purdue.edu/homes/ayg/CS251/slides/chap5.pdf