Sunteți pe pagina 1din 18

REPREZENTAREA GRAFURILOR NEORIENTATE

Listele de adiacenta a nodurilor


Pentru a genera o astfel de lista vom defini tipul nod :
struct nod {int nd;
nod *next;};
Toate listele se vor memora utilizand un vector de liste : nod *L[20];
Datele de intrare : numarul de noduri si muchiile se vor citi din fisier :
#include<conio.h>
#include<fstream.h>
struct nod
{int nd;
nod *next;};
nod *L[20];
void afisare(int nr_nod) //afiseaza lista vecinilor nodului nr_nod
{nod *p=L[nr_nod];
if(p==0)
cout<<nr_nod<<" este izolat "<<endl;
else
{cout<<"lista vecinilor lui "<<nr_nod<<endl;
nod *c=p;
while(c)
{cout<<c->nd<<" ";
c=c->next;}
cout<<endl; }
void main()
{fstream f;int i,j,n;
nod *p,*q;
f.open("graf.txt",ios::in); //citirea datelor din fisier
clrscr(); f>>n;
while(f>>i>>j)
{p=new nod; //se adauga j in lista vecinilor lui i
p->nd=j;
p->next=L[i];
L[i]=p;
q=new nod; //se adauga i in lista vecinilor lui j
q->nd=i;
q->next=L[j];
L[j]=q; }
f.close();
cout<<endl<<"listele de adiacente ";
for(i=1;i<=n;i++)
afisare(i);
getch();
}

Parcurgerea
Rezolvarea multor probleme de grafuri, presupune parcurgerea lor de la un anumit nod.
Pentru explorarea grafurilor, exista doua tipuri de algoritmi: de explorarea in latime si de
explorare in adancime.
Explorarea grafurilor in latime

La explorarea in latime, dupa vizitarea nodului initial, se exploreaza toate nodurile


adiacente lui, se trece apoi la primul nod adiacent si se exploreaza toate nodurile adiacente
acestuia si neparcurse inca, s.a.m.d.
Fiecare nod se parcurge cel mult odata (daca graful nu este conex nu se vor putea
parcurge toate nodurile) Algoritmul
Se va folosi o coada in care se inscriu nodurile in forma in care sunt parcurse: nodul initial
varf (de la care se porneste), apoi nodurile a,b,..., adiacente lui varf, apoi cele adiacente lui a,
cele adiacente lui b,... ,s.a.m.d.
Coada este folosita astfel:
- se pune primul nod in coada;
- se afla toate varfurile adiacente cu primul nod si se introduc dupa primul nod
- se ia urmatorul nod si i se afla nodurile adiacente
- procesul se repeta pana cand se ajunge la sfarsitul cozii
-Graful se va memora utilizand matricea de adiacenta a[10][10]
-pentru memorarea succesiunii nodurilor parcurse se va folosi un vector c[20] care va
functiona ca o coada
-pentru a nu parcurge un nod de doua ori se va folosi un vector boolean viz[20] care va
retine :
- viz[k]=0 daca nodul k nu a fost vizitat inca
- viz[k]=1 daca nodul k a fost vizitat
-doua variabile : prim si ultim vor retine doua pozitii din vectorul c si anume :
- prim este indicele componentei pentru care se parcurg vecinii
(indexul componentelor marcate cu rosu in sirurile parcurse anterior ). Prin urmare
Varf=c[prim], este elementul pentru care se determina vecinii (nodurile adiacente)
-ultim este pozitia in vector pe care se va face o noua inserare
in vectorul c (evident, de fiecare data cand se realizeaza o noua inserare se mareste vectorul)
-vecinii nodului varf se cauta pe linia acestui varf : daca a[varf][k]=1 inseamna ca nodurile
varf si k sunt adiacente. Pentru ca nodul k sa fie adaugat in coada trebuie ca nodul sa nu fi
fost vizitat : viz[k]=0
void bf_iterativ() //parcurgerea in latime
{int k;
while(prim<=ultim)
{varf=c[prim];
for(k=1;k<=n;k++)
if(a[varf][k]==1&&viz[k]==0) //il adaug pe k in coada daca este vecin pt. varf si nu a fost
vizitat
{ultim++;
c[ultim]=k;
viz[k]=1;}
prim++; }}
Varianta recursiva de parcurgere se obtine modificand functia de parcurgere iterativa
adaugand conditia necesara autoapelului:

void bf_recursiv() //parcurgerea in latime


{int k;
if(prim<=ultim)
{varf=c[prim];
for(k=1;k<=n;k++)

if(a[varf][k]==1&&viz[k]==0) //il adaug pe k in coada daca este vecin pt. varf si nu a fost
vizitat
{ultim++;
c[ultim]=k;
viz[k]=1;}
prim++;
bf_recursiv();
}
}
Parcurgerea in latime a grafurilor memorate prin liste este similara cu diferenta ca vecinii
unui nod adaugat in coada se cauta in lista corespunzatoare lui :
struct nod
{int nd;
nod *next;};
nod *L[20];
int viz[100]; //marchez cu 1 nodurile vizitate
int m,n;
int prim,ultim,C[100];
void bfi_lis()
{int varf,nr;
nod *p;
while(prim<=ultim)
{varf=C[prim];
p=L[varf]; // se parcurge lista elementelor din varful cozii
while(p)
{nr=p->nd;
if(viz[nr]==0) //numai daca nu a fost vizitat
{ultim++; //maresc coada
C[ultim]=nr; //il adaug in coada
viz[nr]=1;}; //il marchez ca fiind vizitat
p=p->next;
}
prim++; //avansez la urmatorul nod din coada
}
}

Parcurgerea grafurilor in adancime (depth first)


Parcurgerea unui graf in adancime se face prin utilizarea stivei (alocate implicit prin
subprograme recursive).
Pentru fiecare nod se parcurge primul dintre vecinii lui neparcursi inca
Dupa vizitarea nodului initial x1, se exploreaza primul nod adiacent lui fie acesta x2 , se
trece apoi la primul nod adiacent cu x2 si care nu a fost parcurs inca , s.a.m.d.
Fiecare nod se parcurge cel mult odata (daca graful nu este conex nu se vor putea
parcurge toate nodurile)
Algoritmul
-Graful se va memora utilizand matricea de adiacenta a[10][10]
-pentru a nu parcurge un nod de doua ori se va folosi un vector boolean vizcare va
retine :
- viz[k]=0 daca nodul k nu a fost vizitat inca
- viz[k]=1 daca nodul k a fost vizitat
-ca si la parcurgerea in latime vecinii unui nod se cauta pe linia acestui nod : daca
a[nod][k]=1 inseamna ca nodurile nod si k sunt adiacente. Pentru ca nodul k sa fie fie parcurs
trebuie ca nodul sa nu fi fost vizitat : viz[k]=0
void dfmr(int nod)
{
cout<<nod<<" ";
viz[nod]=1;
for(int k=1;k<=n;k++)
if(a[nod][k]==1&&viz[k]==0)
dfmr(k);
}

Graf hamiltonian
Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m elemente (m
muchii).
Lant hamiltonian: lant elementar care contine toate nodurile grafului.
Ciclu hamiltonian: ciclu elementar care contine toate nodurile grafului.
Graf hamiltonian: graf care contine un ciclu hamiltonian.
Conditii de suficienta:

Teorema lui Dirac: Fie G dat prin perechea (A,B). Daca G are un numar de cel putin 3
varfuri astfel incat gradul fiecarui nod respecta conditia d(x)>=n/2, atunci graful este
hamiltonian.
Un graf hamiltonian nu poate avea noduri izolate.
Fiind dat un graf neorientat memorat prin matricea de adiacente sa se determine daca graful
este Hamiltonian . In caz afirmativ sa se afiseze ul ciclu Hamiltonian altfel se va afisa un
mesaj.
Pentru a rezolva problema vom utiliza tehnica backtracking. Vom incarca in stiva noduri
distincte si adiacente, astfel incat pornind de la problema clasica de backtracking a
permutarilor vom testa valoarea de pe nivelul k astfel:

Sa fie un nod adiacent cu precedentul adaugat. E necesar ca: a[st[k-1]][st[k]]=1, in caz


contrar se returneaza 0

Nodul adaugat sa nu se regaseasca pe nivelurile anterioare . Trebuie ca st[k]st[i] unde


i{1,2k-1}, in caz contrar se returneaza 0

Pentru a se incheia ciclul este necesar ca primul si ultimul nod sa fie adiacente. Adica:
a[st[1]][st[n]]=1, in caz contrar se returneaza 0

//k este este nivelul din stiva (indexul - vetorul solutie),curent


int e_valid()
{if(k>1)
if(!a[st[k-1]][st[k]])
return 0;
else
for(int i=1;i<=k-1;i++)//parcurg nivelurile anterioarenivelului curent
if(st[i]==st[k])
return 0;
if(k==n)
if(!a[st[1]][st[k]])
return 0;
return 1;}
void afisare()
{for(int i=1;i<=n;i++)
cout<<st[i]<<" ";
cout<<st[1];
k=0; //determina iesirea la prima solutie
ns++;}
void back()
{k=1; //pe primul nivel initial
while(k>0)//cand k va descreste la 0 algoritmul se incheie
if(st[k]<n)
{st[k]++;
if(e_valid())//daca elementul incarcat este valid
if(k==n)//verific daca am ajuns la solutia completa.
afisare();
else //daca nu am solutia completa urc in stiva (maresc vectorul, adica pe k)
{k++;
st[k]=0;}
}
else
k--;}

Grafuri euleriene
Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m elemente (m
muchii).
Lan eulerian = un lan simplu care conine toate muchiile unui graf
Condiie necesar i suficient: Un graf este eulerian dac i numai dac oricare vrf al su
are gradul par.
Observatie: graful poate fi eulerian si daca contine noduri izolate.
Problema: fiind dat un graf fara noduri izolate sa se determine daca este eulerian. In caz
afirmativ se vor afisa toate ciclurile euleriene care incep cu un nod nd citit.
vom determina daca graful este conex
vom determina daca fiecare nod are grad par
vom genera toate ciclurile euleriene utilizand tehnica backtracking. Astfel:
o
primul nod va trebui sa fie nd
o
un nou x=st[k], adaugat in stiva trebuie sa fie adiacent cu anteriorul (y=st[k-1])
o
muchia x-y nu trebuie sa mai fi fost adaugata inca odata
o
ultimul nod, care incheie ciclul, trebuie sa fie incident cu primul

Algoritmul lui Roy-Floyd


Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m elemente
(m muchii) memorat prin matricea ponderilor. Se cere ca pentru doua noduri x,y citite sa se
determine lungimea minima a lantului de la x la y.
Algoritmul:
-se genereaza matricea ponderilor:
-se incearca pentru oricare pereche de noduri i,j sa se obtina drumuri mai scurte prin
noduri intermediare k (k1n).
Acest lucru se determina comparand lungimea lantului a[i,j] cu lungimea lantului care
trece prin k si daca:
a[i,j] > a[i,k]+a[k,j] atunci se atribuie: a[i,j] a[i,k]+a[k,j]
Astfel generarea matricii drumurilor optime se realizeaza cu urmatoarea secventa:
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(a[i][j]>a[i][k]+a[k][j])
a[i][j]=a[i][k]+a[k][j];

Algoritmul lui Djikstra


Fie G=(V, E) un graf orientat, unde V are n elemente (n varfuri) si E are m elemente (m
arce). Se cunosc costurile arcelor.
Ne propunem sa determinam costul minim de la un varf la toate celelalte varfuri. Fie
acesta varful 1.
Vom utiliza 3 vectori :
Vectorul d care va retine lungimea drumurilor de la 1 la celelalte varfuri. (d[i]=lungime
drum de la 1 la i)
Vectorul s, numit vector caracteristic va retine 1 pentru nodurile selectate ca
participante la drumul minim, altfel s retine 0. Initial va retine 1 doar pentru varful de pornire ,
in cazul nostru varful 1
Vectorul t , vector de tip tata care va indica drumurile gasite intre varful 1 si celelalte
varfuri astfel incat pentru fiecare varf se va retine precedentul (tatal). Pentru varful de
pornire, 1 in cazul nostru, se retine 0.
Pentru varurile ramase, deci de n-1 ori se executa secventa :
1. Se determina cel mai apropiat varf de varful 1 din vectorul d (valoarea minima din d) :
2. In continuare se pune problema daca pentru nodurile neadaugate inca nu s-ar putea
realiza imbunatatiri ale lungimii drumurilor prin 2:
3. Adica pentru un varf x daca d[x]>d[vf]+a[vf][x] atunci re realizeaza modificarea
efectiv in d si se adauga vf ca fiind predecessor (tata) pentru x:
if(d[x]>d[vf]+a[vf][x])
{ d[x]=d[vf]+a[vf][x];
t[x]=vf;
}

ARBORI
1. Definirea arborilor cu rdcin

Definiie: Se numete arbore un graf conex i fr cicluri.


n continuare se vor lua n discuie arborii cu rdcin. n cazul arborilor cu rdcin se pune n eviden un
vrf special al su numit rdcin. Alegerea rdcinii duce la aezarea arborelui pe nivele astfel:
-

se aeaz rcina pe nivelul 1 (acest nivel se noteaz n multe lucrri de specialitate cu 0)


pe fiecare nivel k (k1) se plaseaz acele vrfuri pentru care lungimea lanurilor care le leag de rdcin
este k-1
- se traseaz muchiile arborelui
Putem da i o definiie recursiv a unui arbore cu rdcin: Se numete arborescen un arbore care are un vrf
special numit rdcin iar celelalte noduri pot fi repartizate n m mulimi disjuncte X1, X2,.,Xm, m0, astfel nct n
fiecare din aceste mulimi exist un nod adiacent cu rdcina, iar subgrafurile generate de acestea sunt la rndul lor
arborescene.
n particular, o arborescen cu un singur nod este format doar din nodul rdcin. Dac ordinea relativ a
arborescenelor generate de mulimile X1, X2,.,Xm din definie are importan, arborescena se numete arbore
ordonat.
n reprezentarea grafic a unei arborescene nodurile se deseneaz pe nivele, astfel: rdcina se afl pe primul nivel,
vrfurile adiacente cu rdcina pe urmtorul nivel, etc.
Nodurile adiacente cu rdcina se numesc descendenii rdcinii. Conceptul se aplic analog pentru nodurile de pe
un alt nivel. Descendenii aceluiai nod se numesc frai. Dac nodul x este descendentul unui nod y, l numim pe
acesta din urm printele nodului x.

Se numete nlimea unui arbore diferena dintre nivelul maxim din arbore i nivelul minim (nivelul rdcinii). Pentru
arborele de mai sus, nlimea este 4-1=3.
Observaie: ntr-un arbore orice nod este legat de rdcin printr-un lan unic de lungime minim (corespunztor
parcurgerii BF de la grafuri).

2. Reprezentarea arborilor oarecare

Nodurile unui arbore oarecare pot fi reprezentate sub forma unei zone de memorie alocat dinamic sau sub
forma unui tablou de date alocate static. Fie urmtorul arbore:

Reprezentarea unui arbore oarecare se poate face astfel:


a) Scrierea parantezat: Fiind dat un arbore cu vrfurile etichetate (numere sau caractere), definim scrierea lui
parantezat ca fiind scrierea parantezat a rdcinii sale. Ea const n scrierea etichetei ataat nodului
urmat, numai dac are descendeni, de scrierea parantezat a descendenilor cuprins ntre paranteze.
Pentru arborele de mai sus, scrierea parantezat este:
4(11(5,10),7,8(2,3,1(6)),9(12))
b) Pentru un nod oarecare putem folosi urmtoarea reprezentare cu informaiile alocate dinamic:
- informaia
- adresa primului descendent (fiu) stng
- adresa primului frate drept
Aceast structur poate fi reprezentat astfel:
typedef struct nod
int inf;
nod *fiu_stang, *frate_drept;
ARBORE;
c) Pentru fiecare nod se poate stoca:
- informaia
- numrul de descendeni
- adresa fiecrui descendent
Implementarea poate fi fcut astfel:
typedef struct nod
int inf;
int nr;

