Professional Documents
Culture Documents
Noţiuni introductive
Există mai multe moduri echivalente de definire a arborilor. Din
punctul de vedere al teoriei grafurilor numim arbore un graf neorientat
conex şi fără cicluri. Dacă graful este aciclic, dar nu este conex îl vom
numi pădure.
De exemplu,
Fig. 1.
a. este arbore, b. este pădure, nefiind conex, iar c. nu este nici arbore,
nici pădure, deoarece conţine cicluri.
Teorema 1
Fie G = (V, U) un graf neorientat. Următoarele afirmaţii sunt
echivalente:
1) G este arbore.
2) Oricare două vârfuri din G sunt unite printr-un lanţ simplu
unic.
3) G este conex minimal (dacă suprimăm o muchie, graful
obţinut este neconex).
4) G este conex şi are V-1 muchii.
5) G este aciclic şi are V-1 muchii.
6) G este aciclic maximal (dacă adăugăm o muchie, graful
obţinut conţine cicluri).
Demonstraţie:
1 ⇒2
Dacă G este arbore, atunci G este conex, deci oricare ar fi două
vârfuri din graf, acestea sunt unite prin cel puţin un lanţ simplu.
Presupunem prin reducere la absurd că există x şi y două vârfuri unite
prin două lanţuri simple distincte l1 şi l2 .
5
Fig. 2.
Fie z primul vârf de la care cele două lanţuri se despart, iar t
primul vârf în care cele două lanţuri se întâlnesc din nou. Dacă notăm
l'1 porţiunea de pe lanţul l1 între z şi t, iar cu l'2 porţiunea de pe lanţul l2
între z şi t, atunci l'1 şi l'2 nu au vârfuri comune, cu excepţia vârfurilor z
şi t. Concatenând l'1 şi l'2, obţinem un ciclu- contradicţie cu ipoteza că
G este arbore. Deci, oricare două vârfuri din graf sunt unite printr-un
lanţ simplu unic.
2 ⇒3
Dacă oricare două vârfuri x, y∈V sunt unite printr-un lanţ
simplu unic, atunci orice muchie [x, y]∈U reprezintă unicul lanţ dintre
x şi y. Suprimând muchia [x, y], între x şi y nu va mai exista lanţ, deci
graful obţinut nu va mai fi conex.
3 ⇒4
Notăm cu n numărul de vârfuri şi cu m numărul de muchii din
graf.
Pentru a demonstra că orice graf conex minimal are n-1 muchii
vom demonstra prin inducţie completă după n că m ≤ n-1. Cum în
orice graf conex m ≥ n-1, deducem m = n-1.
P(1) Dacă n = 1, atunci m = 0 ⇒ m = n-1.
P(2) Dacă n = 2, atunci m = 1 ⇒ m = n-1.
P(n) Presupunem că într-un graf conex minimal cu cel
mult n vârfuri numărul de muchii este strict mai mic decât numărul de
vârfuri.
P(n+1) Demonstrăm că într-un graf conex minimal cu
n+1 vârfuri, numărul de muchii este cel mult egal cu n.
Fie G conex minimal cu n+1 vârfuri şi m muchii. Eliminând o
muchie oarecare din graf obţinem un graf G' cu m-1 muchii şi două
componente conexe C1 şi C2 cu n1, respectiv n2 vârfuri (n1+n2 = n+1) şi
m1, respectiv m2 muchii (m1+m2 = m-1). Subgrafurile C1 şi C2 sunt
conexe minimale, altfel graful G nu ar fi conex minimal. Din ipoteza
inductivă rezultă că m1 ≤ n1-1, m2 ≤ n2-1; dar m1+m2 = m-1 ≤ n1+n2 =
n-2 ⇒ m ≤ n-1. Deci G conex minimal implică G conex cu n-1 muchii.
4 ⇒5
Fie G un graf conex cu n-1 muchii. Să demonstrăm că G este
6
aciclic.
Presupunem prin reducere la absurd, că graful G conţine un
ciclu C format din vârfurile v1, v2, ..., vk.
Să considerăm subgraful parţial Gk = (Vk, Uk) constând din
ciclul C. Deci Vk = {v1, v2 ,..., vk}, iar Uk = {[v1,v2)], [v2,v3],...,[vk-1,vk],
(vk,v1]} (Vk=Uk= k). Dacă Vk<V, atunci ∃ vi∈Vk şi vk+1∈V-Vk
astfel încât [vi, vk+1]∈U, graful G fiind conex.
Construim Gk+1 = (Vk+1, Uk+1) astfel :Vk+1 = Vk ∪{vk+1};
Uk+1= Uk∪{[vi,vk+1]} şi Uk+1=Vk+1=k+1.
Cât timp k+1 < n, aplicăm acelaşi procedeu până când obţinem
un graf Gn = (V, Un), cu Un= n, Un ⊆ U; deci U ≥ n, contradicţie
cu ipoteza U= n-1.
5 ⇒6
Presupunem că graful G este aciclic cu n-1 muchii, să
demonstrăm că G este aciclic maximal.
Fie C1, C2,..., Cp cele p componentele conexe ale grafului G,
având respectiv n1, n2,..., np vârfuri şi m1, m2,..., mp muchii fiecare.
Evident că n1+n2+...+np = n şi m1+m2+...+mp = n-1.
Cum graful G este aciclic, deducem că fiecare componentă conexă este
un arbore. Deoarece am demonstrat că 1 ⇒ 5, rezultă că m i= ni-1,
∀i∈{1, 2, ..., p}. Înlocuind în relaţia de mai sus, obţinem n-p = n-1 ⇒
p = 1, deci G conex. Dacă G este conex şi aciclic, conform definiţiei G
este arbore. Dar am demonstrat că 1 ⇒ 2, deci oricare două vârfuri din
G sunt unite printr-un lanţ simplu. Astfel, adăugând orice muchie
obţinem un ciclu.
6 ⇒1
Presupunem că graful G este aciclic, dar dacă am mai adăuga o
muchie s-ar obţine un ciclu. Să demonstrăm că G este conex.
Fie u şi v două vârfuri neadiacente din graf, arbitrar alese.
Deoarece adăugând muchia [u, v] se obţine un ciclu, rezultă că u şi v
sunt unite printr-un lanţ ale cărui muchii aparţin grafului G. Cum u,v au
fost alese arbitrar, deducem că graful G este conex.
Q.E.D.
Teorema 2
Numerele întregi 0 < d1 ≤ d2 ≤ ...≤ dn (n ≥ 2) sunt gradele
vârfurilor unui arbore dacă şi numai dacă d1+d2+...+dn = 2n-2.
Demonstraţie:
Necesitatea Condiţia este necesară, deoarece orice arbore cu n vârfuri
are n-1 muchii, iar suma gradelor vârfurilor oricărui graf este de două
ori numărul de muchii. Deci d1+d2+...+dn = 2n-2.
7
Suficienţa Fie 0 < d1 ≤ d2 ≤ ...≤ dn astfel încât d1+d2+...+dn = 2n-2.
Să demonstrăm că există un arbore cu gradele vârfurilor d1, d2,..., dn.
Vom proceda prin inducţie.
P(2) Dacă d1+d2 = 2, atunci d1 = d2 = 1 , arborele fiind cel
din figura de mai jos :
Fig. 3
P(n) Presupunem acum că proprietatea este adevărată pentru
orice secvenţă de n numere naturale 0 < d1 ≤ d2 ≤ ...≤ dn, astfel încât
d1+d2+...+dn = 2n-2.
P(n+1) Să demonstrăm că pentru orice secvenţă 0 < d'1 ≤ d'2
≤ ...≤ d'n ≤ d'n+1 astfel încât d'1+d'2+...+d'n+1 = 2n, există un arbore cu
n+1 vârfuri cu secvenţa gradelor d'1, d'2, ..., d'n+1.
Observăm că există măcar un nod terminal x1 cu gradul d'1= 1,
altfel dacă di ≥ 2,∀i∈{1, 2,..., n+1} ⇒ d'1+d'2+...+d'n+1 ≥ 2(n+1), ceea
ce contrazice ipoteza. În mod analog, observăm că există măcar un nod
neterminal xn+1, cu gradul d'n+1 > 1, altfel dacă d'i = 1,∀i∈{1, 2, ..., n+1}
⇒ d'1+d'2+...+d'n+1 = n+1 < 2n .
Să considerăm următoarea secvenţă de n numere întregi d'2,...,
d'n, d'n+1-1 cu proprietatea că d'2+...+d'n+d'n+1 = 2n-2. Din ipoteza
inductivă există un arbore An cu n vârfuri şi secvenţa gradelor d'2,..., d'n,
d'n+1-1. Adăugăm la arborele An un vârf pe care îl unim printr-o muchie
cu vârful având gradul d'n+1-1. Obţinem un arbore An+1 cu gradele
vârfurilor d'1, d'2,..., d'n+1.
Q.E.D.
8
//vectorul d, matricea a şi n, numărul de vârfuri, sunt variabile globale
var NrMuchiiSelectate, VfTerminal, VfNeterminal: Integer;
begin
NrMuchiiSelectate := 0;
VfTerminal := 1;
while NrMuchiiSelectate < n-1 do
begin //selectez o muchie în arbore
VfNeterminal := VfTerminal + 1;
//caut un vârf cu grad mai mare decât 1
while d[VfNeterminal] = 1 do inc(VfNeterminal);
inc(NrMuchiiSelectate);
//selectez muchia [VfTerminal,VfNeterminal]
a[1,NrMuchiiSelectate] := VfTerminal;
a[2,NrMuchiiSelectate] := VfNeterminal;
dec(d[VfNeterminal]);
inc(VfTerminal);
end;
end;
Fig. 4
Acest procedeu sugerează o metodă foarte eficientă de
reprezentare a arborilor. Dacă An este un arbore cu vârfurile x1, x2,.., xn,
suprimăm vârful terminal cu cel mai mic indice şi muchia incidentă cu
acesta şi reţinem a1, vârful adiacent cu vârful terminal suprimat. Am
obţinut astfel un subgraf cu n-2 muchii conex şi aciclic, deci un arbore
An-1. Repetăm procedeul pentru arborele An-1, determinând un al doilea
vârf, a2, adiacent cu vârful terminal de indice minim ce va fi eliminat
din An-1 împreună cu muchia incidentă cu el ş.a.m.d., până când se
obţine un arbore A2 cu două vârfuri adiacente.
9
Am obţinut astfel un sistem {a1, a2,.., an-2} de n-2 numere
1≤ ai≤ n, ∀i∈{1,2,...,n-2} asociat arborelui An numit codul Prüffer al lui
An .
Pentru arborele din figura 4, codul Prüffer este
a={ 8,9,9,10,10,10,11,11,11}.
Propoziţie
Există o corespondenţă biunivocă între mulţimea arborilor A cu
n vârfuri şi mulţimea sistemelor {a1, a2, ..., an-2}, ai∈{1,2,...,n},
∀i∈{1,2,...,n-2} .
Demonstraţie:
Injectivitatea
Fie a={ a1, a2, ..., an-2}, 1 ≤ ai ≤ n, ∀i∈{1,2,...,n-2}, codul Prüffer
al unui arbore A. Completăm şirul cu an-1 = n, deci a = {a1,a2,...,an-2,an-
1}.
Să notăm b = (b1, b2, ..., bn-2, bn-1) indicii vârfurilor în ordinea
în care au fost eliminate. Observăm că :
1. bi ≠ bj, ∀i ≠ j (deoarece un nod nu poate fi eliminat decât o
dată);
2. bi ≠ ai (pentru că există muchia [ai, bi]);
3. bi ≠ aj, ∀j > i (bi fiind eliminat la pasul i, nu mai poate fi
părintele unui alt vârf eliminat ulterior);
4. ∀ k ∈{b1, b2, ..., bi-1, ai, ai+1, ..., an-1}, k este un vârf terminal al
arborelui A-{b1, b2, ..., bi-1}, altfel k ar fi adiacent cu un vârf ce va fi
suprimat ulterior, deci k = aj, cu j > i- contradicţie (un vârf care nu a
fost eliminat şi care nu este părinte pentru un alt vârf, este vârf
terminal).
5. bi, din modul de definire a codului Prüffer va fi :
bi = min{ k k∉{b1, b2, ..., bi-1, ai, ai+1, ..., an-1}} (*)
Deci din codul Prüffer am putut determina în mod unic ordinea
de eliminare a vârfurilor şi implicit, muchiile arborelui
A={[ai, bi], i ∈{1, 2, ..., n-1}}
Surjectivitatea
Demonstrăm că ∀a = {a1, a2, ..., an-2}, 1 ≤ ai ≤ n, ∀i∈{1,2,...,n-
2}, există un arbore A cu n vârfuri astfel încât codul Prüffer al lui A să
fie şirul a.
Completăm a cu an-1 = n şi definim bi conform relaţiei (*),
∀i∈{1, 2, ..., n-1}. Construim A, graful cu muchiile U = {[ai, bi],
∀i∈{1, 2, ..., n-1}}. Vom arăta că A este arbore şi codul Prüffer al lui A
este {a1, a2, ..., an-2}.
10
Notăm Ui = U-{[b1, a1],...,[bi-1, ai-1]}, ∀i∈{1,2,...,n-1},
Vi = {1,2,...,n}-{b1, b2, ..., bi-1}.
Evident A = A1, iar An este format numai din vârful n, deci An este
arbore.
Să demonstrăm că bi este vârf terminal în Ai.
bi = min{ k k∉{b1, b2, ..., bi-1, ai, ai+1, ..., an-1}}
Dar ai∈Ai, pentru că din relaţia (*) ai ≠ bj,∀j < i, iar bi nu poate
fi adiacent în Ai decât cu ai, deoarece, dacă bi ar fi incident cu o altă
muchie [bj, aj], j > i din Ai ar însemna că bi = bj sau aj = bi, cu j > i ceea
ce este în contradicţie cu definiţia lui bi.
Ai-1 se obţine din Ai eliminând vârful terminal bi şi muchia [bi,
ai], incidentă cu aceasta. Procedând inductiv, cum An este arbore,
deducem că Ai, i∈{1,...,n} sunt arbori, în particular A este arbore.
Tot din definirea lui bi rezultă că bi este vârful terminal de indice
minim, deci, conform definiţiei, {a1, a2, ..., ai-2} va fi codul Prüffer al
arborelui Ai. În particular deducem că {a1 ,a2, ..., an-2} este codul Prüffer
al arborelui A.
Q.E.D.
Să considerăm, de exemplu, n = 11 şi a = {1,2,2,2,1,4,4,1,5}.
Pentru a determina muchiile arborelui cu acest cod Prüffer, completăm
codul cu a10= 11. Deci a={ 1,2,2,2,1,4,4,1,5,11} şi determinăm :
b1 = min { kk∉{1,2,2,2,1,4,4,1,5,11}}=3
b2 = min { kk∉{3,2,2,2,1,4,4,1,5,11}}=6
b3 = min { kk∉{3,6,2,2,1,4,4,1,5,11}}=7
b4 = min { kk∉{3,6,7,2,1,4,4,1,5,11}}=8
b5 = min { kk∉{3,6,7,8,1,4,4,1,5,11}}=2
b6 = min { kk∉{3,6,7,8,2,4,4,1,5,11}}=9
b7 = min { kk∉{3,6,7,8,2,9,4,1,5,11}}=10
b8 = min { kk∉{3,6,7,8,2,9,10,1,5,11}}=4
b9 = min { kk∉{3,6,7,8,2,9,10,4,5,11}}=1
b10 = min { kk∉{3,6,7,8,2,9,10,4,1,11}}=5
Muchiile arborelui sunt : [1,3], [2,6], [2,7], [2,8], [1,2], [4,9], [4,10],
[1,4], [5,1], [5,11].
Fig. 5
11
Folosind acest rezultat, deducem că numărul arborilor ce se pot
construi cu n vârfuri date este egal cu nn-2, numărul funcţiilor bijective
definite pe o mulţime cu n-2 elemente, cu valori într-o mulţime cu n
elemente. Această formulă poartă numele de formula lui Cayley.
Aplicaţie*
Generaţi toţi arborii cu n vârfuri.
Problema se reduce la generarea tuturor funcţiilor
a:{1,2,...,n-2} → {1,2,...,n}
şi determinarea arborelui corespunzător fiecărei funcţii.
Vom genera toate codurile Prüffer posibile recursiv, apelând procedura
generare(1).
procedure generare(i:byte);
//poziţiile 1,2,...,i-1 din vectorul global a sunt fixate;
// completăm poziţiile i,...,n-2.
begin
if i = n-1 then // codul este complet
determina_arbore
else
for j := 1 to n do
begin
a[i] := j;
generare(i + 1);
end;
end;
procedure determina_arbore;
// afişează muchiile arborelui;
var k, i: byte;
MB: set of byte;
//mulţimea vârfurilor care au fost deja eliminate din arbore
MA: set of byte;
//mulţimea vârfurilor pentru care trebuie să determinăm nodul
// terminal adiacent
begin
MB := []; MA := [a1, a2, ..., an-1];
for i := 1 to n - 1 do //determin cele n-1 muchii ale arborelui
begin
*
Programul Generare-Arbori-cu-n-Varfuri de la sfârşitul capitolului curent
generează toţi arborii cu n vârfuri date.
12
//calculez k, vârful de indice minim ce ∉ MA∪ MB
k := 1;
while k in MA + MB do inc(k);
MB := MB + [k];
// k este nodul terminal de indice minim căutat
MA := MA - [ai];
write(k, ai,); //afişez muchia [k,ai]
end;
end;
Fig.6.
Definiţia este recursivă, orice nod al unui arbore fiind rădăcina
unui subarbore.
Să observăm că alegând într-un mod arbitrar un vârf drept
rădăcină, orice graf neorientat conex şi fără cicluri este un arbore cu
rădăcină în sensul definiţiei de mai sus. Arborii A1, A2, ..., An se numesc
subarborii rădăcinii, numărul de subarbori nevizi ai unui nod fiind
13
numit gradul nodului respectiv.
De exemplu,
Fig. 7.
Să observăm că definiţia conduce la o ierarhizare a nodurilor
arborelui :
- considerăm că rădăcina r se situează pe nivelul 0.
- dacă notăm cu r1, r2, ..., rn respectiv rădăcinile arborilor A1, A2,
..., An, nodurile r1, r2, ..., rn vor constitui nivelul 1 în arbore, ş.a.m.d.
Nodurile r1, r2, ..., rn, se numesc fiii nodului rădăcină, iar
rădăcina r reprezintă părintele nodurilor r1, r2, ..., rn, rădăcina fiind
singurul nod din arbore care nu are părinte. Fiecărei muchii din arbore
îi putem asocia o orientare de la părinte spre fiu. În plus, fiii nodurilor
de pe nivelul i≥ 0, vor constitui nivelul i+1.
Nivelul maxim din arbore va constitui înălţimea (adâncimea)
arborelui respectiv. Să observăm că orice nod x poate fi atins din
rădăcină pe un drum unic. Orice nod y care se găseşte pe drumul unic
de la r la x se numeşte ascendent (strămoş) al lui x. Dacă y este un
ascendent al lui x, atunci x se numeşte descendent al lui y. Mai exact,
toţi descendenţii unui nod x sunt nodurile din subarborele cu rădăcina x.
Dacă un nod nu are descendenţi el se numeşte nod terminal sau frunză.
Două noduri care au acelaşi părinte se numesc fraţi.
În exemplul de mai sus, 4 este un ascendent al lui 8. Nodurile 5,
6, 3, 8, 9 sunt noduri terminale. Nodurile 8, 9 sunt fraţi, iar descendenţii
nodului 4 sunt nodurile 7, 8, 9.
14
dintr-un nod rădăcină şi doi arbori binari disjuncţi numiţi subarborele
stâng, respectiv subarborele.
Fig. 8.
A1 = subarbore stâng; A2 = subarbore drept.
Se face o distincţie clară între subarborele drept şi cel stâng.
Dacă subarborele stâng este nevid, rădăcina lui se numeşte fiul stâng al
rădăcinii. Analog, dacă subarborele drept este nevid, rădăcina lui se
numeşte fiul drept al rădăcinii.
De exemplu,
Fig. 9.
sunt doi arbori binari distincţi.
În continuare, terminologia folosită la arbori cu rădăcină se va
aplica şi la arbori binari. Vom deosebi între arborii binari câteva clase
speciale:
1. Arbori binari stricţi sunt arborii binari în care orice vârf are
gradul zero (este terminal ) sau doi (are exact doi fii).
De exemplu, arborii din figura 9 nu sunt arbori binari stricţi (nodurile 2
şi 6 având un singur fiu), dar
Fig. 10.
este un arbore binar strict.
2. Arbori binari plini sunt arbori binari care au 2k-1 vârfuri
15
dispuse pe nivelurile 0, 1, ... , k-1, astfel încât pe fiecare nivel i se
găsesc 2i vârfuri.
De exemplu, arborele binar plin cu înălţimea 2 este:
Fig. 11.
3. Arborii binari compleţi sunt arbori binari care se obţin
dintr-un arbore binar plin prin eliminarea din dreapta către stânga a
unor noduri de pe ultimul nivel. Mai exact, pentru a construi un arbore
binar complet cu n noduri, determinăm k astfel încât
2k ≤ n < 2k+1 ⇔ k = [log2n].
Construim arborele binar plin cu 2k+1-1 noduri şi eliminăm de pe
ultimul nivel nodurile 2k+1-1, 2k+1-2, ..., n+1.
De exemplu, arborele binar complet cu 5 vârfuri se obţine prin
eliminarea vârfurilor 7 şi 6 din arborele binar plin cu înălţimea 2 :
Fig. 12.
4. Arbori binari degeneraţi- sunt arbori binari cu n vârfuri
dispuse pe n niveluri.
De exemplu,
Fig. 13.
5. Arbori binari echilibraţi - sunt arbori binari în care, pentru
orice nod, numărul nodurilor din subarborele drept şi numărul nodurilor
din subarborele stâng diferă cu cel mult o unitate.
De exemplu,
16
Fig. 14.
Proprietatea 1.
Numărul maxim de noduri de pe nivelul i al unui arbore binar este 2i.
Demonstraţie:
Vom proceda prin inducţie după numărul nivelului.
P(0) Pe nivelul i = 0 se găseşte un singur nod (rădăcina).
P(k) Presupunem că numărul maxim de noduri de pe nivelul k este
2k .
P(k+1) Vom demonstra că pe nivelul k+1 sunt cel mult 2k+1 noduri.
Pe nivelul k+1 se găsesc fiii nodurilor de pe nivelul k. Din ipoteza
inductivă, pe nivelul k se găsesc cel mult 2k noduri, iar fiecare nod
poate avea cel mult doi fii, deci pe nivelul k+1 se găsesc cel mult 2*2k
= 2k+1 noduri.
Q.E.D.
Proprietatea 2.
Numărul maxim de noduri într-un arbore cu înălţimea h este 2h+1-1.
Demonstraţie:
Numărul maxim de noduri într-un arbore cu înălţimea h se
obţine atunci când fiecare nivel i este plin, deci, conform propoziţiei
anterioare, conţine 2i noduri. Numărul maxim de noduri într-un arbore
cu înălţimea h va fi:
h
i h+1
∑2 = 2 −1
i=0
Q.E.D.
Proprietatea 3.
În orice arbore binar nevid cu n0 noduri terminale există n0-1 noduri de
grad 2.
Demonstraţie:
Notăm cu n0 numărul de noduri terminale, cu n1 numărul de
noduri de grad 1 şi cu n2 numărul de noduri de grad 2. Deci, numărul
total de noduri n= n0+n1+n2.
17
Dacă numărăm muchiile dintr-un arbore binar, observăm că
fiecare nod, cu excepţia rădăcinii, are o singură muchie orientată spre
el. Notând m numărul de muchii obţinem n = m+1. Dar orice muchie
provine de la un nod de grad 1 sau 2, rezultă că m = n1+2n2.
Din n0+n1+n2 = n şi n1+2n2 = n-1 ⇒ n2 = n0-1.
Q.E.D.
Proprietatea 4.
Un arbore cu n vârfuri are înălţimea cel puţin egală cu [log2n].
Demonstraţie:
În cazul cel mai favorabil, nodurile sunt dispuse pe niveluri
astfel încât fiecare nivel să fie plin, cu excepţia, eventuală, a ultimului
nivel. Deci arborele binar cu n noduri de înălţime minimă este arborele
binar complet cu n vârfuri, care, din modul de construcţie, are înălţimea
[log2n].
Q.E.D.
Proprietatea 5.
Definim lungimea drumurilor interne (I) ca fiind suma lungimilor
drumurilor de la rădăcină la noduri neterminale (interne) şi lungimea
drumurilor externe (E) ca fiind suma lungimilor drumurilor de la
rădăcină la noduri terminale (frunză sau externe). Într-un arbore binar
cu n noduri interne, E = I+2n.
Demonstraţie:
Vom proceda prin inducţie după n, numărul nodurilor interne.
P(0) Într-un arbore cu 0 noduri interne (vid sau format numai din
rădăcină) E = I = 0.
P(n-1) Presupunem că într-un arbore binar An-1, cu n-1 noduri interne,
are loc relaţia En-1 = In-1+2(n-1).
P(n) Vom demonstra că într-un arbore binar An, cu n noduri interne,
are loc relaţia En = In+2n.
Fie An un arbore binar cu n noduri interne. Există în An un nod
intern x care are drept fii două noduri terminale. Îndepărtând din An fiii
nodului x, nodul x se transformă în nod terminal, deci obţinem un
arbore An-1 cu n-1 noduri interne. Din propoziţia inductivă rezultă că în
arborele A n-1, En-1 = In-1+2(n-1). Dacă notăm cu d, lungimea drumului
de la rădăcină la nodurile eliminate, obţinem relaţiile :
En = En-1+2d-(d-1) (în An nodurile eliminate sunt terminale, dar nodul x
nu, lungimea drumului de la rădăcină la x fiind d-1).
In = In-1+(d-1) (în An nodul x este intern).
Deci En = In-1+2(n-1)+d+1 = In-d+1+2n-2+d+1 = In+2n.
Q.E.D.
18
1.4. Reprezentarea arborilor
19
Fig. 15.
va fi reprezentat prin :
Fig.16.
Dacă arborele este reprezentat prin referinţe descendente, atunci
este suficient să reţinem rădăcina arborelui pentru a avea acces la toate
nodurile acestuia.
20
O astfel de reprezentare este utilă pentru reprezentarea
mulţimilor disjuncte cu ajutorul arborilor şi o rezolvare eficientă a
problemelor de reuniune a două mulţimi şi de determinare a mulţimii
căreia îi aparţine un element dat.
Fig. 17.
Rotind această reprezentare cu 45° în sensul acelor de ceasornic,
obţinem un arbore binar în care pentru fiecare nod, fiul drept este
fratele lui din dreapta cel mai apropiat. Între cele două grafuri există o
corespondenţă biunivocă. Figura de mai jos ilustrează aceasta
transformare.
Fig. 18.
21
1.5.1. Reprezentarea înlănţuită.
În această reprezentare, pentru fiecare nod reţinem, pe lângă
informaţia asociată nodului, rădăcina subarborelui stâng, rădăcina
subarborelui drept şi dacă este necesar, părintele nodului respectiv.
ArboreBinar = ^NodArboreBinar;
NodArboreBinar = record
c: TipInformaţie;
st, dr, părinte: ArboreBinar;
end;
Arborele binar va fi referit prin intermediul rădăcinii.
22
P(x+1) Demonstrăm relaţiile 1. şi 2. pentru x+1, adică
{
st( x + 1) = 2x+2, dacă 2x+2≤ n
nu există, dacă 2x+2>n şi
dr ( x + 1) = {
2x+3,
nu există,
dacă 2x+3≤ n
dacă 2x+3> n
Fiul stâng al nodului x+1 este precedat de fiul drept al nodului
x. Din ipoteza inductivă, dr(x) = 2x+1, deci st(x+1) = 2x+1+1 =
2x+2, evident, dacă 2x+2 ≤ n. Fiul drept al lui x+1 este succesorul lui
st(x+1), deci dr(x+1) = 2x+2+1 = 2x+3, dacă 2x+3 ≤ n.
Q.E.D.
Deci pentru arbori binari compleţi această reprezentare este
ideală, nefiind necesară decât memorarea informaţiilor nodurilor. Se
poate utiliza reprezentarea secvenţială şi pentru arbori binari oarecare,
completând arborele cu noduri fictive până la un arbore binar complet,
dar în acest caz o mare parte din vectorul ce conţine informaţiile
nodurilor rămâne neutilizată. În plus, ca orice reprezentare secvenţială,
este inadecvată pentru inserări şi ştergeri de noduri.
23
if n = 0 then //arborele este vid
CreareArboreBinarEchilibrat := nil
else
begin
new(rad) //aloc zonă de memorie pentru rădăcina arborelui
readln(rad^.Inf);//citesc informaţia rădăcinii arborelui
// creez subarborele stâng, apoi cel drept
rad^. St := CreareArboreBinarEchilibrat(n div 2);
rad^. Dr := CreareArboreBinarEchilibrat(n - n div 2
-1 );
CreareArboreBinarEchilibrat := rad
end
end;
24
procedure Inordine(rad: ArboreBinar);
begin
if rad ≠ nil then
begin
Inordine(rad^.st);
write(rad^.inf);
Inordine(rad^.dr);
end;
end;
Pentru a parcurge în postordine un arbore binar, se parcurge în
postordine subarborele stâng, apoi cel drept, apoi se vizitează rădăcina.
procedure Postordine(rad: ArboreBinar);
begin
if rad ≠ nil then
begin
Postordine(rad^.st);
Postordine(rad^.dr);
write(rad^.inf);
end;
end;
Pentru a înţelege mai bine operaţia de parcurgere, vom prezenta
şi o variantă iterativă de parcurgere în inordine a unui arbore binar.
Pentru a simula recursia vom folosi o stivă S la care vom adăuga sau
şterge elemente în acelaşi mod ca în procedura recursivă.
Stivă = ^NodStivă;
NodStivă = record
Inf: ArboreBinar;
Urm: Stivă;
end;
procedure InordineIterativ (rad: ArboreBinar);
// procedura parcurge iterativ în inordine arborele cu rădăcina rad
var S, p: Stivă;
NodCurent: ArboreBinar;
begin
S := nil;
NodCurent := rad;
repeat
while NodCurent ≠ nil do
begin
//adaugă nodul curent în stiva S
new(p)
25
p^.Inf := NodCurent;
p^.Urm := S;
S := p;
//rădăcina subarborelui stâng devine nod curent
NodCurent := NodCurent^.St
if S ≠ nil then //extrage un element din stivă
begin
p := S;
S := S^.Urm;
write(p^.inf)
NodCurent := p^.Inf^.Dr
dispose(p);
//eliberează zona de memorie a lui p
until (S = nil) and (NodCurent = nil)
end;
Observaţie
Fiecare nod din arbore este plasat şi şters din stivă o singură
dată, deci timpul necesar parcurgerii inordine este de O(n). Spaţiul
suplimentar necesar depinde de înălţimea arborelui, deci în cazul cel
mai defavorabil este de O(n).
b) Parcurgerea pe niveluri
Se vizitează întâi rădăcina, apoi fiul stâng al rădăcinii, apoi cel
drept şi se continuă în acest mod vizitând nodurile de pe fiecare nivel de
la stânga la dreapta.
Pentru a realiza acest mod de parcurgere, vom utiliza o coadă,
pe care o iniţializăm cu rădăcina arborelui şi din care, la fiecare pas,
vom extrage un nod, îl vizităm şi inserăm în coadă fii săi, dacă aceştia
există.
Coadă = ^NodCoadă;
NodCoadă = record
Inf: ArboreBinar;
Urm: Coadă;
end;
procedure ParcurgerePeNiveluri (rad: ArboreBinar)
//procedura parcurge pe niveluri arborele cu rădăcina rad
var C, SfC, p: Coadă;
begin
if rad ≠ nil then //arborele este nevid
begin
new(C) // iniţializez coada cu rădăcina arborelui
26
C^.Inf := rad;
C^.Urm := nil;
SfC := C;
while C ≠ nil do // coada nu este vidă
begin
p := C;
write(p^.Inf);
if p^.Inf^.St ≠ nil then
begin
//adaug fiul stâng în coadă
new(q);
q^.Inf := p^.Inf^.St;
q^.Urm := nil;
SfC^.Urm := q;
SfC := q;
end;
if p^.Inf^.Dr ≠ nil then
begin
//adaug fiul drept în coadă
new(q);
q^.Inf := p^.Inf^.Dr;
q^.Urm := nil;
SfC^.Urm := q;
SfC := q;
end;
C := C^.Urm //extrag din coadă nodul p
dispose(p);
end
end
end;
Observaţie
Mai întâi am inserat în coadă fiii nodului ce urmează a fi vizitat
şi apoi am extras efectiv nodul respectiv din coadă, pentru a evita
inserarea unui nod într–o coadă vidă.
27
begin
if rad = nil then // arbore vid
Înălţime := –1
else
Înălţime := max(Înălţime(rad^.st),
Înălţime(rad^.dr))+ 1
end;
Am presupus cunoscută funcţia max, care întoarce cel mai mare
dintre cele două argumente ale sale.
Aplicaţie*
Se dau secvenţele obţinute prin parcurgerile în preordine şi în
inordine ale unui arbore binar. Construiţi arborele binar corespunzător.
De exemplu, fie A,B,C,D,E,F,G,H- parcurgerea în preordine şi
C,B,A,E,D,G,F,H- parcurgerea în inordine.
Analizînd parcurgerea în preordine, deducem că nodul A este
rădăcina. De asemeni, analizând parcurgerea în inordine, deducem că
nodurile C, B vor constitui arborele stâng, iar D, E, G, F, H subarborele
drept. Pentru a determina structura subarborelui stâng, respectiv a
subarborelui drept, procedăm analog: din parcurgerea în preordine
deducem că B este rădăcina subarborelui stâng, iar din parcurgerea în
inordine deducem că C este fiul stâng al lui B. În mod similar, pentru
subarborele drept deducem din parcurgerea în preordine că D este
rădăcină, iar din parcurgerea în inordine că subarborele stâng al lui D
este format numai din nodul E, iar subarborele drept al lui D din
*
Programul Construcţie-Arbore-Binar-cu-Secvenţele-Preordine-Inordine-Date
generează un arbore binar pentru care se cunosc parcurgerile în preordine şi inordine.
28
nodurile F, G, H. Procedeul se repetă pînă cînd obţinem întreg arborele.
Succesiunea operaţiilor este ilustrată în figura 19:
Fig. 19.
Propoziţie
Succesiunile de noduri obţinute prin parcurgerile în inordine şi
în preordine ale unui arbore binar definesc în mod unic structura
arborelui.
Demonstraţie:
Vom proceda prin inducţie completă după numărul de noduri.
P(1) Dacă arborele are un singur nod, rădăcina, afirmaţia este
evidentă.
P(n) Presupunem că pentru ∀k∈{1,2,... ,n} afirmaţia este
adevărată, adică pentru orice pereche de secvenţe inordine–preordine de
lungime k, arborele binar corespunzător este unic.
P(n+1) Să demonstrăm că orice pereche de secvenţe preordine-
inordine de lungime n+1 determină în mod unic un arbore binar.
Să considerăm o pereche de secvenţe preordine-inordine de
lungime n+1. Primul nod din parcurgerea în preordine este în mod
necesar rădăcina arborelui, celelalte n noduri fiind distribuite în
subarbori: toate nodurile situate în stânga rădăcinii în parcurgerea în
inordine vor constitui subarborele stâng, nodurile situate în dreapta
rădăcinii în parcurgerea inordine vor constitui subarborele drept.
Obţinem două perechi de secvenţe preordine-inordine de
lungime cel mult n, care din ipoteza inductivă, determină în mod unic
29
subarborele stâng, respectiv cel drept şi în consecinţă, cum rădăcina este
în mod unic determinată, deducem că perechea de secvenţe preordine-
inordine de lungime n+1 determină în mod unic un arbore binar cu n
noduri.
Q.E.D.
Vom descrie o funcţie recursivă ConstrArb, care determină
arborele binar corespunzător unei perechi de secvenţe preordine-
inordine date. Pentru simplificare, vom considera că nodurile arborelui
sunt numerotate în preordine de la 1 la n. Astfel, este suficient să
reţinem într-un vector global i indicii vârfurilor în ordinea în care au
fost atinse în inordine. Iniţial, apelăm ConstrArb(1,1,n).
function ConstrArb (rad, st, dr): ArboreBinar;
//functia întoarce rădăcina arborelui unic determinat de parcurgerile
//inordine-preordine
//rad este indicele rădăcinii arborelui
//st şi dr sunt limitele între care se găseşte parcurgerea inordine a
//arborelui în vectorul i
var r: ArboreBinar;
IPozRad: byte;
begin
new(r); //aloc memorie pentru rădăcina arborelui
r^.c := rad; //reţinem drept informaţie numărul asociat nodului
IPozRad := st;
// determin poziţia rădăcinii arborelui în parcurgerea inordine
while i[IPozRad] ≠ rad do inc(IPozRad);
if IPozRad = st then //subarborele stâng este vid
r.st^ := nil
else
// i[ st.. IPozRad-1] conţine subarborele stâng
r.st^ := ConstrArb(rad+ 1, st, IPozRad-1);
if IPozRad = dr then //subarborele drept este vid
r.dr^:= nil
else
// i[ IPozRad+ 1.. dr] conţine subarborele drept
r.dr^ := ConstrArb(rad+ IPozRad-st+ 1, IPozRad+ 1, dr);
// în subarborele stâng au fost IPozRad-st+ 1 vârfuri
end;
30
Se pune problema determinării numărului de arbori binari
distincţi cu n noduri, făcând abstracţie, bineînţeles de numerotarea
nodurilor.
Pentru n= 0 sau n= 1 există un singur arbore binar.
Dacă n= 2, există doi arbori binari distincţi.
Fig. 20.
Fig. 21.
Notăm cu bn numărul arborilor binari distincţi cu n noduri.
Evident, b0 = 1.
Pentru n > 0 arborii sunt formaţi din rădăcină şi doi subarbori cu i,
respectiv n-i-1 noduri (0 ≤ i < n).
R
||1,
dac\ n=1
bn = S|
||n−1
||i=∑0bibn−i−1, dac\ n>1
T
Pentru a obţine numărul arborilor binari distincţi cu n noduri este
suficient să rezolvăm această relaţie de recurenţă. Să considerăm funcţia
B( x) = ∑ bnxn
n≥ 0
Din relaţia de recurenţă, înmulţind ambii membri cu xn ,obţinem :
n−1
bnxn = x∑ (bx i
i )(bn − i − 1x
n− i −1
)
i =0
Sumând după n ≥ 1, obţinem :
n−1
31
Obţinem B(x)-b0 = x*B2(x) ⇔ xB2(x) - B(x)+1 = 0.
Rezolvând această ecuaţie de grad II obţinem :
1
1− 1− 4x 1
B( x) = ⇔ B( x) = (1− (1− 4x)2 )
2x 2x
Dezvoltând binomial (1-4x)1/2 , obţinem:
1 1 1
1 ( −1)...( − n+1)
B( x) = (1− ∑ 2 2
n!
2
(−4x)n ) ⇔
2x n≥ 0
1 1 1
1 ( −1)...( − n+1)
B( x) = ∑
2x n≥1
2 2
n!
2
(−1)n+122n xn ⇔
1 1 1
( −1)...( − n+1)
B( x) = ∑ 2 2
n!
2
(−1)n+122n−1 xn−1)
n≥1
Notând n-1 cu m, obţinem:
1 1 1
( −1)...( − m)
B( x) = ∑ 2 2 2
( m+1)! (−1)m22m+1 xm)
m≥ 0
Cum bn este coeficientul lui xn în B(x) obţinem
1 1 1
( − 1)...( − n)
n 2n+1 2 2 2
bn = (−1) 2 ⇔
(n + 1)!
1 (1− 2)(1− 2⋅ 2)...(1− 2n)
bn = (−1)n 22n+1 ⇔
2n+1 (n + 1)!
(2n − 1)(2(n − 1) − 1)...(2 − 1)
bn = 2n ⇔
(n + 1)!
1 (2n ⋅ n!)(2n − 1)(2(n − 1) − 1)...(2 − 1)
bn = ⇔
n+ 1 n!⋅ n!
(2n)!
bn = n1 1 n
+1⋅ n!⋅n! ⇔ bn = n+1C2n
Numărul arborilor binari distincţi cu n vârfuri va fi aproximativ
F I n
b = OG J
4
H K
n
3/ 2
n
Observaţii
1. Am demonstrat că fiecărui arbore binar îi corespunde o
32
singură pereche de secvenţe preordine-inordine. Considerând nodurile
numerotate în preordine, rezultă că arborii binari sunt definiţi de
permutările inordine distincte (permutările distincte ce se pot obţine
trecând numerele 1,2,...,n într-o stivă şi ştergându-le în toate modurile
posibile).
Deci numărul permutărilor inordine de n elemente este
1 n
n+1C2n
2. O problemă care are în mod surprinzător legătură cu cele
precedente este calculul produsului a n+1 matrici, M0,M1,...,Mn. Cum
înmulţirea matricilor este asociativă, am dori să ştim în câte moduri
putem calcula produsul M0× M1× ...× Mn.
Pentru n = 1 există o singură posibilitate.
Pentru n = 2 există două posibilităţi: (M0× M1) × M2 sau
M0× (M1× M2).
Pentru n = 3 există cinci posibilităţi :
((M0× M1) × M2) × M3;
(M0× M1) × (M2× M3);
(M0× (M1× M2)) × M3;
M0× ((M1× M2) × M3 );
M0× (M1× (M2× M3)).
Notăm cu P(n) numărul de moduri distincte în care putem calcula
produsul M0× M1× ...× Mn.
Produsul M0× M1× ...× Mn poate fi împărţit într-un produs de două
produse de matrici : (M0× M1× ...× Mk)(Mk+1× ...× Mn ).
Acestei asocieri i se poate pune în corespondenţă un arbore binar:
Fig. 22.
R
| 1, dacă n = 1
P (n) = S
||Tkn∑=−10P (k)⋅P (n−k−1), dacă n>1
Deci numărul de parantezări posibile pentru produsul a n+1
matrici coincide cu numărul arborilor binari distincţi cu n noduri.
1.8. Păduri
33
Definiţie
O pădure este un ansamblu de n ≥ 0 arbori disjuncţi.
De exemplu,
Fig. 23.
Îndepărtând rădăcina din orice arbore obţinem o pădure formată
din subarborii rădăcinii.
Orice pădure poate fi reprezentată ca un arbore binar. Pentru
aceasta, transformăm arborii din care este constituită pădurea în arbori
binari, utilizând reprezentarea fiu-frate. Apoi construim arborele binar
corespunzător pădurii, utilizând câmpul frate al rădăcinii fiecărui
arbore.
De exemplu, pentru pădurea din figura 24 arborele binar asociat este
Fig. 24.
Definiţie
Fie A1, A2, ..., An arborii unei păduri. Atunci arborele binar
corespunzător pădurii, B(A1, A2, ..., An) este:
1.vid, dacă n = 0;
2.are rădăcina egală cu rădăcina lui A1, subarborele stâng este
arborele corespunzător pădurii formată din subarborii lui A1, iar
subarborele drept este arborele binar corespunzător pădurii formată din
arborii A2, ..., An.
Operaţiile de parcurgere a unei păduri se reduc la parcurgerea
arborelui corespunzător.
34
Fig. 25.
35
Să considerăm p o m-ponderare oarecare pentru arborele T şi x
un vârf interior astfel încât p[x]>1. Dacă există mai multe astfel de
noduri, considerăm un vârf de pe un nivel de rang minim. Fie y şi z cei
doi fii ai lui x. Construim o altă m-ponderare p’ a lui T astfel:
p’[x] = 1; p’[y] = p[y]+p[x]-1; p’[z] = p[z]+p[x]-1.
Atunci P’(T) = P(T)-p[x]-p[y]-p[z]+p’[x]+p’[y]+p’[z] ⇒
P’(T) = P(T) -p[x]-p[y]-p[z]+1+ p[y]+p[x]-1+ p[z]+p[x]-1 ⇒
P’(T) = P(T)+p[x]-1 ⇒ P’(T) > P(T).
Modificând succesiv ponderile nodurilor interioare de sus în jos,
obţinem după un număr finit de paşi m-ponderarea po, în care toate
vârfurile interioare au ponderea 1.
Deci Po(T) ≥ P(T), ∀p o m-ponderare a arborelui T
2. Observaţia 1. oferă o modalitate de m-ponderare optimală a
unui arbore binar strict dat. Rămâne să determinăm, pentru n şi m daţi,
arborele binar strict cu n vârfuri terminale care maximizează Pn,m(T).
Demonstrăm că arborele căutat T* este arborele binar complet. Fie T un
arbore binar strict cu n vârfuri terminale care nu este complet şi fie x şi
y două vârfuri terminale fii ai aceluiaşi nod interior, situate pe nivelul i,
iar z un nod terminal situat pe nivelul j, astfel încât i-j > 1.
Fig. 26.
Mutăm nodurile x şi y de pe nivelul i pe nivelul j+1, ca fii ai
nodului z şi reponderăm nodurile.
P(T’) = P(T)-2(m-i+1)-1-(m-j+1)+2(m-j)+m-i+2+1 ⇒
P(T’) = P(T)+i-j-1 > P(T).
Aplicăm succesiv această transformare până când obţinem un
arbore binar complet.
Deci P(T*) > P(T), ∀T arbore binar strict.
Q.E.D.
Reprezentarea informaţiilor
Arborele căutat fiind complet, pentru implementare alegem
reprezentarea secvenţială.
program arbore_m_ponderat;
uses crt;
const NMaxVfT=20;
36
NMaxVf=2*NMaxVfT-1;
type Vf=0..NMaxVf;
arbore_ponderat=array[Vf] of word;
var n,i,nivel:Vf;
m:word;
p:arbore_ponderat;
procedure afisare;
const pst=13;
pdr=28;
pp=42;
var i:Vf;
begin
writeln('Nodul Fiu stang Fiu drept Pondere');
for i:=1 to n-1 do
begin
write(i);
gotoxy(pst,wherey);write(2*i);
gotoxy(pdr,wherey);write(2*i+1);
gotoxy(pp,wherey);writeln(1);
end;
for i:=n to 2*n-1 do
begin
write(i);
gotoxy(pp,wherey);writeln(p[i]);
end
end;
begin
clrscr;
write('n=');readln(n);
write('m=');readln(m);
if m<trunc(ln(n)/ln(2))+1
then writeln('Nu exista solutie!')
else
begin
for i:=1 to n-1 do p[i]:=1;
{nodurile interioare au ponderea 1}
for i:=n to 2*n-1 do
begin
nivel:=trunc(ln(i)/ln(2));
p[i]:=m-nivel;
{nodurile terminale au ponderea egala cu m-numarul
nivelului pe care sunt situate}
end;
afisare
end;
readln
end.
program interclasare_optimala;
const NMaxSecv=20;
LgMaxSecv=50;
type Ind=1..LgMaxSecv;
Secv=1..NMaxSecv;
Nod=1..2*NMaxSecv;
Arbore=array[Nod] of integer;
var l:array[Secv] of Ind;{lungimile secventelor}
n:Secv; {numarul de secvente}
m,k:word;{m=numarul total de elemente}
o:array[1..LgMaxSecv*NMaxSecv] of integer;
{rezultatul interclasarii}
s:array[Secv,Ind] of integer;{secventele}
A:Arbore;{arborele de selectie}
j:array[Secv] of Ind;{indicii curenti in secvente}
procedure citire;
var f:text;
i:Secv;
j:Ind;
begin
assign(f,'int.in'); reset(f);
readln(f,n);
for i:=1 to n do read(f,l[i]);
38
readln(f);
for i:=1 to n do
begin
m:=m+l[i];
s[i,l[i]+1]:=MaxInt;
for j:=1 to l[i] do read(f,s[i,j]);
readln(f);
end;
close(f);
end;
procedure ConstrArbSel;
var i:Nod;
begin
{constructia arborelui de selectie}
for i:=n to 2*n-1 do A[i]:=s[i-n+1,1];
{initializarea nodurilor terminale}
for i:=n-1 downto 1 do
if A[2*i]<A[2*i+1] then
A[i]:=A[2*i]
else
A[i]:=A[2*i+1];
{initializez valorile indicilor in secvente}
for i:=1 to n do j[i]:=1;
end;
procedure restructurare;
var i,tata,frate:Nod;
begin
{determin secventa corespunzatoare nodului eliminat}
i:=1;
while i<=n-1 do
if A[i]=A[2*i] then
i:=2*i
else
i:=2*i+1;
{i este nodul terminal corespunzator secventei din care am luat
un element}
inc(j[i-n+1]);
A[i]:=s[i-n+1,j[i-n+1]];
{restauram valorile nodurilor de pe drumul de la nodul i la
radacina}
while i>1 do
begin
tata:=i div 2;
if i=2*tata then frate:=2*tata+1
else frate:=2*tata;
if A[i]>A[frate] then A[tata]:=A[frate]
else A[tata]:=A[i];
i:=tata;
end;
end;
procedure afisare;
39
var i:word;
begin
writeln('Rezultatul interclasarii: ');
for i:=1 to m do
write(o[i],' ');
writeln;
readln
end;
Fig. 27.
Codificăm drumurile de la rădăcină la nodurile terminale
marcând cu 0 fiecare deplasare la stânga şi cu 1 fiecare deplasare la
dreapta. Concatenând codificările în preordine, obţinem o reprezentare
a arborilor binari stricţi.
Pentru exemplul din figura 27 codificarea este 00010001010111.
Problemă
Data fiind s, reprezentarea unui arbore binar strict obţinută prin
concatenarea în preordine a codificărilor drumurilor de la rădăcină la
nodurile terminale, generaţi arborele corespunzător.
Soluţie:
Fie x, y două noduri terminale, situate pe nivelul maxim in
arbore, fii ai aceluiaşi nod t. Dacă notăm p secvenţa ce codifică drumul
40
de la rădăcină la t, atunci şirul s are forma αp0p1β. Determinăm p astfel
încât s = αp0p1β, p fiind cea mai lungă secvenţă cu această proprietate.
De exemplu, pentru codificarea de mai sus p = 010.
Putem construi astfel un drum de la rădăcina arborelui la două
noduri terminale fraţi.
Fig. 28.
Eliminăm din s secvenţa 0p1, ceea ce corespunde eliminării din
arbore a două noduri terminale fii ai aceluiaşi nod. Obţinem, de
exemplu, s = 000100111.
Am redus problema la generarea unui arbore cu un nod terminal
mai puţin, pe care îl vom suprapune peste drumul generat anterior.
program Generare_Arbore_Binar_Strict;
uses crt;
type Arbore=^NodArbore;
NodArbore= record
st,dr:Arbore
end;
var s:string;
f:text;
n, poz, NrNod, lg, NrTest: byte;
A, T: Arbore;
41
then begin
lg:=j;
poz:=i
end;
inc(j);
end;
inc(i)
end;
end;
procedure ConstrDrum;
var q, x: Arbore;
gata: boolean;
i: byte;
begin
q:=A; i:=poz;
{construiesc drumul de la radacina arborelui la nodul tata al
nodurilor terminale ce urmeaza sa le agat in arbore,
suprapunand eventual peste arborele deja construit}
gata:=false;{gata devine true cand am terminat de parcurs
portiunea de drum deja construita in arbore}
while not gata and (i<poz+lg) do
if s[i]='0' then
if q^.st<>nil then
begin
q:=q^.st;
inc(i)
end
else gata:=true
else
if q^.dr<>nil then
begin
q:=q^.dr;
inc(i)
end
else gata:=true;
while i<poz+lg do
begin
new(x);
x^.st:=nil; x^.dr:=nil;
if s[i]='0' then
q^.st:=x
else
q^.dr:=x;
q:=x;
inc(i)
end;
{agat doua noduri terminale, fii ai nodului q}
new(x);
x^.st:=nil; x^.dr:=nil;
if q^.st=nil then q^.st:=x;
new(x);
x^.st:=nil; x^.dr:=nil;
if q^.dr=nil then q^.dr:=x;
42
end;
procedure afisare;
begin
clrscr;
inc(NrTest);
NrNod:=1;
writeln('Testul nr. ', NrTest,':');
scrie(A,2,1,80);
readln;
end;
1.10. Exerciţii
1. Demonstraţi că în orice graf G = (V, U) conex, U≥ V-1.
2. Demonstraţi că în orice arbore există cel puţin două vârfuri
terminale.
3. Calculaţi numărul arborilor cu n vârfuri şi secvenţa gradelor
vârfurilor d1,d2,...,dn (di ≥ 1, ∀i∈{1, 2, ..., n}, d1+d2+...+dn = 2n-2).
Scrieţi un program de generare a tuturor arborilor cu secvenţa gradelor
43
dată.
4. Calculaţi numărul arborilor cu n vârfuri, dintre care p terminale.
5. Secvenţele obţinute prin parcurgerile în postordine şi în inordine ale
unui arbore binar definesc în mod unic arborele? Dacă da, scrieţi un
algoritm de generare a arborelui binar corespunzător.
Aceeaşi problemă pentru parcurgerile în preordine şi în postordine,
respectiv parcurgerea în inordine şi parcurgerea pe niveluri.
6. Generaţi toţi arborii binari distincţi cu n vârfuri.
7. Scrieţi o funcţie de duplicare a unui arbore binar.
8. Scrieţi o funcţie de căutare a unei valori de tipul informaţiei asociate
nodurilor într-un arbore binar. Analizaţi complexitatea funcţiei.
9. Scrieţi o procedură iterativă de parcurgere în preordine a unui arbore
binar.
10. Scrieţi o procedură iterativă de parcurgere în postordine a unui
arbore binar.
11. Scrieţi o funcţie de ştergere a unui arbore binar.
12.Scrieţi un program care să parcurgă un arbore oarecare în
reprezentarea fiu-frate pe niveluri.
13. Definim gradul unui arbore cu rădăcină ca fiind gradul maxim al
nodurilor sale. Fie un arbore cu gradul k şi înălţimea h. Care este
numărul maxim de noduri din acest arbore ?
Reprezentăm fiecare nod al arborelui printr-un articol ce conţine
informaţia asociată nodului şi k pointeri spre rădăcinile subarborilor :
Arbore = ^NodArbore;
NodArbore = record
c: TipInf;
leg: array[1..k] of Arbore;
end;
a). Scrieţi o funcţie de creare a unui arbore echilibrat de grad k.
b). Descrieţi algoritmul de parcurgere pe niveluri şi în adâncime a unui
arbore de grad k.
c). Scrieţi o funcţie care să determine înălţimea unui arbore de grad k.
d). Scrieţi o funcţie care să testeze egalitatea a doi arbori de grad k.
14. Scrieţi o funcţie care să determine numărul de noduri terminale ale
unui arbore binar.
15. Scrieţi un algoritm care, dat fiind un arbore binar, schimbă fiul
stâng cu fiul drept, pentru orice nod din arbore. De exemplu :
44
Fig. 29.
16. Demonstraţi că orice arbore binar este 2-colorabil.
17. Fie P un poligon convex. O diagonală este un segment ce uneşte
două vârfuri neadiacente. Numim triangularizare a poligonului convex
P o mulţime de diagonale care împart poligonul în triunghiuri disjuncte.
Calculaţi numărul triangularizărilor posibile pentru un poligon convex
cu n vârfuri.
18. Scrieţi o funcţie care să verifice dacă un arbore binar este strict.
19. Calculaţi numărul arborilor binari de înălţime h.
20. Scrieţi un program care să construiască arborele binar corespunzător
pădurii formate din arborii A1, A2, ..., An şi parcurgeţi arborele în
preordine, inordine, postordine.
21. Fie P o pădure, arborii componenţi fiind reprezentaţi prin referinţe
ascendente. Scrieţi un algoritm care să determine pentru oricare două
vârfuri din pădure cel mai apropiat ascendent comun, dacă acesta există.
Analizaţi complexitatea algoritmului.
22. "Problema" telefonistelor
O reţea telefonică formată din n centrale numerotate de la 1 la n,
are forma unui arbore oarecare. Telefonista de la centrala k, 1 ≤ k
≤ n, care a intrat în posesia unei informaţii importante, arde de
nerăbdare s-o împărtăşescă tuturor colegelor ei. Ştiind că fiecare
telefonistă poate vorbi la un moment dat doar cu una dintre vecinele ei,
că o convorbire durează un minut şi că fiecare telefonistă, după ce a
intrat în posesia informaţiei se grăbeşte să o comunice celorlalte vecine
ale ei care n-au aflat-o încă, să se determine succesiunea propagării în
timp a informaţiei şi timpul minim necesar ca toate telefonistele să
cunoască vestea pornită de la centrala k.
45
Anexă
program Constructie_arbore_cu_secventa_gradelor_data;
const NMaxVf = 20;
type Vf = 1..NMaxVf;
var a, d: array[Vf] of Vf;
n, i, VfT, VfNt: Vf;
s: byte;
fout: text;
procedure citire;
var i: Vf;
fin: text;
begin
assign(fin, 'grade.in'); reset(fin);
readln(fin, n);
for i := 1 to n do read(fin, d[i]);
readln(fin);
close(fin);
end;
procedure afisare;
var i: Vf;
begin
writeln(fout, 'Muchiile arborelui sunt: ');
for i := 1 to n-1 do write (fout, '(', i, ',', a[i], ') ');
writeln(fout);
end;
begin
citire;
assign(fout,'grade.out'); rewrite(fout);
s := 0;
for i := 1 to n do s := s+d[i];
if s <> 2*(n-1)then
writeln(fout,'Secventa eronata! Suma gradelor trebuie sa
fie 2(n-1)!')
else
for VfT := 1 to n-1 do
begin
VfNt := VfT+1;
while (VfNt < n) and (d[VfNT] = 1) do inc(VfNt);
a[VfT] := VfNt;
dec(d[VfNT]);
end;
afisare;
close(fout);
end.
46
Muchiile arborelui sunt:
(1,4) (2,5) (3,6) (4,7) (5,7) (6,7)
program Generare_Arbori_cu_n_Varfuri;
const NrMaxVf=10;
type Vf = 1..NrMaxVf;
Arbore = array[Vf] of Vf;
var n: Vf;
fout: text;
a: Arbore;
NrArb: longint;
procedure determina_arbore;
var MB, MA: set of Vf;
i, k: Vf;
begin
inc(NrArb);
write(fout, 'Arborele nr. ',NrArb,' :');
MB := []; MA := [];
for i := 1 to n-1 do MA := MA+[a[i]];
for i := 1 to n-1 do
begin
k := 1;
while k in MA+MB do inc(k);
MB := MB+[k];
MA := MA-[a[i]];
write(fout, '(', k, ',', a[i], ') ');
end;
writeln(fout);
end;
47
De exemplu, pentru n=4, conţinutul fişierului de ieşire va fi:
Arborele nr. 1 :(2,1) (1,1) (3,4)
Arborele nr. 2 :(3,1) (1,2) (2,4)
Arborele nr. 3 :(2,1) (1,3) (3,4)
Arborele nr. 4 :(2,1) (1,4) (3,4)
Arborele nr. 5 :(3,2) (2,1) (1,4)
Arborele nr. 6 :(1,2) (2,2) (3,4)
Arborele nr. 7 :(1,2) (2,3) (3,4)
Arborele nr. 8 :(1,2) (2,4) (3,4)
Arborele nr. 9 :(2,3) (3,1) (1,4)
Arborele nr. 10 :(1,3) (3,2) (2,4)
Arborele nr. 11 :(1,3) (2,3) (3,4)
Arborele nr. 12 :(1,3) (2,4) (3,4)
Arborele nr. 13 :(2,4) (3,1) (1,4)
Arborele nr. 14 :(1,4) (3,2) (2,4)
Arborele nr. 15 :(1,4) (2,3) (3,4)
Arborele nr. 16 :(1,4) (2,4) (3,4)
program Operatii_pe_arbori_binari;
const NrMaxVf = 20;
type Vf = 0..NrMaxVf;
ArboreBinar = ^NodArboreBinar;
NodArboreBinar = record
inf: char;
st, dr: ArboreBinar;
end;
var n: Vf;
fin, fout: text;
A: ArboreBinar;
function CreareArbore(x: Vf): ArboreBinar;
{creeaza un arbore binar echilibrat cu n varfuri}
var rad: ArboreBinar;{radacina arborelui care se creaza}
begin
if x = 0 then {arbore vid}
CreareArbore := nil
else
begin
new(rad);{aloc memorie pentru radacina arborelui}
read(fin, rad^.inf);
{citesc informatia asociata radacinii}
rad^.st := CreareArbore(x div 2);
{creez subarborele stang}
rad^.dr := CreareArbore(x-x div 2 -1);
{creez subarborele drept}
CreareArbore:=rad;
end;
end;
48
begin
if rad <> nil then
begin
write(fout, rad^.inf);
preordine(rad^.st);
preordine(rad^.dr);
end
end;
49
type Coada = ^NodCoada;
NodCoada = record
inf: ArboreBinar;
urm: Coada
end;
var IncC, SfC, p, q: Coada;
begin
write(fout, 'Parcurgerea pe niveluri: ');
if rad <> nil then
begin
{initializez coada cu radacina arborelui}
new(IncC); IncC^.inf := rad; IncC^.urm := nil; SfC := IncC;
while IncC <> nil do
begin
p := IncC;
write(fout, p^.inf^.inf);
{vizitez nodul din arbore corespunzator lui p}
if p^.inf^.st <> nil then
{inserez in coada fiul stang al nodului vizitat}
begin
new(q); q^.inf := p^.inf^.st; q^.urm := nil;
SfC^.urm := q; SfC := q
end;
if p^.inf^.dr <> nil then
{inserez in coada fiul drept al nodului vizitat}
begin
new(q); q^.inf := p^.inf^.dr; q^.urm := nil;
SfC^.urm := q; SfC := q
end;
{extrag efectiv din coada nodul p}
IncC := IncC^.urm;
dispose(p)
end;
end;
writeln(fout)
end;
50
assign(fout,'opbin.out'); rewrite(fout);
readln(fin,n);
A:=CreareArbore(n);
close(fin);
write(fout,'Parcurgerea preordine: '); preordine(A);
writeln(fout);
write(fout,'Parcurgerea postordine: '); postordine(A);
writeln(fout);
inordine_iterativ(A);
parcurgere_pe_niveluri(A);
writeln(fout, 'Inaltimea arborelui este: ', inaltime(A));
close(fout);
end.
program Constructie_Arbore_Binar;
const NrMaxVf = 20;
type Vf = 0..NrMaxVf;
ArboreBinar = ^NodArboreBinar;
NodArboreBinar = record
inf: Vf;
st, dr: ArboreBinar;
end;
Parcurgere = array[Vf] of Vf;
var A: ArboreBinar;
i: Parcurgere;{parcurgerea inordine}
n: Vf;{numarul de varfuri din arbore}
fout: text;
function ConstrArb(rad, st, dr: Vf): ArboreBinar;
{functia intoarce un adresa radacinii arborelui binar cu
radacina rad; st,dr reprezinta limitele intre care se gaseste
parcurgerea inordine a arborelui in vectorul i}
var r: ArboreBinar;
IPozRad: Vf;
begin
new(r); r^.inf := rad;
{determin pozitia radacinii in parcurgerea inordine}
IPozRad := st;
while i[IPozRad] <> rad do inc(IPozRad);
if IPozRad = st then {subarborele stang este vid}
r^.st := nil
else
{i[st..IPozRad-1] constituie parcurgerea inordine a
subarborelui stang, cu radacina rad+1}
r^.st := ConstrArb(rad+1,st,IPozRad-1);
if IPozRad = dr then {subarborele drept este vid}
r^.dr := nil
else
{i[IPozRad+1..dr] constituie parcurgerea inordine a
subarborelui drept, cu radacina rad+IPozRad-st+1,
deoarece in subarborele stang sunt IPozRad-st+1 varfuri}
r^.dr := ConstrArb(rad+IPozRad-st+1,IPozRad+1,dr);
51
ConstrArb := r;
end;
procedure Citire;
var k: Vf;
fin: text;
begin
assign(fin,'pi.in'); reset(fin);
readln(fin,n);
for k := 1 to n do read(fin, i[k]);
readln(fin);
close(fin);
end;
procedure AfisareArb;
begin
assign(fout,'pi.out'); rewrite(fout);
if A = nil then
write(fout, 'Arbore vid')
else
preordine(A);
{afiseaza reprezentarea cu paranteze a arborelui}
writeln(fout);
close(fout);
end;
begin {program principal}
Citire;
if n > 0 then
A := ConstrArb(1, 1, n)
else
A := nil;
AfisareArb;
end.
{$N+ }
52
program Numar_Arbori_Binari_Distincti;
var NrVf, i: byte;
NrArb: extended;
begin
write('Introduceti numarul de varfuri '); readln(NrVf);
NrArb := 1;
for i := 2 to NrVf do
NrArb := NrArb*(NrVf+i)/i;
writeln('Nr. de arbori binari distincti cu ',NrVf,'varfuri: ');
writeln(NrArb:50:0);
readln
end.
53
54
55