8. Още структури от данни
Двоични дървета
*** Определение и свойства на дърво.
- Ацикличен насочен граф, в който всички върхове без 1 имат по 1 предшественик, а 1 връх (корен) няма предшественици.
- Единствен път до корена, ниво - брой на върховете по пътя до корена
- Ниво на върха - брой на върховете в пътя до корена
- Корен - ниво 0
- Листа - върхове без наследници
- Поддърво определено от връх - върхът и всичките му наследници, техните наследници и т.н.
- Рекурсивна същност на дърветата като структури от данни

*** Двоично дърво:
Брой на наследниците на върховете - 0, 1 или 2;
Ляв и десен наследник.
-- Рекурсивно определение на двоично дърво
Крайно множество от елементи (възли), което е или празно, или се състои от корен (възел), свързан с две непресичащи се двоични дървета (поддървета) - ляво и дясно поддърво.
-- Реализация с масив
-- Реализация с указатели
Дърво за двоично търсене:
// tree: Introduction to binary trees
#include <iostream.h>

struct node {int info; node *pLeft, *pRight;};

class tree {
public:
   tree();
   void print()const{pr(root);}
private:
   node *root;
   void AddNode(int x, node* &p);
   void pr(const node *p)const;
};

tree::tree()
{  root = NULL;
   int x;
   while (cin >> x, !cin.fail()) AddNode(x, root);
}

void tree::AddNode(int x, node* &p)
{  if (p == NULL)
   {
      p = new node;
      p->info = x;
      p->pLeft = p->pRight = NULL;
   }
   else AddNode(x, x < p->info ? p->pLeft : p->pRight);
}

void tree::pr(const node *p)const
{  if (p)
   {
      pr(p->pLeft);
      cout << p->info << " ";
      pr(p->pRight);
   }
}

int main()
{  cout <<
      "Enter some integers to be placed in a binary tree,\n"
      "followed by /:\n";
   tree t;
   cout << "Tree contents (in ascending order):\n";
   t.print();
   cout << endl;
   return 0;
}
***  Обхождане на структура от данни двоично дърво
Обхождане на двоично дърво - линейно нареждане на възлите на дървото
* корен, ляв, десен (низходящо, preorder,  клд)
* ляв, корен, десен (смесено,     inorder,    лкд)
* ляв, десен, корен (възходящо, postorder, лдк)
Пример:
Аритметични изрази и двоични дървета
Листата са имена на променливи и константи, другите възли са аритметични операции
(a+b/c)*(d-e*f)
1) preorder (клд) - *+a/bc-d*ef
2) inorder  (лкд)  - a+b/c*d-e*f
3) postorder(лдк) - abc/+def*-*

Претърсване на дърво за двоично търсене:
/* bintree:
   This program builds and searches a binary search
   tree and prints its contents. The program produces
   a frequency distribution of words read from a textfile.
   We can also search the tree for a given word to inquire
   how often that word occurs.
*/
#include <fstream.h>
#include <iomanip.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

struct node {char *pWord; int count; node *pLeft, *pRight;};

class tree {
public:
   tree(ifstream &input);
   ~tree(){DelTree(root);}
   void print()const{pr(root);}
   node *search()const;
   int ReadWord(istream &input);
private:
   node *root;
   enum {buflen = 50};
   char buf[buflen];
   void AddNode(node* &p);
   void DelTree(node* &p);
   void pr(const node *root)const;
};

int tree::ReadWord(istream &input)
/* This function reads a word from the stream 'input'.
   It skips any leading nonalphabetic characters.
   Then the longest possible string of letters (no longer
   than buflen - 1) is read, converted to upper case and
   placed in 'buf'. Return value: success (1) or failure (0).
*/
{  char ch;
   int i;
   do
   {  input >> ch;
      if (input.fail()) return 0;
   }  while (!isalpha(ch));
   for (i=0; i<buflen-1; )
   {  buf[i++] = toupper(ch);
      input.get(ch);
      if (input.fail() || !isalpha(ch)) break;
   }
   buf[i] = '\0';
   return 1;
}

tree::tree(ifstream &input)
{  root = NULL;
   while (ReadWord(input)) AddNode(root);
}

void tree::DelTree(node* &p)
{  if (p)
   {  DelTree(p->pLeft); DelTree(p->pRight);
      delete[] p->pWord; delete p; p = NULL;
   }
}

void tree::AddNode(node* &p) // Add word in buf to tree
{  if (p == NULL)
   {  p = new node;
      p->pWord = new char[strlen(buf) + 1];
      strcpy(p->pWord, buf); p->count = 1;
      p->pLeft = p->pRight = NULL;
   }  else
   {  int code = strcmp(buf, p->pWord);
      if (code) AddNode(code < 0 ? p->pLeft : p->pRight);
      else p->count++;
   }
}