//numrul descendenilor

nod *fiu[NR_MAXIM_FII];
ARBORE;

//tablou cu adresele descendenilor

3. Parcurgerea arborilor oarecare

Prin parcurgere se nelege vizitarea n mod sistematic a


arborelui n scopul prelucrrii informaiei ataate
sau a liniarizrii nodurilor.

nodurilor
nodurilor

Vom exemplifica
urmtorul arbore:

metodele

de

parcurgere

pe

Un arbore oarecare poate fi parcurs astfel:


a

n preordine: se viziteaz rdcina i apoi descendenii direci ai fiecrui nod de la stnga la dreapta

Pentru arborele de mai sus, succesiunea preordine a nodurilor este:


4,11,5,10,7,8,2,3,1,6,9,12
b

n postordine: se parcurg de la stnga la dreapta descendenii direci ai fiecrui nod i apoi se viziteaz
rdcina
Pentru arborele de mai sus, succesiunea postordine a nodurilor este:
5,10,11,7,2,3,6,1,8,12,9,4

pe nivele: se parcurg n ordine nodurile de pe fiecare nivel de la stnga la dreapta, ncepnd de la primul
nivel la ultimul nivel:
Pentru arborele de mai sus, succesiunea postordine a nodurilor este:
4,11,7,8,9,5,10,2,3,1,12,6

