Documente Academic
Documente Profesional
Documente Cultură
-------------------------------------------------------------------------------------------------------------
1
NoŃiuni introductive
S Caracteristica Mantisa(normalizată)
Structuri de date liniare şi arborescente în C 13
-------------------------------------------------------------------------------------------------------------
În Limbajul "C", datele reale pot fi de tip: float, double, long double
aparŃine intervalului
[1.7*10-308, 1.7*10308]
long double Valoarea absolută a unei date diferite de zero
aparŃine intervalului
-4932 4932
[3.4*10 , 3.4*10 ]
• Tipul tablou
tip nume[lim1][lim2]…[limn];
unde:
anumită ordine: a[k1], a[k2], ... , a[kn] astfel încât şirul cheilor să
devină monoton crescător (sau descrescător), adică:
a[k1].cheie≤ a[k2].cheie≤ ...≤ a[kn].cheie
Tipul cheii l-am presupus a fi întreg pentru o înŃelegere mai
uşoară, în realitate el poate fi de orice tip scalar.
O metodă de sortare se spune că este stabilă dacă după
sortare, ordinea elementelor cu chei egale coincide cu cea iniŃială.
Metodele de sortare sunt clasificate în două mari categorii
după cum elementele de sortat sunt înregistrate în memoria centrală
sau într-o memorie externă.
În primul caz elementele se pot considera a fi organizate într-
o structură de tablou şi în consecinŃă ele sunt accesibile într-o ordine
oarecare, respectiv în orice moment pot fi comparate între ele
oricare dintre chei.
În al doilea caz elementele formează o structură secvenŃială
de fişier, structură care permite accesul la chei numai în ordine
secvenŃială.
Din acest motiv, algoritmii de sortare se clasifică în două mari
categorii: sortarea tablourilor numită şi sortare internă şi sortarea
fişierelor numită şi sortare externă.
i = 5.
=> j =0 => k=0 => Aux = 18 => A=(12, 22, 22, 34, 65, 83, 4, 67), apoi
A[k+1] = Aux => A=(12, 18, 22, 34, 65, 83, 4, 67);
i = 6.
=> nu există j astfel încât A[j] < A[i] => k=-1 => Aux = 4 => A=(12,
12, 18, 22, 34, 65, 83, 67) => A[k+1] =4 =>
A= ( 4, 12, 18, 22, 34, 65, 83, 67) ;
i = 7.
=> j =5 => k=5 => Aux = 67=> A=(4,12, 18, 22, 34, 65, 83, 83) =>
A[k+1]= 67 => A=(4, 12, 18, 22, 34, 65, 67, 83).
P
START
j=i-1
Cit n,
a[i], i=0,n-1
j ≥ 0 si DA
j=j-1
a[j].cheie>a[i].cheie
i=1
DA
NU
i<n P k=j
NU
aux=a[i]
Scrie a[i],
i=0,n-1
j=i-1
STOP DA
j>k a[j+1]=a[j]
j=j-1
NU
22 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
i=i+1
Exit
n −1 n −1
n(n − 1) n 2 − n
c min = ∑ 1 = n − 1 c max = ∑ (i − 1) = =
i =1 i =1 2 2
c min + c max n2 + n − 2
c med = =
2 4
Cit n,
a[i], i=0,n-1
i=1
DA
i<n P
NU
Scrie a[i],
i=0,n-1
P STOP
P1
s=0
d=i
m=[s+d]/2
DA
s< d P1
NU DA
a[m]≤a[i]
NU
d=m s=m+1
aux=a[i]
j=i-1 Exit
DA a[j+1]=a[j]
j≥d j=j-1
NU
24 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
Exemplu:
A=(34,65,12,22,83,18,04,67)
START P
k=i
Cit n, min=A[i]
a[i], i=0,n-1
j=i+1
i=0
Min DA
DA
j<n Min
i<n-1 P
min>a[j] DANU
NU A[k]=A[i]
A[i]=min
min=a[j]
NU k=j
Scrie a[i],
i=0,n-1
i=i+1
Exit
În general
Mmax= [(n-1)+(n-3)+…+1]+3(n-1) =
= (n-1)+(n-2)+(n-3) +…+ 3+2+1 -
28 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
- [(n-2)+(n-4) +…+4+2]+3(n-1)=
n−2 n−2
( + 1)
n(n + 1)
= −2 2 2 + 3(n − 1) =
2 2
n(n − 1) n − 2 n
= − ⋅ + 3(n − 1) =
2 2 2
n2
= + 3(n − 1)
4
START P
j=n-1
Cit n,
a[i], i=0,n-1
j>=i DA
P1
i=1 NU
i=i+1
DA
i≤n-1 P
Exit
P1
NU
Scrie a[i], DA
i=0,n-1 a[j-1]>a[j]
aux=a[j-1]
a[j-1]=a[j]
STOP NU a[j]=aux
j=j-1
Exit
START P P1
sw=0 DA
Citeste n,
a[i],i=0,n-1 a[j-1]>a[j]
j=n-1 aux=a[j-1]
a[j-1]=a[j]
i=1 NU
a[j]=aux
sw=1
DA
j>=i P1
P
NU
i=i+1 j=j-1
DA
i<=n-1 ||
sw==1
Exit
Exit
NU
Tip a[i],i=0,n-1
STOP
Structuri de date liniare şi arborescente în C 31
-------------------------------------------------------------------------------------------------------------
START P
i=d
Citeste n,
a[i],i=0,n-1
DA
i>=s P1
s=0
NU
d=n-2 s=k+1
k=n-1
i=s
P
DA
DA i<=d P2
s<=d
NU NU
d=k-1
Tipareste a[i],
i=0,n-1
Exit
STOP
32 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
3n (n − 1) 3(n 2 − n )
Mmin = 0, Mmax = 3C = , Mmed = ,
2 4
ale tipului compus constă din toate combinaŃiile posibile ale valorilor
tipurilor componente, selectând câte o singură valoare din fiecare. O
astfel de combinaŃie se numeşte n-tuplu; numărul acestor combinaŃii
este egal cu produsul numerelor de elemente ale fiecărui tip
constitutiv.
Datele care se grupează formează o structură - în C,
respectiv un articol sau o înregistrare (record) - în PASCAL.
Unei structuri i se ataşează un nume. De asemenea, se
ataşează câte un nume fiecărei componente ale unei structuri, nume
care se utilizează la referirea componentelor respective.
Componentele structurii se numesc câmpuri iar numele lor se
numesc identificatori de câmp.
Accesul la câmpurile unei variabile structurate de tip structură
se face precizând numele câmpului şi numele structurii căreia îi
aparŃine câmpul.
Un exemplu simplu de structură este data calendaristică, care
se compune din zi, lună şi an,
unde:
zi şi an sunt de tip întreg
iar
luna este un tablou de tip caracter.
Structura, ca şi tabloul, este o mulŃime ordonată de elemente. În
exemplul de mai sus se consideră că zi este primul ei element, luna
al doilea, iar an ultimul ei element.
O structură se declară în felul următor:
struct nume
{
istă de declaraŃii
} nume1,nume2, … , numen;
unde:
nume, nume1,nume2, … , numen - sunt nume care pot şi lipsi, dar
nu toate deodată.
34 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
Exemplu:
Data calendaristică indicată mai sus, se poate declara în felul
următor:
struct data_calend
{
int zi;
char luna[11];
int an;
} data_nasterii, data_angajarii;
struct
{
int zi;
char luna[11];
int an;
Structuri de date liniare şi arborescente în C 35
-------------------------------------------------------------------------------------------------------------
} data_nasterii, data_angajarii;
Modul cel mai frecvent întâlnit este acela în care prin declaraŃia de
structură introducem numai tipul ei:
struct data_calend
{
int zi;
char luna[11];
int an;
};
unde:
tip - Este fie numele unui tip predefinit, fie o declaraŃie de tip
definită de utilizator;
36 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
Reprezentarea structurii
Structurile sunt reprezentate în memoria unui sistem de calcul
prin simpla alăturare a componentelor lor. Adresa unei componente
(câmp) ci relativă la adresa de origine a structurii S se numeşte
deplasamentul (offset) ki al componentei. El se calculează astfel :
ki = s1 + s2 + . . . + si-1
unde sj j=1,i-1 este mărimea în unităŃi de informaŃie a celei de-a j-a
componente.
Caracterul de generalitate al unei structuri de tipul struct nu
permite calculul simplu al deplasamentelor câmpurilor ca şi în cazul
Structuri de date liniare şi arborescente în C 37
-------------------------------------------------------------------------------------------------------------
• Structura secvenŃă
A1 A2 . . . Ak-1 Ak . . . An EOF
Fig.1.2. Pointerul după citirea ultimului articol din fi şier
A1 A2 . . . A k-1 Ak . . . An EOF
număr_relativ × lungime_articol.
Organizarea aleatoare
Acest tip de organizare presupune memorarea înregistrărilor în
fişier în funcŃie de valoarea unui anumit câmp din înregistrare, numit
cheia înregistrării. Orice operaŃie de I/E referitoare la o înregistrare
Structuri de date liniare şi arborescente în C 43
-------------------------------------------------------------------------------------------------------------
Organizarea relativă
Într-un fişier de organizare relativă informaŃiile sunt organizate
în celule numerotate de la 1 la n, 1 - prima celulă, n - ultima celulă,
fiecare celul ă având aceeaşi lungime. Numărul celulei reprezintă
44 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
Organizarea indexată
Elementele fişierului, informaŃia este identificată după valoarea
unui anumit câmp din articol, numit câmp cheie sau zonă cheie.
Acest câmp are aceeaşi lungime şi aceeaşi poziŃie relativă în toate
articolele. Pentru un fişier de organizare indexată pot fi definite mai
multe câmpuri cheie, dar dintre acestea unul este obligatoriu şi se
numeşte cheie primară, iar celelalte se numesc chei secundare.
Fiecare fi şier indexat conŃine pe lângă informaŃia propriu-zisă şi o
tabelă de index. O tabelă de index conŃine valorile câmpului cheie
împreună cu adresele la care sunt memorate elementele cu cheia
respectivă. Tabelele de indecşi sunt numerotate (0..256 dBase).
Tabela de index cu numărul 0 se numeşte tabelă de index primară şi
este asociată cheii primare.
În fi şierul care conŃine informaŃiile despre muncitorii unei
societăŃi comerciale, cu articole de forma:
Exemplu: FIŞIER
46 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
T10 00001 .
S1 .
00020 .
00021
00024 T23 S1 02110
T10 00029 01131
S2
00105 01136
00111 01151
00113 T23 S2 02200
. 02201
. 02204
. 02207
T10 00191 .
S9 .
00203 .
00204
00205 T23 S9 03001
T11 00206 03002
S1
00210 03003
00211
00230
T11 00250
S2
00251
00252
00253
.
.
.
T11 00299
S9
00300
00303
00305
Structuri de date liniare şi arborescente în C 47
-------------------------------------------------------------------------------------------------------------
T23
03001 03003 S9
memoria centrală
citire
scriere
zonă tampon
suport
3. Actualizare:
- adăugare de noi articole în fi şier
- modificare: pentru aceasta articolul se transferă în memoria
central ă, se face modificarea dorită şi se copiază din
nou pe suport în formă modificată, în locul de unde a fost citit .
- ştergere.
Ex:
PETRU
struct expresie
{
char operator;
struct expresie *operand1,*operand2;
};
Exemplu: (x∗(y-z))/u
∗ u NULL NULL
x NULL NULL -
2
Structura de date de tip listă
Din punct de vedere matematic, o listă este o secvenŃă de zero
sau mai multe elemente numite noduri, de un anumit tip numit tip de
bază. Dacă n≥1, lista se prezintă de regulă astfel : a1, a2, ... , an, şi
fiecare ai aparŃine tipului de bază. Numărul n al nodurilor se numeşte
lungimea listei. Presupunând n≥1 se spune că a1 este primul nod al
listei iar an este ultimul nod. Dacă n=0 avem de-a face cu o listă vidă.
O proprietate importantă a unei liste este aceea că nodurile
sale pot fi ordonate liniar în funcŃie de poziŃia lor în cadrul listei. Se
spune că ai precede pe ai+1 pentru i=1,2, ... ,n-1 şi că ai succede pe
ai -1 pentru i = 2,3, ... ,n. De regulă se spune că nodul ai se afl ă pe
poziŃia i.
Aşadar o listă liniară este o colecŃie de n ≥ 0 noduri ale căror
proprietăŃi structurale se reduc în principal la poziŃiile relative liniare
(unidimensionale) ale acestor noduri.
OperaŃiile pe care dorim să le efectuăm asupra listelor liniare
pot include, de exemplu, următoarele :
1) Accesul la nodul al i-lea din listă pentru a examina şi/sau
modifica conŃinutul câmpurilor sale.
2) Inserarea unui nod nou înaintea nodului al i-lea.
3) Ştergerea nodului al i-lea.
4) Combinarea a două sau mai multe liste într-o singură listă.
5) DespărŃirea unei liste liniare în două sau mai multe liste.
6) Copierea unei liste liniare.
7) Determinarea numărului de noduri dintr-o listă liniară.
8) Sortarea nodurilor unei liste liniare într-o ordine crescătoare
a conŃinutului anumitor câmpuri ale nodurilor.
9) Căutarea în listă a nodului cu o valoare particulară a unui
anumit câmp.
Structuri de date liniare şi arborescente în C 53
-------------------------------------------------------------------------------------------------------------
intrarea în
coadă
ieşirea din coadă
ştergere inserare
vârf
(început) FaŃă Al 2-lea Al 3-lea Spate
b)Coadă
#define lungime_max 10
int ultim;
} lista;
Primul nod
Al doilea nod
ultim lista
Ultimul nod
Gol
info urm
p el1 el2 eln NULL
char info[10];
struct nod *urm;
};
typedef struct nod Tnod;
1 2 2 3
q info cheie urm
} /* ins_p */
p p
4
3
1 2
q
p q 5 r
1
4 NULL
2 3
void listare(void)
{
r=p;
while (r!=NULL)
{
printf("Cheia: %-10d | Informatia: %10s\n",r->cheie,r->info);
r=r->urm;
}
} /* listare */
void cautare(void)
{
r=p;
while ((r->cheie!=k)&&(r!=NULL))
r=r->urm;
} /* cautare */
else r=r->urm;
}
} /* cautare */
4
3
r
1
s 2
r->urm=s; /* 4 */
} /* ins_d */
p r
NULL
4 3
info 2 2
s
1
void ins_i(void)
{
s=(ref)malloc(sizeof(Tnod)); /* 1 */
*s=*r; /* 2 */
r->urm=s; /* 3 */
Structuri de date liniare şi arborescente în C 65
-------------------------------------------------------------------------------------------------------------
NULL
r
Fig. 2.10 Suprimarea unui nod *r
{
if (r->urm==NULL)
printf("Eroare: nodul de suprimat nu exista.\n");
else
{
s=r->urm;
r->urm=s->urm;
free(s);
printf("Suprimarea s-a realizat cu succes.\n");
}
} /* suprima_d */
s=r->urm;
*r=*s;
free(s);
p=(ref)malloc(sizeof(Tnod));
fanion=(ref)malloc(sizeof(Tnod));
p->urm=fanion;
fanion->urm=NULL;
fanion->cheie=x;
while(q1->cheie<x)
{
q2=q1;
q1=q2->urm;
}
if((q1->cheie==x)&&(q1!=fanion))
q1->contor++;
else
{
r=(ref)malloc(sizeof(Tnod));
r->cheie=x;
r->contor=1;
r->urm=q1;
q2->urm=r;
}
} /* caută */
void listare(void)
{
if(p->urm==fanion)
printf("Lista vida !\n");
else
{
clrscr();
Structuri de date liniare şi arborescente în C 71
-------------------------------------------------------------------------------------------------------------
NULL NULL
72 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
r
2
Fig. 2.12 Suprimarea unui nod dintr-o listă dublu înlănŃuită
void sterg_nod(void)
{
ref pred,suc;
pred=r->ant;
suc=r->urm;
if (r->urm!=NULL) suc->ant=pred;
if (r->ant!=NULL) pred->urm=suc;
}
Nodul suprimat este indicat în continuare de r. SpaŃiul de
memorie care a fost alocat nodului *r, ar putea fi reutilizat în regim
de alocare dinamică a memoriei. Dacă în prelucrările ulterioare nu
mai avem nevoie de nodul *r, spaŃiul care a fost alocat nodului *r
poate fi pus la dispoziŃia sistemului, în limbajul C, prin free(r).
Când nodul de suprimat este primul nod al listei (r=p), trebuie
modificat şi pointerul p care conŃine adresa acestuia, astfel încât să
indice succesorul lui ( cel care va deveni primul nod al listei, după
suprimare) şi de aceea funcŃia trebuie să fie completată cu
rezolvarea şi a acestui caz:
void sterg_nod(void)
{
ref pred,suc;
pred=r->ant;
suc=r->urm;
if (r->urm!=NULL) suc->ant=pred;
if (r->ant!=NULL) pred->urm=suc;
if (r==p) /* daca nodul de suprimat este primul nod */
p=p->urm;
free(r);
}
O listă dublu înlănŃuită permite inserarea uşoară a unui nod fie la
dreapta fie la stânga unui nod dat r.
NULL NULL
4 5 3 6
r
1
s 2
Fig. 2.14 Inserarea după nodul *r
void insd_d(void)
{
ref s,suc;
s=(ref)malloc(sizeof(Tnod)); /* 1 */
printf("Introduceti informatia:");fflush(stdin);scanf("%s",s->info);/*2 */
printf("Introduceti cheia: "); scanf("%d",&s->cheie); /* 2 */
suc=r->urm;
s->urm=suc; /* 3 */
s->ant=r; /* 4 */
r->urm=s; /* 5 */
if (suc!=NULL)
suc->ant=s; /* 6 */
}
NULL NULL
6 5
Structuri de date liniare şi arborescente în C 75
-------------------------------------------------------------------------------------------------------------
4 3 r
s 1 2
void insd_i(void)
{
ref s,pred;
pred=r->ant;
s=(ref)malloc(sizeof(Tnod)); /* 1 */
printf("Introduceti informatia:");fflush(stdin);scanf("%s",s->info); /*2*/
printf("Introduceti cheia: "); scanf("%d",&s->cheie); /* 2 */
s->urm=r; /* 3 */
s->ant=pred; /* 4 */
r->ant=s; /* 5 */
if (pred!=NULL)
pred->urm=s; /* 6 */
else p=s;
}
2.2 STIVE
O stivă este un tip special de listă în care toate inserările,
suprimările şi orice alt acces se execută la un singur capăt, care se
numeşte vârful stivei. Stivele se mai numesc liste de tipul LIFO (Last
In First Out) , adică “ultimul element introdus primul suprimat” sau
liste de tipul “push-down”.
Deoarece stiva este o listă liniară cu caracter special toate
implementările discutate până acum sunt valabile şi pentru stive.
0
gol
vîrf
primul element
al doilea element
#define lungime_max 10
int stivid(Stiva s)
{
if (s.varf>=lungime_max)
return 1;
else return 0;
} /* stivid */
int virfst(Stiva s)
{
if (stivid(s))
{
er=1;
printf("Stiva este vida.\n");
return 0; /* aici fc. trebuie sa intoarca o valoare diferita de cea
a elementelor stivei*/
}
else return s.elemente[s.varf];
} /* virfst */
Structuri de date liniare şi arborescente în C 79
-------------------------------------------------------------------------------------------------------------
2.3 COZI
Cozile sunt o altă categorie specială de liste în care
elementele sunt inserate la un capăt (spate) şi sunt suprimate la
cel ălalt capăt (faŃă). Cozile se mai numesc liste de tipul “FIFO”
(First-In-First-Out) adică liste de tipul “primul-venit-primul-servit”.
OperaŃiile care se execută aupra cozii sunt analoage cu cele asupra
stivei, cu diferenŃa că inserŃiile se fac la spatele cozii şi nu la
începutul ei şi că ele diferă ca terminologie.
typedef struct
{
Tnod *fata,*spate;
} Coada;
c.fată
NULL
c.spate
void initializare(Coada* c)
{
c->fata=(ref)malloc(sizeof(Tnod)); /* creeaza nodul fictiv; */
c->fata->urm=0;
c->spate=c->fata; /* nodul fictiv este primul si ultimul nod; */
} /* initializare */
int cvid(Coada c)
{
if (c.fata==c.spate)
return 1;
else return 0;
} /* cvid */
tipelement primul(Coada c)
{
if (cvid(c))
{
er=1; printf("Eroare: coada este vida.\n");
return 0;
}
else return c.fata->urm->element;
} /* primul */
c.spate
2
Fig. 2.18 Adăugarea unui nod la o coadă
void adauga(tipelement x, Coada *c)
{
c->spate->urm=(ref)malloc(sizeof(Tnod)); /* 1 */
c->spate=c->spate->urm; /* 2 */
Structuri de date liniare şi arborescente în C 83
-------------------------------------------------------------------------------------------------------------
c->spate->element=x; /* 3 */
c->spate->urm=NULL; /* 3 */
} /* adauga */
5. FuncŃia sterge(&c) suprimă primul element al cozii c, dacă
coada nu este vidă:
Coada înainte de suprimare
c.fata NULL
c.spate
Coada după suprimare
c.fata NULL
c.spate
.
.
.
2 n-4
n-3
1
n-2
0 n-1
Fig. 2.20 Coada circulară
.
. . c.fata = 0
c.spate = 3
e3 3
2 n-1
e2 1 0
e1 e0
e4 .
4
e3 3
2 n-1
e2 1 0 Fig. 2.21 Adăugarea unui element într-o
coadă circulară
e1 e0
.
4 e4
3 e3
e2
2 e1 e0 n-1
1 0
. .
coada plină
e1
en-2
e0 en-1
c.fata 0 n-1 c.spate
Structuri de date liniare şi arborescente în C 87
-------------------------------------------------------------------------------------------------------------
e c.fata
2 2
1 n-1 c.spate 1 n-1
0 0
c.fata
c.spate
1. int avanseaza(int i)
{
if (i== Lung_max -1)
return 0;
else return i+1;
} /* avanseaza */
avanseaza(c.spate)=0
int cvid(Coada c)
{
if (avanseaza(c.spate)==c.fata)
return 1;
else return 0;
} /* cvid */
tipel primul(Coada c)
{
if (cvid(c))
{
printf("Eroare: Coada este vida.\n");
er=1;
return 0;
}
else return c.elemente[c.fata];
} /* primul */
else
{
(*c).spate=avanseaza((*c).spate);
(*c).elemente[(*c).spate]=x;
}
}/* adauga */
3
Structuri Arborescente
Cele mai importante structuri neliniare ce intervin în algoritmii
de prelucrare cu calculatorul sunt structurile arborescente. Vorbind
în general, structurile arborescente presupun relaŃii de “ramificare”
între noduri, asemănător configuraŃiei arborilor din natură.
DefiniŃie. Prin arbore se înŃelege o mulŃime finită de n ≥ 0 elemente
denumite noduri sau vârfuri, de acelaşi fel, care dacă nu este vidă
atunci are:
a) un nod cu destinaŃie special ă, numit rădăcina arborelui ;
b) celelalte noduri sunt repartizate în m ≥ 0 seturi disjuncte A1,
A2,..., Am, fiecare set Ai constituind la rândul său un arbore.
Există şi alte defini Ńii nerecursive pentru arbori (de exemplu cea pe
baza relaŃiei de preordine), dar defini Ńia recursivă pare cea mai
potrivită deoarece recursivitatea reprezintă o caracteristică naturală
a structurilor arborescente.
Elementele arborelui sunt nodurile şi legăturile dintre ele.
Nodul rădăcină r îl considerăm ca fiind de nivelul zero. Întrucât A1,
A2,..., Am sunt la rândul lor arbori, fie r1, r2,..., rm rădăcinile lor. Atunci
r1, r2,..., rm formează nivelul unu al arborelui (fig.3.1).
Nodul r va avea câte o legătură cu fiecare dintre nodurile r1,
r2,..., rm . Continuând acest procedeu vom avea nivelurile 2, 3, ... ale
arborelui. Deci fiii tuturor nodurilor nivelului i formează nivelul i+1.
Dacă numărul nivelurilor este finit atunci şi arborele este finit.
În caz contrar se numeşte arbore infinit.
92 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
r nivelul 0
r1 r2 . . . rm nivelul 1
A A
B C C B
Fig. 3.2
nivel 0 A
nivel 1 B C D
nivel2 E F G H I J
nivel 3 K L M N O
nivel 4 P
94 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
Fig. 3.3
ÎnălŃimea unui nod într-un arbore este lungimea celui mai lung
drum de la nodul respectiv la un nod terminal. Pornind de la această
definiŃie, înălŃimea unui arbore se poate defini şi ca fiind egal ă cu
înăl Ńimea nodului rădăcină.
Adâncimea unui nod este egal ă cu lungimea drumului unic de
la rădăcină la acel nod.
În practică se mai defineşte şi lungimea drumului unui arbore,
numită şi lungimea drumului intern, ca fiind egală cu suma lungimilor
drumurilor corespunzătoare tuturor nodurilor arborelui. În mod
evident, lungimea medie a drumului notată cu Pi este:
1 h
Pi = ∑ ni ⋅ i
n i =1
unde n = numărul de noduri ale arborelui, n i = numărul de noduri de
la nivelul i şi h = nivelul maxim al arborelui.
În scopul definirii lungimii drumului extern, ori de câte ori se
întâlneşte în arborele iniŃial un subarbore vid, acesta se înlocuieşte
cu un nod special. În structura care se obŃine după această
activitate, toate nodurile originale vor fi de acelaşi grad şi anume
gradul arborelui. Se precizează că nodurile speciale care au înlocuit
ramurile absente nu au descendenŃi prin defini Ńie.
Lungimea drumului extern se defineşte ca fiind suma
lungimilor drumurilor la toate nodurile speciale. Dacă numărul
nodurilor speciale la nivelul i este si, atunci lungimea medie a
drumului extern PE este:
1 h +1
PE = ∑ si ⋅ i
m i =1
unde m=numărul de noduri speciale, h=înăl Ńimea arborelui.
B C
D E F G H
I J K L M N O P
Fig. 3.4
B C
D E F G H
Structuri de date liniare şi arborescente în C 97
-------------------------------------------------------------------------------------------------------------
I J K L M N O
P
Fig. 3.5
Numărul de noduri speciale care se adaugă într-un arbore de grad α
depinde în mod direct de numărul n de noduri originale, astfel:
a) Fiecare nod are exact o săgeată care intră în el; deci există
m+n-1 săgeŃi;
b) Pe de altă parte din fiecare nod original ies α săgeŃi.
Dar numărul de săgeŃi în cazurile a) şi b) este acelaşi ⇒ m+n-1=αn
⇒ m=n(α-1)+1.
Nici o săgeată nu iese dintr-un nod special.
Numărul maxim de noduri într-un arbore de o înălŃime dată h
şi grad α, este atins dacă toate nodurile au α subarbori cu excepŃia
celor de la nivelul h care nu au niciunul. Pentru arborele de grad α,
nivelul 0 va conŃine rădăcina, nivelul 1 va conŃine cei α descendenŃi
direcŃi ai săi, nivelul 2 va conŃine cei α 2 descendenŃi direcŃi ai
acestora, etc., ceea ce ne conduce la formula:
h
N α (h) = ∑α
i=0
i
N 2 (h ) = 2 h+1 − 1
De o importanŃă particulară sunt arborii ordonaŃi de gradul 2. Ei se
numesc arbori binari.
Exemple familiare de arbori sunt:
1) Arborii genealogici, cu cele 2 tipuri obişnuite:
a) Genealogia cu tatăl şi mama unei persoane ca descendenŃi;
b) DescendenŃa care îi indică pe urmaşi. (Dacă apar şi căsătorii
intrafamiliale arborele familial nu mai este de fapt un arbore,
deoarece ramuri diferite ale arborelui nu pot fi legate între
98 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
+ -
a / d *
b c e f
A
Structuri de date liniare şi arborescente în C 99
-------------------------------------------------------------------------------------------------------------
B C
H I D E F
G
Fig. 3.7
C
H I D F
B
E G
sau
2) Prin paranteze incluse , care indică şi ordonarea parŃială:
( A ( B ( H ) ( I ) ) ( C ( D ) ( E ( G ) ) ( F ) ) ) pentru arborele din
fig. 3.7.
3) Folosind “indentaŃia”.
Arborele din fig. 3.7 se va reprezenta prin indentare după cum
urmează:
A
B
100 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
H
I
C
D
E
G
F
A1 A2 ... Ak
Fig.3.8
1° Traversarea în preordine a arborelui A presupune traversarea
rădăcinii R urmată de traversarea în preordine a lui A1 , apoi de
traversarea în preordine a lui A2 şi aşa mai departe, până la Ak
inclusiv.
2° Traversarea în inordine presupune parcurgerea în inordine a
subarborelui A1 , urmată de nodul R şi în continuare de parcurgerile
în inordine ale subarborilor A2,...,Ak.
3° Traversarea în postordine a arborelui A constă în traversarea în
postordine a lui A1 , A2 ,..., Ak şi în final a nodului R.
Spre exemplu, pentru arborele din fig.3.9 traversările definite
anterior conduc la următoarele secvenŃe de noduri:
102 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
2 3 4
5 6 7
8 9 10
Fig.3.9
- preordine: 1 , 2 , 3 , 5 , 8 , 9 , 6 , 10 , 4 , 7
- postordine: 2 , 8 , 9 , 5 , 10 , 6 , 3 , 7 , 4 , 1
- inordine: 2 , 1 , 8 , 5 , 9 , 3 , 10 , 6 , 7 , 4
1
Structuri de date liniare şi arborescente în C 103
-------------------------------------------------------------------------------------------------------------
2 3 4
5 6 7
8 9 10
Fig.3.10
1° Preordine : *+a/bc-d*ef
2° Inordine : a+b/c*d-e*f
3° Postordine : abc/+def*-*
Ordonarea în postordine este cunoscută sub denumirea de
“notaŃie poloneză” (postfix).
+ -
a / d *
b c e f
2 3 4
5 6 7 8
Fig 3.12
l0 l1 l2 l3
1 3
2 2 3 1 4 1
5 0 6 0 7 0 8 0
Fig. 3.13
1 3
2 3 ∧ 3 3 ∧ ∧ 4 3 ∧ ∧
5 3 ∧ ∧ ∧ 6 3 ∧ ∧ ∧ 7 3 ∧ ∧ ∧ 8 3 ∧ ∧ ∧
Fig.3.14
Datorită faptului că în fiecare nod trebuie să rezervăm loc
pentru numărul maxim de referinŃe către fii (care poate să nu fie
cunoscut dinainte) risipa de memorie este uneori prea mare. Există
evident şi posibilităŃi de înlăturare a acestor neajunsuri, de exemplu
păstrând lista referinŃelor către fiii unui nod nu într-un vector (ce
corespunde alocării semistatice la nivel de nod), ci într-o listă
înlănŃuită (alocare dinamică).
Definim nodurile ca variabile cu o structură fixă. În Limbajul C
structura se descrie în modul următor (presupunem că informaŃia
ataşată nodurilor este de tip întreg şi coincide cu cheia de
identificare a nodului):
ref radacina;
În continuare, cele trei metode de traversare ale arborilor
binari vor fi concretizate în trei funcŃii recursive, în care r este o
variabilă de tip pointer (ref) care indică rădăcina arborelui, iar P
reprezintă operaŃia care trebuie executată asupra fiecărui nod (de
exemplu, operaŃia de tipărire).
Considerăm structura de arbore definită mai sus.
void Preordine(ref r)
{
if (r!=NULL)
{
P(r);
Preordine(r->st);
Preordine(r->dr);
}/* Preordine */
}
108 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
void Inordine(ref r)
{
if (r!=NULL)
{
Inordine(r->dr);
P(r);
Inordine(r->st);
}/* inordine */
}
void Postordine(ref r)
{
if (r!=NULL)
{
Postordine(r->st);
Postordine(r->dr);
P(r);
}/* Postordine */
}
n=5 n=6
Fig.3.15
Regula după care se asigură distribuŃia egală pentru un anumit
număr cunoscut de N noduri, poate fi formulată simplu în termeni
recursivi:
1° Se ia un nod drept rădăcină;
2° Se generează subarborele stâng cu NS=N/2 noduri;
3° Se generează subarborele drept cu ND=N -NS -1 noduri.
Aceşti arbori se numesc arbori perfect (total) echilibraŃi. Un arbore
este perfect echilibrat dacă are toate vârfurile terminale pe ultimele
două niveluri, astfel încât pentru orice nod, numărul nodurilor din
subarborele stâng să difere de numărul nodurilor din subarborele
drept cu cel mult unu.
Considerând variabilele:
ref radacina;
int N ;
construim pentru introducerea (generarea) arborelui funcŃia
Arbore(N) care generează un arbore perfect echilibrat cu N noduri:
ref Arbore(int N)
{
ref nodnou;
int ND,NS;
110 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
2 3
4 5 6 7
Structuri de date liniare şi arborescente în C 111
-------------------------------------------------------------------------------------------------------------
8 9 10 11 12 13 14 15
Fig.3.16
Tipărindu-l cu indentare îl vom obŃine, în funcŃie de modul de
traversare, sub una din următoarele trei forme(fig.3.17 a,b,c):
1 8 8
2 4 9
4 9 4
8 2 10
9 10 11
5 5 5
10 11 2
11 1 12
3 12 13
6 6 6
12 13 14
13 3 15
7 14 7
14 7 3
15 15 1
a) Preordine b) Inordine c) Postordine
Fig.3.17
Rotind cu 90° în sensul acelor ceasornicului imaginea din
figura 3.17b, se obŃine reprezentarea grafică a simetricului arborelui
binar (total echilibrat). Indentarea se face în funcŃie de nivelul
nodului. În acest sens (pentru tipărirea unui nod de un anumit nivel)
vom folosi un ciclu:
for(i=1;i<=nivel;i++)
printf(" ");
112 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
}
} /* Inordine */
5 7
3 8 2 8
2 4 7 9 1 6 9
1 6 3
4
a) înălŃime 3 b) înăl Ńime 5
Fie t un pointer care indică rădăcina unui arbore binar ordonat ale
cărui noduri au structura:
if (x<t->cheie)
t=t->st;
else t=t->dr;
return t;
}
3 8
2 4 7 9
1 6
118 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
fanion
3 8
2 4 7 9
120 Structuri de date liniare şi arborescente în C
-------------------------------------------------------------------------------------------------------------
1 6 Fig.3.20 a)
3 8
2 4 7 9
1 6 8
FuncŃia care realizează inserarea unui nod nou într-un arbore binar
ordonat, cu această implementare, va fi:
Structuri de date liniare şi arborescente în C 121
-------------------------------------------------------------------------------------------------------------
}
}/* inordine */
radacina=NULL;
printf("\nIntroduceti primul cuvant: ");
fflush(stdin);scanf("%s",&cuv);
while (strcmp(cuv,"*")!=0)
{
cauta(cuv,&radacina);
printf("\nIntroduceti urmatorul cuvant: ");
fflush(stdin);scanf("%s",&cuv);
}
a) x b) x c) x
P=NULL
Fig.3.21
Fragmentul de program care apare în continuare realizează acest
lucru:
q=p;
if(q->dr==NULL)
p=q->st; /* nodul nu are fiu drept*/
else
if(q->st==NULL)
p=q->dr; /* nodul nu are fiu sting*/
else /* nodul are doi fii */
2) Cel de-al doilea caz, în care nodul cu cheia x are doi fii se
rezolvă astfel:
Structuri de date liniare şi arborescente în C 125
-------------------------------------------------------------------------------------------------------------
p p
q x y
q y q y r
Fig.3. 22
if(x<(*p)->cheie)
suprimare(x,&((*p)->st));
else
if(x>(*p)->cheie)
suprimare(x,&((*p)->dr));
else /* suprim nodul *p */
{
q=*p;
if(q->dr==NULL)
(*p)=q->st;
else
if(q->st==NULL)
(*p)=q->dr;
else /* nodul are doi fii */
suprimafd(&((*p)->st));
free(q);
}
}/* suprimare */
BIBLIOGRAFIE
2. ***
BORLAND INTERNATIONAL TURBO C++: Programer's Guide
1990
3. Frederic Lung
Le Langage C++
CNIT Paris - La Defense
1990
6. Adamson I. T.
Data Structures and Algorithms: A First Course
Springer-Verlag London Limited
1996
CUPRINS
Structuri de date liniare şi arborescente în C 129
-------------------------------------------------------------------------------------------------------------
BIBLIOGRAFIE………………………………………………………..267