void tree::pr(const node *p)const
{  if (p)
   {  pr(p->pLeft);
      cout << setw(5) << p->count << " "
           << p->pWord << endl;
      pr(p->pRight);
   }
}

node *tree::search()const
{  node *p = root;
   for (;;)
   {  if (p == NULL) return NULL;
      int code = strcmp(buf, p->pWord);
      if (code == 0) return p;
      p = (code < 0 ? p->pLeft : p->pRight);
   }
}

int main()
{  ifstream input;
   node *ptr;
   const int NameLen=50;
   char FileName[NameLen];
   cout << "Input file: ";
   cin >> setw(NameLen) >> FileName;
   input.open(FileName, ios::in);
   if (input.fail())
   {  cout << "Cannot open input file.\n"; exit(1);   }

   tree t(input);
   cout << "Frequency distribution:\n";
   t.print();
   for (;;)
   {  cout << "\nEnter a word, or type Ctrl+Z (or Ctrl+D) "
      "to stop: ";
      if (t.ReadWord(cin) == 0) break;
// The word read by ReadWord has been placed in the buf
// class member. We now search the tree for this word:
      ptr = t.search();
      cout << "Number of occurrences: "
           << (ptr ? ptr->count : 0) << endl;
   }
   return 0;
}
 
 
 

Кодиране на Хъфман
*** Алгоритъм на Хъфман:
 1. Всички букви - листа на дървото с тегла броя на срещанията на буквата в текста
 2. Всички други възли на дървото имат тегла 0
 3. В началото разглеждаме всички листа като отделни дървета
 4. Намираме две дървета с най-малки претеглени дължини
 5. Построяваме ново дърво, като създаваме нов възел (корен) с наследници - двете поддървета
 6. Пресмятаме претеглената дължина на новото дърво (сума от претеглените дължини на двете поддървета)
 7. Отиваме на 4., ако имаме поне 2 дървета

*** Построяване на оптимално двоично дърво по алгоритъма на Хъфман и пример за следния текст:

програмиране и структури от данни

#include <iostream.h>
struct Huf {
char id;
int wh;
Huf *left, *right; };

struct List {
List *next;
Huf *tree; };

List *head;

void CreateList();
void WriteList();
void DelList(List *l);
void AddList(Huf *h);
Huf *FindDels();
void CreateTree();
void rlrootTree(Huf *h);

void main()
{
 CreateList();
 WriteList();
 CreateTree();
 rlrootTree(head->tree);
}

void CreateList()
{
 const n=13;
 char ch[n]={'Ї','°','®','Ј',' ','¬','Ё','­','±','І','і','Є','¤'};
 int   w[n]={ 1,  5,  2,  1,  3,  1,  4,  3,  1,  2,  2,  1,  1};
 List *l; Huf *h;
 head=0;
 for (int i=0; i<n; i++)
 {
  h=new Huf;
  h->id=ch[i]; h->wh=w[i];
  h->left=0; h->right=0;
  l=new List;
  l->tree=h;
  l->next=head; head=l;
 }
}

void WriteList()
{
 List *l;
 l=head;
 while  (l)
 {
  cout << (l->tree)->id << "  " << (l->tree)->wh << endl;
  l=l->next;
 }
}

void DelList(List *l)
{
 List *lp, *lc;
 if (l==head) {head=l->next; delete l;}
 else
 {
  lp=head; lc=lp->next;
  while (lc!=l)
  {
   lp=lc; lc=lc->next;
  }
  lp->next=lc->next; delete lc;
 }
}

void AddList(Huf *h)
{
 List *l;
 l=new List;
 l->tree=h;
 l->next=head;
 head=l;
}

Huf *FindDels()
{
 List *l, *sm;
 Huf *h;
 l=head; sm=l;
 while (l)
 {
  if ((l->tree)->wh < (sm->tree)->wh) sm=l;
  l=l->next;
 }
 h=sm->tree;
 DelList(sm);
 return h;
}

void CreateTree()
{
 Huf *h, *h1, *h2;
 while (head->next)
 {
  h1=FindDels();
  h2=FindDels();
  h=new Huf;
  h->id=' '; h->wh=(h1->wh)+(h2->wh);
  h->left=h1; h->right=h2;
  AddList(h);
 }
}

void rlrootTree(Huf *h)
{
 if (h)
 {
  rlrootTree(h->right);
  rlrootTree(h->left);
  cout << h->id << " : " << h->wh << endl;
 }
}