Dup cum se observ definirea celor trei moduri de parcurgere este recursiv.
Pentru parcurgerea n preordine procedm astfel: se viziteaz nodul rdcin i se apeleaz recursiv funcia
de parcurgere pentru fiecare descendent, de la stnga la dreapta.
Pentru parcurgerea n postordine se apeleaz recursiv funcia pentru parcurgerea tuturor descendenilor i
apoi se va afia informaia nodului rdcin.
Pentru parcurgerea pe nivele (orizontal) se utilizeaz o parcurgere de tip coad pentru c este o parcurgere
BF (n lime). Ct timp coada nu este vid se extrage nodul curent din coad i se adaug la captul opus al cozii toi
descendenii nodului.
Programul urmtor gestioneaz un arbore oarecare reprezentat prin metoda a treia descris n seciunea
anterioar: pentru fiecare nod se precizeaz informaia ataat, numrul de descendeni i tabloul cu adresele acestor
descendeni. Programul ilustreaz modul de creare a unui astfel de arbore, cele trei metode de parcurgere descrise
precum i tergerea din memorie a arborelui creat. Pentru tergere se va proceda astfel: se terg mai nti toi
descendenii unui nod, dup care se terge nodul tat. Ultimul nod ters va fi rdcina arborelui.
#include<iostream>
using namespace std;
#define NMAX 30 //numarul maxim de descendenti ai unui nod
typedef struct nod{
int inf;
int n;
//numarul de descendenti

nod *leg[NMAX]; //tabloul adreselor descendentilor


}ARB;
ARB *coada[100]; //coada pentru parcurgerea pe nivele
int prim,ultim;
//pentru gestionarea cozii
ARB* Creare() //creaza arborele oarecare si returneaza adresa radacinii
{
int info,nr,i; ARB *p;
cout<<"informatia nodului: "; cin>>info;
p=new ARB; p->inf=info;
cout<<"numarul descendentilor pentru "<<info<<": ";
cin>>nr; p->n=nr;
//se descriu in ordine descendentii de la stanga la dreapta si in adancime
for(i=0;i<p->n;i++) p->leg[i]=Creare();
//apelez recursiv functia pentru crearea descendentilor
return p; //radacina arborelui
}
void Preordine(ARB *p) //afiseaza nodurile in preordine
{
int i;
if(p)
{
cout<<p->inf<<" "; //afisez nodul tata
for(i=0;i<p->n;i++) Preordine(p->leg[i]); //afisez descendentii
}
}
void Postordine(ARB *p) //afiseaza nodurile in postordine
{
int i;
if(p)
{
for(i=0;i<p->n;i++)
if(p->leg[i]) Postordine(p->leg[i]); //afisez descendentii
cout<<p->inf<<" "; //afisez nodul tata
}
}
void Adauga(ARB *p) //adauga un nod in coada
{
if(prim>ultim) cout<<"Coada este plina\n";
else coada[ultim++]=p;
}
ARB* Extrage_nod() //extrage un nod din coada
{
if(prim==ultim) return 0;
else return coada[prim++];
}
void Traversare_nivele(ARB *rad)
{
ARB *p; int i;
prim=ultim=0;
Adauga(rad); //in coada se introduce nodul radacina
do{
p=Extrage_nod(); //extrag un nod din coada
if(p)
{
cout<<p->inf<<" "; //afisez informatia nodului
for(i=0;i<p->n;i++)
Adauga(p->leg[i]); //adaug in coada descendentii nodului
}
}while(p);
cout<<"\n";
}
void Sterge(ARB *p) //stergerea unui nod din arbore
{
int i;
if(p)
for(i=0;i<p->n;i++) Sterge(p->leg[i]);
//mai intai sterg descendentii nodului

delete p; //sterg nodul tata


}
int main()
{
ARB *rad; //radacina arborelui oarecare
cout<<"\n\t\tIntroduceti arborele:\n";
rad=Creare(); //arborele a fost creat
cout<<"\n\t\tTraversarea in preordine:\n";
Preordine(rad);
cout<<"\n\t\tTraversarea in postordine:\n";
Postordine(rad);
cout<<"\n\t\tTraversarea pe nivele:\n";
Traversare_nivele(rad);
Sterge(rad); //stergerea arborelui din heap
return 0;
}
4. Definirea arborilor binari

