Post on 10-Nov-2015
description
Programarea calculatoarelor -
Algoritmi
Modul 3
Curs 12-13
2
Cuprins
1. Arbori
Introducere
Arbori binari
Reprezentarea arborilor binari
Operaii pe arbori binari
Parcurgerea arborilor binari
Abordare obiectual
3
1. Arbori
Introducere
Noiunea de arbore este legat de elemente ale teoriei
grafurilor i elemente legate ierarhizarea informaiilor, cum ar fi:
organizarea administrativ
structurarea unei cri
ordinea executrii operaiilor la evaluarea expresiilor aritmetice
Arborii combin avantajele tablourilor (cutri rapide) cu cele ale listelor nlnuite (inserare i tergere rapid)
Arborii pot fi definiti in mai multe moduri:
a) conform teoriei grafurilor, un arbore e considerat
un graf neorientat, conectat i fr cicluri.
Numim un graf neorientat o pereche ordonata de
multimi (X, U), unde:
-X e o multime finita si nevida de elemente, numite
noduri sau varfuri
-U e o multime de perechi neordonate, submultime de
doua elemente din X, numite muchii.
G = (X, U) este astfel un graf neorientat avand multimea
de noduri X si U, multimea de muchii.
Un graf G e conectat daca pentru doua noduri diferite x
si y, exista un lant care le leaga.
Lantul L in G este dat de o succesiune de noduri L = [xi1,
xi2,, xik] cu proprietatea ca doua noduri consecutive din L sunt adiacente, si:
4
5
[xi1, xi2], [xi2, xi3],, [xik-1, xik] U, nodurile xi1 respectiv xik sunt extremitatile lantului, si numarul de muchii ce apar in L defineste
lungimea lantului. E numit ciclu in G, un lant L pentru care
xi1=xik, si toate muchiile [xi1, xi2], [xi2, xi3],, [xik-1, xik] sunt diferite doua cate doua.
6
b) a doua definiie: un arbore cu rdcin este o mulime finit de noduri (n general nevid), n care exist un nod special numit rdcina arborelui r, celelalte noduri fiind grupate n subarbori ai nodului r
Orice nod este rdcina unui subarbore i orice arbore este sau poate deveni subarbore
Nodurile conin informaie specific i informaie de legtur spre alte noduri
Numrul de subarbori nevizi ai unui nod indic gradul nodului respectiv
Rdcina arborelui este unit cu rdcinile subarborilor prin muchii
7
Prin prisma acestei definiii arborii sunt date recursive i dinamice (la fel ca listele)
Definiia anterioar duce la ierarhizarea arborilor pe niveluri:
nivelul maxim din arbore d nlimea (adncimea arborelui)
1
2 4
6 7
8
5
3
9
Nivel : 0
Nivel : 1
Nivel : 2
Nivel : 3 Inaltime: 3
8
nlimea mai poate fi dat recursiv ca suma:
1 + maximum(nlimea subarborilor)
Pentru exprimarea relaiilor ntre noduri se folosesc termeni preluai de la arborii genealogici:
printe, fiu, frate, strmo, descendent
Fiecrei muchii i se poate asocia o orientare de la printe la fiu
9
Orice nod x poate fi atins din rdcin pe un drum unic
Orice nod care se gsete pe drumul unic de la rdcina r la nodul x se numete ascendent (strmo) al lui x
Dac y este un ascendent al lui x atunci x este un descendent al lui y
Toti descendenii unui nod y sunt nodurile din subarborele cu rdcina y
Un nod ce nu are descendeni se numete nod terminal sau frunz
Dou noduri care au acelai printe se numesc frai
10
Operatii
Vizitarea unui nod: accesarea n scopul executrii unor operaii asupra
informaiei utile (consultare, afiare,)
Traversarea unui arbore: vizitarea tuturor nodurilor
Majoritatea arborilor cu radacina sunt arbori binari (binary trees). n unele aplicatii se folosesc alti arbori cum ar fi arbori de tip quad, hexagonal sau octal
11
Arbori binari Arbore binar:
un arbore constituit dintr-un nod rdcin i doi subarbori binari disjunci numii subarborele stng, respectiv subarborele drept (ce pot fi i vizi)
Fiecare nod are cel mult 2 succesori (fii)
r
A1 A2
12
Rdcina subarborelui stng se numete fiul stng al rdcinii, iar rdcina subarborelui drept se numete fiul drept al rdcinii
1
2 3
5
6
4
7
13
Clase speciale de arbori binari
Arbori binari strici :
orice nod are gradul 0 (nod terminal) sau 2 (doi fii)
5
6 7
1
2 3
4
14
Arbori binari plini:
au 2k - 1 noduri distincte, plasate pe nivelurile 0,1,...,k-1 a.i. pe fiecare nivel l se gsesc 2l noduri
1
2 3
5 6 4 7
l = 0
l = 1
l = 2
k = 3
15
Arbori binari complei:
se obin dintr-un arbore plin prin eliminarea de la dreapta la stnga a unor noduri de pe ultimul nivel
pentru a obine un arbore complet cu n noduri:
se construiete un arbore plin pentru cele n noduri cu 2k+1 - 1 noduri unde n satisface inegalitatea:
2k
16
Arbori binari degenerai: au nodurile dispuse pe n niveluri
toate nodurile, cu excepia ultimului, sunt de ordinul 1 (au un singur descendent direct)
Observaie: o list poate fi considerat ca un arbore degenerat
1
2
4
3
1
2
4
3
17
Arbori binari echilibrai: pentru orice nod, numrul nodurilor din subarborele
stng i cel al nodurilor din subarborele drept difer cu cel mult o unitate
1
2 3
4 5
1
2 3
4 5 6
18
Proprietile arborilor binari
numrul maxim de noduri pe nivelul l este 2l
numrul maxim de noduri dintr-un arbore cu nlimea k este 2k+1-1 :
n orice arbore binar cu n noduri terminale exist (n-1) noduri de grad 2
un arbore binar cu n noduri are nlimea cel puin egal cu [log2(n)]
Daca definim prin I lungimea drumurilor interne ca reprezentand suma lungimilor drumurilor de la radacina
la nodurile nonterminale (interne), cu E suma lungimilor
drumurilor de la radacina la nodurile terminale (frunze,
adica externe), atunci un arbore binar cu n noduri interne
are, E = I + 2n.
19
Utilizare:
cel mai adesea pentru reprezentarea unor date caracterizate de o cheie unic:
cheia este un cmp din informaia specific care este utilizat pentru a identifica nodul respectiv
dac tipul cheii admite o relaie de ordine (de exemplu mai mic sau mai mare), aceti arbori se pot utiliza pentru cutarea i sortarea eficient a informaiei
20
Reprezentarea arborilor binari
Cel mai frecvent se utilizeaz reprezentarea nlnuit cu referine descendente:
struct ANOD
{
// declaratii date; informatii specifice
void *info;
struct ANOD *st;
struct ANOD *dr;
};
21
Arbori binari de cutare
sunt arbori pentru care cheia fiului stng este mai mic dect cea a nodului printe, iar cheia fiului drept este mai mare sau egal cu cea a printelui.
Daca introducem valorile 200, 100, 150, 125, 400, 80, 60, 90, aceste numere vor fi introduse in arbore
in felul urmtor:
22
Urmatorul exemplu genereaz arborele binar prezentat, iar funcia tiparb() care tiprete subarborele stang creat, apoi nodul i apoi subarborele drept (in-order), va duce la ordonarea listei de valori asfel: 60, 80, 90, 100, 125,
150, 200, 400.
Exemplu:
#include
#include
#include
typedef struct anod{
int nr;
struct anod *stg;
struct anod *dr;
}ANOD;
void tiparb(ANOD*);
ANOD* arbore(ANOD*,int);
23
void main(void){
ANOD *p=NULL;
int val=0;
char ch;
do
{
puts("\nIntrodu nodurile arborelui:");
scanf("%d",&val);
p=arbore(p,val);
puts("Continui introducerea cu y sau Y:");
ch=getche();
}while(ch=='y'||ch=='Y') ;
puts("\nNodurile arborelui creat sunt:");
tiparb(p);
}//main
24
ANOD* arbore(ANOD *x,int y){
if(x==NULL){
x=new ANOD; //radacina
x->nr=y;
x->stg=NULL;
x->dr=NULL;}
else if(x->nr < y) x->dr=arbore(x->dr,y);
else if(x->nr > y) x->stg=arbore(x->stg,y);
return x;
}//arbore
void tiparb(ANOD* p){
if(p!=NULL){
tiparb(p->stg);
printf("%d\n",p->nr);
tiparb(p->dr);
}
}//tiparb
25
Operaii pe arbori:
cutarea unui nod cu o anumit cheie
inserarea unui nod frunz
accesul la un nod
parcurgerea arborelui
tergerea unui nod, tergerea arborelui
etc.
Operaiile de creare, inserare, acces la un nod au la baz un criteriu (nu neaparat de ordonare), care definete locul nodului n arbore, criteriu specific aplicaiei
26
Se folosesc de obicei dou funcii specifice:
int criteriu(struct ANOD *p1, struct ANOD *p2);
ce returneaz:
-1 : *p2 poate fi un nod al subarborelui stng al
nodului indicat de p1
+1 : *p2 poate fi un nod al subarborelui drept al
nodului indicat de p1
0 : *p2 nu poate fi un nod al subarborilor nodului
indicat de p1 (mai exista unul si incrementam un
contor, frecv)
Exemplu (pentru un arbore binar de cautare)
int criteriu(ANOD *p1, ANOD *p2){
if(p2 ->key< p1 ->key) return -1; // cheia e un numar intreg
if(p2 ->key > p1 ->key) return 1;
return 0;
}//criteriu
27
n cazul n care funcia criteriu() returneaz valoarea 0 se consider c nodurile sunt echivalente i se apeleaz o alt funcie specific:
struct ANOD *echivalenta(struct ANOD *p1, struct ANOD *p2);
nodul indicat de p1 este prelucrat (depinde de aplicaie), iar nodul indicat de p2 este eliminat
ANOD *echivalenta(ANOD *p1, ANOD *p2) {
p1 ->frecv++; // prelucrare nod (incrementam nr. de aparitii)
elibnod(p2);
return p1;
}//echivalenta
Obs: n cazul arborilor binari de cutare aceste funcii nu sunt necesare, deoarece relaia de ordine este simpl
28
Operaii pe arbori binari
Pentru simplitate vom considera reprezentarea
arborelui astfel (informaia util sub forma unui ntreg):
struct ANOD {
int info;
struct ANOD *st;
struct ANOD *dr;
};
29
Cutarea unui nod este cea mai simpl operaie pe arbori, ce
presupune parcurgerea arborelui i compararea cheii cu valoarea dat
struct ANOD * CautaNod(int key, struct ANOD *root)
{
struct ANOD *current = root;
while(current->info != key) {
if(key < current->info)
current = current->st;
else
current = current->dr;
if(current == NULL)
return NULL;
}
return current;
}
30
Inserarea unui nod
Pentru a realiza inserarea trebuie mai nti gsit locul n care se va insera noul nod
Aceast cutare se face similar cu cazul anterior, cutarea ncheindu-se atunci cnd s-a ajuns cu nodul curent la valoarea NULL
Exemplu:
iniial inserare 6 inserare 10
31
void InsNod(int key, struct ANOD *root)
{
struct ANOD *nNod = new struct ANOD;
nNod->info = key;
if(root == NULL) root = nNod;
else {
struct ANOD *current = root;
struct ANOD *parent;
while(1) {
parent = curent;
if(key < current->info) {
current = current->st;
if(current == NULL) {
parent->st = nNod; return;
}
}
else {
current = current->dr;
if(current == NULL) {
parent->dr = nNod; return;
}
}
} // end while
} // else
}
32
tergerea unui nod Este cea mai complicat operaie pentru un arbore
binar de cutare Pentru tergere trebuie gsit nodul care urmeaz a fi
ters, dup care apar urmtoarele situaii:
nodul respectiv este o frunz: se anuleaz referina de la nodul printe ctre nodul n
cauz
nodul are un singur fiu: se conecteaz nodul printe la nodul fiu
nodul are doi fii: trebuie gsit nodul succesor (acel nod care plasat n locul
nodului ters asigur pstrarea relaiei de ordine ntre noduri)
nodul de ters se nlocuiete cu: nodul care are cheia cea mai mare n subarborele stng
sau cu nodul ce are cea mai mic cheie n subarborele drept
nodul cel mai din stnga al subarborelui drept sau cu nodul cel mai din dreapta al subarborelui stng
33
Nod de grad 1:
Nod de grad 2:
34
int StergeNod(int key, struct ANOD *root)
{
struct ANOD *current = root;
struct ANOD *parent = root;
int FiuStang;
// cautare nod
while(current->info != key) {
parent = current;
if(key < current->info) {
FiuStang = 1;
current = current->st;
}
else {
FiuStang = 0;
current = current->dr;
}
if(current == NULL)
return 0;
}
35
// nu are fii (frunza)
if(current->st == NULL && current->dr == NULL)
{
if(current = =root) // este chiar radacina
root = NULL; // se sterge nodul radacina
else if(FiuStang) // e in subarborele stang
parent->st = NULL;
else // e in subarborele drept
parent->dr = NULL;
return 1;
}
36
Exemplu: nodul care se va sterge este frunza
if(FiuStang)
parent->st = NULL; 8
4 12
2 6 current
parent
st dr
37
// nod cu un singur fiu
if(current->dr == NULL) { // nu are fiu drept
if(current == root) // este radacina
root = current->st; // fiul stang devine radacina
else if(FiuStang) // este in subarborele stang
parent->st = current->st;
else
parent->dr = current->st; // este in subarb. drept
return 1;
}
if(current->st == NULL) { // nu are fiu stang
if(current == root) // este radacina
root = current->dr; // fiul drept devine radacina
else if(FiuStang)
parent->st = current->dr;
else
parent->dr = current->dr;
return 1;
}
38
Exemplu: stergere nod cu un singur fiu
if(!FiuStang)
parent->dr = current->st;
12
10
current
parent
dr dr
parent
dr
current
parent
dr
st
6 2
4
8
39
// nod cu doi fii
struct ANOD *succesor = getSuccesor(current);
if(current == root) // nodul de sters este radacina
root = succesor; // succesorul devine radacina
else if(FiuStang) // nodul este in subarborele stang
parent->st = succesor; // succesorul devine fiu stang
else
parent->dr = succesor; // succesorul devine fiu drept
succesor->st = current->st; // legatura cu fiul stang al nodului sters
return 1;
}
40
1 18
-3 4 7 21
19 25
5
succesor
parent
current
parent->dr = succesor;
succesor->st = current->st;
succesor
41
struct ANOD *getSuccesor(struct ANOD *p)
{
struct ANOD *succParent = p;
struct ANOD *succ = p;
struct ANOD *current = p->dr; // cautam in subarborele drept
while(current != NULL) {
succParent = succ;
succ = current;
current = current->st; // mergem spre stanga (cautam min)
}
if(succ != p->dr) {
succParent->st = succ->dr; (NULL , sterge legatura spre succesor)
succ->dr = p->dr; // legatura cu subarborele drept
}
return succ;
}
42
n cazul n care frecvena operaiei de tergere este mic, se poate ataa un indicator (flag) fiecrui nod care s precizeze dac nodul respectiv este ters, iar funciile de vizitare s testeze acest indicator nainte de a lua o decizie
43
Parcurgerea arborilor binari
Parcurgerea (traversarea) se face pentru prelucrarea informaiilor pstrate n noduri
Parcurgerea se face ntr-o anumit ordine, dat de aplicaie
44
Parcurgerea n lime: se prelucreaz nodul rdcin apoi, de la stnga
spre dreapta, nodurile aflate pe primul nivel, etc. (exemplu lista personalului cu funcii de conducere);
pentru parcurgere se poate folosi o coad iniializat cu nodul rdcin; apoi ct timp exist noduri n coad:
se extrage primul nod din coad
se prelucreaz nodul extras
se adaug n coad fiii nodului extras
Parcurgerea n adncime: fiii unui nod sunt parcuri de la stnga la dreapta dar
trecerea de la fiul curent la fratele din dreapta se face numai dup ce s-au parcurs toi descendenii fiului curent
45
Moduri simple de parcurgere pentru arborii binari: n preordine:
rdcina -> subarbore stng -> subarbore drept
n inordine: subarbore stng -> rdcina -> subarbore drept (mai
frecvent utilizat)
n postordine: subarbore stng -> subarbore drept -> rdcina
Subarborii sunt parcuri n acelai mod (de la stnga la dreapta)
Parcurgerile n preordine i n postordine sunt folosite la evaluarea expresiilor algebrice
n cazul arborilor binari de cutare, parcurgerea n inordine asigur vizitarea nodurilor n ordine cresctoare
46
// parcurgerea in preordine
void preord(struct ANOD *p)
{
if(p != NULL) {
prelucrare(p);
preord(p->st);
preord(p->dr);
}
}
//parcurgerea in inordine
void inord(struct ANOD *p)
{
if(p != NULL) {
inord(p->st);
prelucrare(p);
inord(p->dr);
}
}
47
// parcurgerea in postordine
void postord(struct ANOD *p)
{
if(p != NULL)
{
postord(p->st);
postord(p->dr);
prelucrare(p);
}
}
48
// stergerea arborelui
void sterge_arb(struct ANOD *p)
{
if(p != NULL) {
sterge_arb(p->st);
sterge_arb(p->dr);
elib_nod(p); //functie specifica aplicatiei
}
}
// afisarea arborelui
void afis_arb(struct ANOD *p)
{
if(p != NULL) {
afis_arb(p->st);
afis_inf_specif(p->inf);
afis_arb(p->dr);
}
}
49
// initializare arbore
int init_arb(struct ANOD *rad)
{
rad = NULL;
return 0;
}
//verifica daca arborele este vid
int arbore_vid(struct ANOD *rad)
{
return(rad == NULL)
}
50
//creaza arbore
int creaza_arb(void *info, struct ANOD *stt, struct ANOD *drr, struct ANOD *rad)
{
rad = new ANOD;
if(!rad)
return 1;
rad -> inf = info;
rad -> st = stt;
rad -> dr = drr;
return 0;
}
// modificarea informatiei dintr-un nod
int modif_inf(struct ANOD *rad, void *x)
{
if(rad != NULL)
rad -> inf = x;
return (rad != NULL);
}
51
// modificarea fiului stang
int modif_st(struct ANOD *rad, struct ANOD *x)
{
if(rad != NULL)
rad ->st = x;
return (rad != NULL);
}
// modificarea fiului drept
int modif_dr(struct ANOD *rad, struct ANOD *x)
{
if(rad != NULL)
rad ->dr = x;
return (rad != NULL);
}
52
// verifica daca nodul este frunza
int frunza(struct ANOD *rad)
{
return((rad -> st == NULL) && (rad -> dr == NULL));
}
// calculeaza numarul de noduri din arbore
int nr_noduri(struct ANOD *rad)
{
if(rad == NULL)
return 0;
return(nr_noduri(rad->st) + nr_noduri(rad->dr) + 1);
}
53
// calculeaza inaltimea arborelui
int inalt_arb(struct ANOD *rad) {
int hst, hdr;
if(rad == NULL)
return 0;
hst = inalt_arb(rad -> st);
hdr = inalt_arb(rad -> dr);
return((hst > hdr ? hst : hdr) + 1);
}
// adauga o frunza in subarborele stang
void adauga_frunza(struct ANOD *nodc, struct ANOD *nodf){
if(arbore_vid(nodc))
nodc = nodf;
else
adauga_frunza(nodc->st, nodf);
}
54
Aplicaie: citete datele de intrare sub forma unor cuvinte distincte
construiete un arbore binar cu cuvintele n ordine alfabetic
transmite datele de ieire formate din cuvintele distincte din
datele de intrare ordonate mpreun cu numrul lor de apariii
typedef struct anod {
char *word;
int count;
struct anod *left;
struct anod *right;
} ANOD;
void afis_arb(ANOD*);
ANOD* adauga_arb(ANOD*, char *);
55
void main(void)
{
ANOD *p=NULL;
char word[32];
char ch;
do {
coutword;
p=adauga_arb(p,word);
cout
56
ANOD *adauga_arb(ANOD *p, char *w)
{
int cond;
if (p == NULL) {
p = new ANOD;
p->word = new char [strlen(w)+1];
strcpy( p->word, w);
p->count = 1;
p->left = p->right = NULL;
}
else if ((cond = strcmp(w, p->word)) == 0)
p->count++; /*ajusteaza nr. de aparitii*/
else if (condleft = adauga_arb(p->left, w);
else /*adauga nod la dreapta*/
p->right = adauga_arb(p->right, w);
return p;
}
57
void afis_arb(ANOD* p)
{
if(p!=NULL) {
afis_arb(p->left);
cout
58
Abordare obiectual
enum BOOL {FALSE=0, TRUE=1};
// clasa nod
class TNodAB
{
public:
int cheie;
TNodAB *stg,*dr;
TNodAB(int k) {
cheie = k;
stg = dr =NULL;
}
};
59
// clasa arbore
class TArboreABO {
TNodAB *radacina;
TNodAB *adauga(TNodAB *r, int k);
TNodAB *elimina(TNodAB *u, TNodAB *q);
TNodAB *sterge(TNodAB *r, int k);
int inaltime(TNodAB *r);
void inordine(TNodAB *r);
void preordine(TNodAB *r);
void postordine(TNodAB *r);
void tipar_arbore(TNodAB *r, int h);
void distruge(TNodAB *r);
60
public:
TArboreABO( );
~TArboreABO( );
void Adauga(int k);
void Sterge(int k);
BOOL Prezent(int k);
int Inaltime( );
void Inordine( );
void Preordine( );
void Postordine( );
void TiparArbore( );
};
61
TArboreABO::TArboreABO( ) {
radacina = NULL;
}
TArboreABO::~TArboreABO( ) {
distruge(radacina);
}
void TArboreABO::distruge(TNodAB *r) {
if (r) {
distruge(r->stg);
distruge(r->dr);
delete r;
}
}
62
void TArboreABO::Adauga(int k) {
radacina = adauga(radacina, k);
}
TNodAB * TArboreABO::adauga(TNodAB *r, int k) {
if (!r) {
r = new TNodAB(k);
return r;
}
else if (k < r->cheie)
r->stg = adauga(r->stg, k);
else if (k > r->cheie)
r->dr = adauga(r->dr, k);
return r;
}
63
int TArboreABO::Inaltime( ) {
return inaltime(radacina);
}
int max(int a, int b) {
if (a>b)
return a;
return b;
}
int TArboreABO::inaltime(TNodAB *r) {
if (!r)
return 0;
return
max(inaltime(r->stg), inaltime(r->dr))+1;
}
64
BOOL TArboreABO::Prezent(int k)
{
TNodAB *crt=radacina;
while (crt) {
if (k == crt->cheie)
return TRUE;
if (kcheie)
crt = crt->stg;
if (k>crt->cheie)
crt = crt->dr;
}
return FALSE;
}
65
TNodAB *TArboreABO::elimina(TNodAB *u, TNodAB *q) {
// elimina nodul din extrema dreapta
// si returneaza radacina subarborelui obtinut
TNodAB *t;
if(q->dr != NULL)
q->dr = elimina(u, q->dr);
else {
t = q;
u->cheie = q->cheie;
q = t->stg;
delete t;
}
return q;
}
66
TNodAB * TArboreABO::sterge(TNodAB *r, int k) {
TNodAB *t;
if(r != NULL)
if( k < r->cheie)
r->stg = sterge(r->stg, k);
else
if (k> r->cheie)
r->dr = sterge(r->dr, k);
else {
t = r;
if (t->stg == NULL) {
r = t->dr;
delete t;
}
else if (t->dr == NULL) {
r = t->stg;
delete t;
}
else t->stg = elimina(t, t->stg);
}
return r;
}
67
void TArboreABO::Sterge(int k) {
radacina=sterge(radacina, k);
}
void TArboreABO::Inordine( ) {
inordine(radacina);
}
void TArboreABO::inordine(TNodAB * r) {
if (r) {
inordine(r->stg);
cout
68
void TArboreABO::Preordine( ) {
preordine(radacina);
}
void TArboreABO::preordine(TNodAB * r) {
if (r) {
cout
69
void TArboreABO::Postordine( ) {
postordine(radacina);
}
void TArboreABO::postordine(TNodAB * r) {
if (r) {
postordine(r->stg);
postordine(r->dr);
cout
70
void TArboreABO::tipar_arbore(TNodAB *r, int h)
{
if (r) {
tipar_arbore(r->stg, h-5);
for (int i=1; i
71
void main(void)
{
int rn;
int vmax = 100, vmin = 1;
srand((unsigned)time(NULL)); TArboreABO *a=new TArboreABO;
cout
72