Definiie: Un arbore binar este o mulime finit de noduri care este fie vid, fie reprezint un arbore ordonat n care
fiecare nod are cel mult doi descendeni.

Un arbore binar conine cel mult doi subarbori, pe care i numim subarbore stng, respectiv subarbore
drept. Ei se pot obine prin suprimarea rdcinii i a nodurilor incidente cu aceasta. Un nod fr descendeni se
numete nod terminal sau frunz.

Un arbore binar n care fiecare nod are 0 sau 2 descendeni se numete arbore binar complet. Un astfel de arbore
apare n figura de mai jos.

Proprieti ale arborilor binari

1. Un arbore binar complet care are n noduri terminale, toate situate pe acelai nivel, are n total 2n-1 noduri. n
consecin, un arbore binar complet are un numr impar de noduri.
2. Numrul maxim de noduri de pe nivelul i al unui arbore este 2i.
3. Numrul maxim de noduri dintr-un arbore binar cu nlimea h este 2h+1-1.
4. Un arbore binar cu n noduri are nlimea mai mare sau egal cu [log2n].

5. Reprezentarea static a arborilor binari

Exist mai multe posibiliti de reprezentare a arborilor binari. n continuare sunt descrise trei metode de
reprezentare static a arborilor binari. Vom considera pentru exemplificare urmtorul arbore binar:

a) Reprezentarea standard se bazeaz pe urmtorul principiu:


pentru fiecare nod n parte se precizeaz, dac exist, descendentul
stng i descendentul drept. Dac un nod este terminal, atunci acest
lucru se precizeaz punnd 0 n locul descendenilor si. Pentru aceast
se utilizeaz fie doi vectori numii, de exemplu, S-pentru descendenii din
stnga i D-pentru descendenii din dreapta. Dac pentru reprezentarea
unui arbore binar cu n noduri se folosesc vectorii S i D, atunci pentru
fiecare nod i1,2,....n componenta S[i] conine descendentul stng al
nodului i, iar componenta D[i] conine descendentul drept al nodului i.
De exemplu, pentru arborele binar de mai sus, cei doi vectori vor avea urmtoarea structur:
Nodul i

S[i]

D[i]

Se observ c nu este important s se precizeze rdcina, deoarece ea nu este descendenta nici unui nod.
b) Legturi de tip tat. Se folosesc doi vectori: TATA i DESC. Pentru fiecare nod i, TATA[i] precizeaz care
nod i este ascendent (nodul printe). DESC[i] poate lua dou valori: -1 dac i este descendent stng pentru TATA[i]
i 1 dac este descendent drept pentru acesta. Pentru nodul rdcin, care nu are un nod printe asociat, valoarea
corespunztoare n cei doi vectori este 0. Pentru arborele binar de mai sus, configuraia celor doi vectori este:

Nodul i

TATA[i]

DESC[i]

-1

-1

-1

-1

c) Reprezentarea cu paranteze (parantezat). Pentru a obine o reprezentare a arborelui folosind


paranteze, se procedeaz n felul urmtor:
1) se scrie nodul rdcin
2) fiecare nod al arborelui va fi urmat de:
o parantez rotund deschis
o descendent stng
o virgul
o descendent drept
o parantez rotund nchis
Pentru arborele din figura anterioar, reprezentarea parantezat este: 1( 2 (4 , 5 ( 6 , 7 ( 8 , 9 ) ) ) , 3)

Prin parcurgerea arborilor binari se nelege, ca i la grafurile obinuite, examinarea n mod sistematic a
nodurilor astfel nct fiecare nod s fie atins o singur dat. Aceast procedur se mai numete i vizitare a nodurilor
arborelui n scopul prelucrrii informaiei coninut de fiecare dintre acestea. Deoarece arborii sunt o structur
neliniar de date, rolul traversrii este tocmai obinerea unei aranjri liniare a nodurilor, pentru ca trecerea de la unul
la altul s se realizeze ct mai simplu posibil. Exist trei modaliti de parcurgere a arborilor binari, toate utiliznd
modul standard de reprezentare (sau alocarea dinamic): n preordine (Rdcin-Stnga-Dreapta), n inordine
(Stnga-Rdcin-Dreapta) i n postordine (Stnga-Dreapta-Rdcin). Aceste trei sunt definite recursiv i
parcurgerea se face n trei etape:
a) Traversarea n preordine (RSD)
- se viziteaz rdcina
- traverseaz subarborele stng
- traverseaz subarborele drept
b) Traversarea n inordine (SRD)
- se traverseaz subarborele stng
- se viziteaz rdcina
- se traverseaz subarborele drept
c) Traversarea n postordine (SDR)
- se traverseaz subarborele stng
- se traverseaz subarborele drept
- se viziteaz rdcina
Pentru arborele binar din figura de mai sus, avem urmtoarele rezultate ale parcurgerilor:
- metoda RSD: 1, 2, 4, 5, 6, 7, 8, 9, 3
- metoda SRD: 4, 2, 6, 5, 8, 7, 9, 1, 3
- metoda SDR: 4, 6, 8, 9, 7, 5, 2, 3, 1
Programul care implementeaz cele trei metode de parcurgere (bazate pe o strategie DEI) este prezentat n
continuare:
#include<iostream>
using namespace std;
#define N 30
int S[N],D[N],rad,n;
void SRD(int k) //parcurgere inordine
{
if(S[k]) SRD(S[k]);
cout<<k<<" ";
if(D[k]) SRD(D[k]);
}
void RSD(int k) //parcurgere preordine
{
cout<<k<<" ";
if(S[k]) RSD(S[k]);
if(D[k]) RSD(D[k]);
}
void SDR(int k) //parcurgere postordine
{
if(S[k]) SDR(S[k]);
if(D[k]) SDR(D[k]);
cout<<k<<" ";
}
int main()
{
int i;
cout<<"numar varfuri, n=";
do{
cin>>n;
}while(n<1||n>N);
cout<<"radacina arborelui: ";
do{
cin>>rad;
}while(rad<1||rad>n);
//memorarea arborelui prin vectorii S,D
for(i=1;i<=n;i++)
{
cout<<"descendentii stanga,dreapta ("<<i<<"): ";

cin>>S[i]>>D[i];
}
cout<<"\n\tParcurgerea in preordine:\n";
RSD(rad);
cout<<"\n\n\tParcurgerea in inordine:\n";
SRD(rad);
cout<<"\n\n\tParcurgerea in postordine:\n";
SDR(rad);
cout<<"\n";
return 0;
}
6. Alocarea dinamic a arborilor binari

n acest mod de alocare, fiecare nod este o structur cu trei cmpuri: informaia ataat nodului, adresa fiului
stng i adresa fiului drept. Absena unui fiu este marcat cu pointerul nul. Se observ c acest mod de reprezentare
este similar cu reprezentarea standard de la alocarea static a arborilor binari. Pentru descrierea unui nod putem
folosi declaraiile:
typedef struct nod
int inf;

//sau orice alt tip pentru informaie

nod *st,*dr; //adresele fiilor


ARB;

Crearea unui arbore binar alocat dinamic se realizeaz cel mai uor aplicnd metoda DEI astfel:
-

se genereaz un nod, adic se aloc spaiu n heap i se ncarc informaia


pentru fiecare nod se construiete subarborele su stng, apoi subarborele su drept i se completeaz
adresele descendenilor nodului cu adresele acestor subarbori
un descendent vid trebuie marcat printr-o proprietate stabilit asupra informaiei (spre exemplu apariia valorii
0 drept coninut informaional al nodului curent)
Spre exemplu, pentru arborele binar urmtor
irul datelor furnizat la intrare va arta astfel: 1 2 4 0 0 5
7 0 0 0 3 0 6 8 0 0 9 0 0
ceea ce corespunde
unei liste n preordine a nodurilor.

Funcia care creaz un arbore binar furnizat ca mai sus este:

typedef struct nod{


int inf;
nod *st,*dr;
}ARB;
void Creare(ARB* &r)
{
int x;
cin>>x;

if(!x)

//urmeaza subarbore vid


r=0;

else
{

r=new ARB; r->inf=x;


//creare nod curent si memorare informatie
Creare(r->st); Creare(r->dr); //crearea subarborilor

}
Funcia Creare() se poate proiecta astefel nct s ntoarc adresa rdcinii arborelui. n acest caz, funcia va
avea antetul: ARB* Creare(ARB* &r).

Parcurgerea arborilor binari poate fi fcut:


-

n adncime (pe subarbori) n trei moduri: preordine (RSD), inordine (SRD), postordine(SDR)
n lime (pe niveluri, ca la arborii oarecare)

Programul urmtor realizeaz crearea unui arbore binar i afieaz listele nodurilor obinute pein cele trei metode de
parcurgere n adncime.

#include<iostream>
using namespace std;
typedef struct nod{
int inf;
nod *st,*dr;
}ARB;
void Creare(ARB* &r)
{
int x;
cin>>x;
if(!x) //urmeaza subarbore vid
r=0;
else
{
r=new ARB; r->inf=x;
//creare nod curent si memorare informatie
Creare(r->st); Creare(r->dr); //crearea subarborilor
}
}
void SRD(ARB *r) //parcurgere inordine
{
if(r)
{
SRD(r->st); cout<<r->inf<<" "; SRD(r->dr);
}
}
void RSD(ARB *r) //parcurgere preordine
{
if(r)
{
cout<<r->inf<<" "; RSD(r->st); RSD(r->dr);
}
}
void SDR(ARB *r) //parcurgere postordine
{
if(r)
{
SDR(r->st); SDR(r->dr); cout<<r->inf<<" ";
}
}
void Sterge(ARB *r)
{

if(r)
{
}

Sterge(r->st); Sterge(r->dr); delete r;

}
int main()
{
ARB *rad;
//adresa radacinii arborelui
cout<<"Introduceti nodurile : ";
Creare(rad); //crearea arborelui binar
cout<<"\nNodurile in inordine:
"; SRD(rad);
cout<<"\nNodurile in preordine: "; RSD(rad);
cout<<"\nNodurile in postordine: "; SDR(rad);
cout<<"\n";
Sterge(rad); //stergerea arborelui din heap
return 0;
}
7. Forma polonez a expresiilor aritmetice

Se mai numete notaia fr paranteze a expresiilor aritmetice i de studiul acestei probleme s-a ocupat
matematicianul polonez J. Lukasiewicz. Deoarece ntr-o expresie aritmetic, un operator se aplic unui operand sau
unei perechi de operanzi, s-a ajuns la ideea de a asocia unei expresii aritmetice un arbore binar. Presupunem c se
noteaz cu E1 respectiv cu E2 dou expresii aritmetice crora li se aplic operatorul notat op. Operatorii pe care i
vom accepta sunt doar operatorii binari, i anume: adunarea (+), scderea (-), nmulirea (*), mprirea (/) i ridicarea
la putere (^). Pentru a asocia unei expresii aritmetice fr paranteze un arbore binar se folosesc urmtoarele reguli:
-

unei expresii aritmetice format dintr-un singur operand i se asociaz un arbore binar format doar din nodul
rdcin n care se pune operandul respectiv
dac expresia aritmetic E este de forma E1 op E2, atunci arborele binar complt asociat are ca rdcin
operatorul op, ca subarbore stng are arborele binar asociat expresiei aritmetice E1, iar ca subarbore drept
are arborele binar asociat expresiei aritmetice E2

Exemplu: Fie expresiile cele mai simple care se pot forma cu operatorii binari cunoscui: a+b, a-b, a*b, a/b i ab.
Considernd op unul dintre operatorii binari +,-,*,/,, arborele binar asociat este cel
din figura urmtoare:

Exemplu: Fie expresia E=a*b+c/d-e. Ne propunem s determinm arborele binar asociat. Se observ c, deoarece
nu este stabilit modul de asociere a termenilor, se pot obine doi arbori binari, dup cum se consider E1=a*b+c/d i
E2=e, sau E1=a*b i E2=c/d-e. Se obin corespunztor arborii din figura de mai jos.
Aceti arbori binari se pot parcurge folosind
oricare din cele trei metode. Se obine astfel:
RSD: -+*ab/cde respectiv
RSD: +*ab-/cde
SRD: a*b+c/d-e respectiv
SRD: a*b-c/d-e
SDR: ab*cd/+e- respectiv
SDR: ab*cd/e-+

irul obinut prin parcurgerea n preordine a arborelui binar ataat expresiei aritmetice se numete forma polonez
prefixat asociat expresiei, iar irul obinut prin prin parcurgerea n postordine se numete forma polonez
postfixat asociat expresiei aritmetice.
Observaii: Se poate construi arborele binar asociat i pentru o expresie aritmetic care conine paranteze rotunde.
n forma polonez nu apar parantezele, acestea nemaifiind necesare pentru a marca prioritile de calcul.
Programul urmtor citete o expresie aritmetic care conine paranteze rotunde, operanzi de o liter i
operatori binari +,-,*,/ i construiete forma polonez prefixat. Pentru a construi n memorie arborele binar ataat
pornind de la expresia aritmetic (citit ca ir de caractere) se acord prioriti operatorilor i operanzilor astfel:
- prioritatea iniial a operatorilor +,- este 1
- prioritatea iniial a operatorilor *,/ este 10
- la prioritatea unui operand se adaug zece uniti pentru fiecare pereche de paranteze ntre care se gsete
- prioritatea unui operand este 1000
Construirea prioritilor se realizeaz astfel:
- se citete expresia aritmetic n irul de caractere e
- se utilizeaz o variabil j care indic ce valoare se adaug la prioritatea iniial a unui operator (la ntlnirea
unei paranteze deschise j crete cu 10, iar la ntlnirea unei paranteze nchise scade cu 10)
- se parcurge expresia caracter cu caracter i se memoreaz n vectorul p prioritile operatorilor i operanzilor
- n vectorul efp se construiete expresia aritmetic fr paranteze, iar n pfp se obine vectorul prioritilor din
care, spre deosebire de epf, lipsesc componentele corespunztoare parantezelor
- utiliznd vectorii efp i pfp funcia Creare() construiete n heap arborele binar ataat expresiei aritmetice.
Conform tehnicii Divide Et Impera, funcia Creare() lucreaz astfel:
- de la limita superioar (ls) ctre cea inferioar (li), limite corespunztoare irurilor din efp, se caut operandul
sau operatorul cu prioritate minim, reinndu-se i poziia acestuia
- n situaia n care limita superioar este diferit de cea inferioar, pentru completarea adresei subarborelui
stng, respectiv drept, se reapeleaz funcia Creare()
#include<iostream>
#include<string.h>
using namespace std;
#define MAX 100
typedef struct nod{
char inf;
nod *st,*dr;
}ARB;
char e[MAX],efp[MAX];
int p[MAX],pfp[MAX];
ARB *rad;
void Creare(ARB* &c,int li,int ls,char epf[],int pfp[])
{
int i,j,min;
min=pfp[ls]; i=ls;
for(j=ls;j>=li;j--)
if(pfp[j]<min) { min=pfp[j]; i=j; }
c=new ARB; c->inf=efp[i];
if(li==ls)
c->st=c->dr=0;
else
{
Creare(c->st,li,i-1,efp,pfp);
Creare(c->dr,i+1,ls,efp,pfp);
}
}
void Parcurgere(ARB *c)
{
if(c)
{
cout<<c->inf;Parcurgere(c->st); Parcurgere(c->dr);
}
}
int main()
{
int i,j=0;
cout<<"introduceti expresia: "; gets(e);

for(i=0;e[i];i++)
switch(e[i])
{
case ')': j-=10; break;
case '(': j+=10; break;
case '+':
case '-': p[i]=j+1; break;
case '*':
case '/': p[i]=j+10; break;
default: p[i]=1000;
}
j=-1;
for(i=0;e[i];i++)
if(e[i]!=')'&&e[i]!='(')
{
j++; efp[j]=e[i]; pfp[j]=p[i];
}
Creare(rad,0,j,efp,pfp);
cout<<"\nForma poloneza prefixata este: ";
Parcurgere(rad); cout<<"\n";
return 0;
}
Modul n care se folosete forma polonez prefixat pentru evaluarea expresiei aritmetice asociate este
urmtorul: se parcurge forma polonez prefixat de la dreapta la stnga utilizndu-se o stiv pentru memorarea
operanzilor. Dac simbolul curent este un operand, el se introduce n vrful stivei. Dac simbolul citit este un operator,
se aplic acest operator primilor doi operanzi din vrful stivei obinndu-se un rezultat. Cei doi operanzi sunt eliminai
din stiv i n locul lor se trece rezultatul obinut. Se trece apoi la urmtorul simbol care conine forma polonez
prefixat asociat expresiei aritmetice.
Programul urmtor ilustreaz aceast modalitate de evaluare a expresiilor aritmetice. Operanzii sunt
identificai printr-o singur liter, iar operatorii sunt binari din mulimea +,-,*,/.
#include<iostream>
#include<string.h>
using namespace std;
char e[100];
float s[100],x,y; //s este stiva
int n,i,p;
int main()
{
cout<<"\tIntroduceti expresia aritmetica in forma prefixata\n";
gets(e); n=strlen(e);
for(i=n-1;i>=0;i--)
if(e[i]=='+'||e[i]=='-'||e[i]=='*'||e[i]=='/')
{
x=s[p--]; y=s[p];
switch(e[i])
{
case '+': s[p]=x+y; break;
case '-': s[p]=x-y; break;
case '*': s[p]=x*y; break;
case '/': s[p]=x/y; break;
}
}
else
s[++p]=e[i]-'0';
cout<<"\nvaloarea expresiei este "<<s[1]<<endl; return 0;}

S-ar putea să vă placă și