Sunteți pe pagina 1din 63

Alocare dinamic

Generaliti
Alocare static i alocare dinamic
Structuri semistatice
Structuri dinamice
Implementarea structurilor de date folosind
alocare dinamic
Structuri arborescente
Implementri sugerate
Probleme propuse
Soluiile problemelor
Capitolul
9



9.1. Generaliti
Tipurile de date de care avem nevoie pentru a putea memora i prelucra datele, fie
exist n limbajul de programare ales, fie se pot declara cu ajutorul tipurilor existente.
n organizarea datelor, datele de diferite tipuri se grupeaz n structuri de date. Dac
gruparea se face la nivelul rezolvrii problemei, independent de mediul de stocare a
datelor, vorbim de structuri abstracte (logice) de date.
Problema stocrii acestor structuri ne conduce la existena unei reprezentri fizice a
structurilor logice de date. Avnd n vedere c datele trebuie s fie disponibile n me-
moria intern n timpul executrii programului i c aceast memorie se consider or-
ganizat liniar, se pune problema liniarizrii structurilor de date. Aceast liniarizare
se realizeaz n mod diferit pentru structuri de date diferite, astfel nct operaiile ele-
mentare care se efectueaz asupra lor s se poat efectua n timp optim.
O structur de date poate ocupa n memorie o zon de dimensiune constant, n ca-
re elementele componente ocup tot timpul executrii programului acelai loc. O astfel
de structur este static.
Dac o structur de date ocup o zon de dimensiune constant, dar elementele
componente ocup un loc variabil n timpul executrii programului, atunci o astfel de
structur este semistatic.
Dac o structur de date ocup n memoria intern o zon care se aloc n timpul
executrii programului pe msura nevoii de prelucrare, fr a avea o dimensiune cons-
tant, atunci structura este dinamic.
Alocarea de memorie pentru diferite tipuri de structuri de date se realizeaz n mod
diferit de ctre compilatoare.
234 9. Alocarea dinamic

n cazul structurilor statice alocarea este static att la nivelul ntregii structuri, ct
i pentru fiecare component n parte.
Pentru o structur semistatic alocarea este static la nivelul structurii i dinamic
la nivelul componentelor. Acestea se pot aloca fie static, fie dinamic.
Pentru o structur dinamic alocarea este dinamic la nivelul componentelor ct i
la nivelul ntregii structuri.

Observaie
Aceast clasificare a structurilor de date n structuri statice, semistatice i dinamice
se refer la structurile fizice de date i nicidecum la nsi aceste structuri. O aceeai
structur abstract de date poate fi, n general, implementat i ca structur static i
ca structur dinamic.

Exist structuri logice de date ale cror implementare n mod consacrat este fie
static, fie dinamic. De exemplu, o mulime se va implementa, de regul static. O stiv
poate fi implementat static sau dinamic, asigurnd o implementare adecvat a ope-
raiilor specifice de adugare i eliminare a elementelor. Este important s se gseasc
acea implementare care convine cel mai bine aplicaiei considerate.

9.2. Alocare static i alocare dinamic
n studiul realizat pn acum am ntlnit doar variabile alocate static. Acestora li se
aloc spaiu n timpul compilrii, spaiul respectiv rmne ocupat pn la sfritul
execuiei programului respectiv. Am pus ghilimele, deoarece nu ntotdeauna vom ocu-
pa efectiv cu date de prelucrat o astfel de zon de memorie alocat. Rezervarea spaiu-
lui se realizeaz n timpul compilrii, pe baza declaraiilor, urmnd ca n timpul execu-
iei programului s se lucreze cu date care ocup ntreaga zon rezervat sau numai o
parte din aceasta. De aici rezult imediat prima deficien a acestui tip de alocare, de-
oarece este evident c n timpul prelucrrii acest spaiu poate s se dovedeasc fie prea
mare, fie prea mic.
Cealalt inconvenien se refer la faptul c dimensiunea spaiului alocat rmne
nemodificat pe tot parcursul execuiei programului. Altfel spus, dac ntr-un program
prelucrm o structur de date care crete pe parcursul execuiei, eventual i o structur
care descrete, pentru ambele trebuie s alocm spaiu la dimensiunea maxim posi-
bil. Dac ntr-o astfel de structur (de exemplu ntr-un tablou unidimensional) trebuie
s inserm elemente noi, la nceputul ei sau undeva ntre dou elemente, vor fi necesa-
re numeroase translatri. Invers, dac din aceste structuri vrem s eliminm elemente,
din nou vom efectua translatri care conduc la mrirea timpului de execuie.
Aceste probleme, practic, pot s apar n cazul prelucrrii acelor structuri de date
ale cror dimensiune se modific n timpul prelucrrilor. Ele, de regul, se aloc dina-
mic. Alocarea dinamic este o alternativ de a lucra cu resursele memoriei care ofer
9. Alocarea dinamic 235

posibilitatea ca programatorul s se extind cu datele de prelucrat ntr-o zon specia-
l de memorie, numit heap. Aici se pot aloca variabile simple, tablouri, dar structurile
de date specifice sunt cele numite n introducerea acestui capitol semistatice i dinami-
ce. Reamintim c orice structur de date poate fi alocat fie static, fie dinamic. Rm-
ne la latitudinea programatorului, ca n funcie de urmtoarele criterii s aleag moda-
litatea de alocare. De regul, se lucreaz cu alocare dinamic, dac:
nu se cunoate dimensiunea structurii de date n momentul nceperii execuiei pro-
gramului;
dimensiunea structurii de date variaz n timpul execuiei;
se vor efectua operaii de inserare respectiv eliminare de elemente n/din structur;
structurile de date nu au suficient spaiu n memoria static, unele trebuie plasate n
heap.

9.3. Structuri semistatice
Datele de tip tablou, mulime i articol (nregistrare), ntlnite pn acum sunt structuri
de date statice care, de regul, se prelucreaz alocnd spaiul necesar n mod static.
Dar am ntlnit n clasa a 9-a i o structur semistatic, stiva.
Pe parcursul rezolvrii problemelor care prelucrau stive (n clasa a 9-a) am utilizat
alocare static, declarnd stiva sub forma unui tablou unidimensional. Am realizat
operaii de adugare i de eliminare elemente, actualiznd dimensiunea tabloului dup
fiecare astfel de operaie. Atunci cnd un element nu a mai fost necesar, el a fost eli-
minat din structur, iar locaiile corespunztoare au devenit libere (puteau fi reutili-
zate).
Cnd structura semistatic nu conine nici un element, ea este vid, iar cnd ntreg
spaiul alocat a fost utilizat, ea este plin. n cazul alocrii statice, nainte de o operaie
de adugare a unui element nou am verificat dac structura nu este plin, iar nainte de
o operaie de tergere, am verificat dac structura nu este vid.
n acest capitol vom aborda implementri dinamice i pentru structurile semistati-
ce, exemplificnd diferitele operaii n limbajul Pascal.

9.3.1. Stiva
Reamintim c stiva (din punct de vedere logic) se aloc ntr-un spaiu S avnd baza B
i vrful V, astfel nct B < V.

element nou
element eliminat
V
B
V
inds
B
inds

236 9. Alocarea dinamic

a) Adugarea unui nou element se va face prin alocarea primei locaii libere de la ba-
z ctre vrf;
b) tergerea unui element se face prin eliberarea primei locaii ocupate de la vrf ctre
baz.

Aceste operaii necesit pstrarea i gestionarea unui indicator de stiv care crete
la adugare (inds inds + 1) i descrete la tergere (inds inds 1). Aadar, stiva
poate fi privit ca o structur semistatic format din cuplul (S, inds).
Dac stiva este vid, inds = B, iar dac stiva este plin, inds = V. nainte de aduga-
re, respectiv eliminare, se va verifica dac stiva nu este plin, respectiv vid. Modul n
care intr i ies elementele din stiv justific i denumirea de list LIFO (Last In
First Out).

9.3.2. Coada
Cealalt structur semistatic, frecvent utilizat n prelucrarea datelor este coada. Fie S
o zon de memorie avnd baza B i vrful V, astfel nct B < V. Avem V B = n.
Coada poate fi privit ca o structur semistatic format din tripletul (S, inda, inde),
unde inda i inde sunt doi indicatori relativi, care arat locul unde trebuie adugate
elemente noi, respectiv de unde se elimin elementele. Adresa elementului de extras i
adresa la care se pune elementul de adugat va fi B + inde respectiv B + inda.


liber
V
inda
inde
B
liber

Coada este plin dac inde = (inda + 1)(mod n) i este vid dac inda = inde. Iniial
inda = inde = 0 (coada vid). La adugare inda (inda + 1)(mod n), iar la eliminare
inde (inde + 1)(mod n). nainte de adugare se verific dac structura este plin, iar
nainte de tergere se verific dac avem sau nu coad vid.
Datorit modului n care se adaug i se extrag elementele, coada se mai numete i
list FIFO (First In First Out).

9.3.3. Tabela de dispersie
Amintim i tabela de dispersie care, de asemenea se aloc ntr-un spaiu S avnd baza
B i vrful V, astfel nct B < V, mprit n n pri egale, numerotate cu valori apari-
nnd mulimii {0, 1, ..., n 1}. Fie M o mulime i o funcie h: M {0, 1, ..., n 1}
definit pe M.
9. Alocarea dinamic 237

Tabela de dispersie T este o structur semistatic prin care se realizeaz partiiona-
rea mulimii M n n clase disjuncte: M
0
, M
1
, ..., M
n-1
.








Cel mai simplu exemplu de tabel de dispersie este acela n care elementul m M
se caut mai nti la adresa B + h(m), apoi la B + (h(m) + 1)(mod n), apoi la adresa
B + (h(m) + 2)(mod n) .a.m.d. Datele din prile tabelei, n general, sunt memorate
sub forma unor liste liniare. Cutarea se termin n unul din urmtoarele trei cazuri:
elementul cutat a fost gsit, celula B + (h(m) + i)(mod n) este liber sau au fost par-
curse toate cele n locaii.

9.4. Structuri dinamice
n cazul structurilor dinamice alocarea dinamic a spaiului de memorie necesar se re-
alizeaz pe msura apariiei elementelor structurii. Cu toate c exist posibilitatea alo-
crii n heap i a unor variabile simple sau a unor tablouri, alocarea dinamic, de regu-
l, impune nlnuirea acestora. Reprezentarea intern a unui element trebuie s coni-
n, pe lng informaia util propriu-zis, i o informaie suplimentar numit refe-
rin prin care se realizeaz nlnuirea.

Informaie util
(propriu-zis)
Informaie de legtur
(referin)

Dac ntr-o structur dinamic toate elementele sunt pe un singur nivel, ea se nu-
mete list liniar, dac elementele sunt structurate pe mai multe niveluri, avem o
structur arborescent, iar dac elementele nu sunt grupate pe niveluri avem structuri
de tip reea.

9.4.1. Lista liniar
Fiind dat o mulime M, prin list liniar se nelege o secven (eventual vid) de ele-
mente din M. Pentru reprezentarea intern a unei liste vor fi necesare:
a) referin la primul element din list (numit cap de list);
b) nlnuirea elementelor listei ntre ele;
c) indicarea ultimului element din list (prin referina special nil care semnific
faptul c n nlnuirea respectiv, dup acest element nu mai urmeaz alt element).
V

M
n-1
M
n-2

.
.
.
M
1
B

M
0
238 9. Alocarea dinamic


Cap
Inf
1
Leg
1
Inf
2
Leg
2 Inf
n
Leg
n
= nil
. . .


Listele de acest tip sunt liste liniare simplu nlnuite. Dac informaia de legtur
conine att adresa elementului precedent ct i adresa elementului urmtor, avem
liste dublu nlnuite. Dac ntr-o list liniar primul i ultimul element sunt legate
astfel nct Leg
n
= referin la primul element, lista se numete circular.
Listelor liniare alocate dinamic le sunt caracteristice operaiile: inserare i elimina-
re element n/din structur, crearea structurii prin inserare n lista iniial vid, traversa-
rea listei, cutarea unui element, concatenarea sau tierea n dou (eventual n mai
multe pri) a unei liste.

9.4.2. Arborele
Fiind dat o mulime M de elemente denumite noduri sau vrfuri, vom numi arbore
(conform definiiei date de Knuth) un set finit de noduri astfel nct:
a) exist un nod cu destinaie special, numit rdcina arborelui;
b) celelalte noduri sunt repartizate n m 0 seturi disjuncte A
1
, A
2
, ... A
m
, fiecare set
A
i
constituind la rndul su un arbore.

Elementele arborelui sunt nodurile i legturile dintre ele. Nodul rdcin r l con-
siderm ca fiind pe nivelul 0.
ntruct A
1
, A
2
, ..., A
m
sunt la rndul lor arbori, fie r
1
, r
2
, ..., r
m
rdcinile lor. Atunci
r
1
, r
2
, ..., r
m
formeaz nivelul 1 al arborelui. Nodul r va avea cte o legtur cu fiecare
dintre nodurile r
1
, r
2
, ..., r
m
. Continund acest procedeu, vom avea nivelurile 2, 3, ...,
ale arborelui. Dac numrul nivelurilor este finit, atunci i arborele este finit, n caz
contrar se numete arbore infinit.



r
r
1

r
11
r
12
r
m
r
m1
r
2

r
21 r
23
r
22
. . .
. . . nivelul 1
. . . nivelul 2
A
m
A
2
A
1


Aceast viziune asupra arborilor este ierarhic, deoarece nodurile sunt subordonate
unele altora. Fiecare nod este subordonat direct unui singur nod, excepie constituie r-
dcina care nu este subordonat nici unui nod. Dac un nod nu are nici un nod subor-
donat, atunci se numete frunz sau nod terminal.
9. Alocarea dinamic 239

O categorie foarte rspndit de arbori o reprezint arborii binari. Un arbore binar
se compune din rdcin i din doi subarbori denumii subarborele stng, respectiv
drept. Amintim faptul c exist o deosebire ntre arborii binari i arborii oarecare
avnd noduri cu cel mult doi descendeni. n cazul arborilor oarecare este suficient
dac se tie c B este fiul lui A, pe cnd la un arbore binar trebuie s tim n plus dac
B este fiul stng sau fiul drept al lui A.
Dac nodul A are ca subordonai nodurile B i C, atunci A se numete printele
(tatl) lui B i C, B i C sunt fiii lui A, iar B este fratele lui C.

Ierarhizarea nodurilor se poate concretiza prin diferite tipuri de referine, n general
folosindu-se referine descendente (printe, fiu), dar putndu-se folosi i referine as-
cendente (fiu, printe) sau orizontale (frate, frate).
Dac numrul maxim de referine ale unui nod este n, atunci un element corespun-
ztor unui nod poate fi reprezentat astfel:

Informaie util Leg
1
Leg
2
. . . Leg
n


Leg
i
conine referina la cel de-al i-lea nod adiacent nodului dat. Absena unei leg-
turi se semnaleaz prin valoarea special nil.
Dac nu exist o ierarhizare a nodurilor pe niveluri, atunci arborele se numete ne-
orientat.

9.4.3. Reeaua
Dac nodurile unei mulimi M nu se mai organizeaz pe niveluri, dndu-se posibilita-
tea existenei de legturi ntre oricare dou dintre ele, se obine o structur de tip reea.
Fiecare nod N poate avea un numr oarecare de referine i poate fi referit la rndul
su de un numr oarecare de noduri.

9.5. Implementarea structurilor de date folosind
alocare dinamic
n cazul n care anumitor variabile intenionm s le alocm spaiu n heap, acestea
vor fi numite variabile dinamice. Tipul variabilelor dinamice poate fi orice tip recu-
noscut de limbajul de programare utilizat. Dar, atenie: variabilele dinamice se declar
prin intermediul referinelor lor. Altfel spus, n seciunea var (n Pascal) nu vom avea
nici o declaraie care s asocieze nume unei variabile dinamice, n schimb vom avea
declaraii ale variabilelor de tip referin (pointerilor) care la rndul lor asigur accesul
la variabila dinamic.

240 9. Alocarea dinamic

9.5.1. Variabile de tip referin (pointeri)
Variabila de tip referin (pointerul) este o variabil alocat static. n limbajul Pascal
tipul acestora este un tip special care nu este compatibil cu nici un alt tip de date. Re-
gulile de prelucrare ale lor sunt bine precizate, deoarece valoarea unui pointer este o
adres din heap sau valoarea special nil.

A. Declararea
Dac dorim s declarm o variabil ref de tip referin care n timpul prelucrrii va
conine adresa unei variabile dinamice de tipul tip_variabil_dinamic, declaraia are
urmtoarea form general:

type Tipref = ^tip_variabil_dinamic;
tip_variabil_dinamic =...
var ref:Tipref;

Exemplu
type TipTablou=array[1..100,1..100] of Real;
Tipref1=^Integer;
Tipref2=^TipTablou;
var a,b:Tipref1;
reftab:Tipref2;

S observm c n seciunea var s-au declarat doar variabilele de tip referin. De-
claraia tipului Tipref1 se citete n felul urmtor: pointerul de tipul Tipref1 va
conine adresa unei variabile de tip Integer (alocat n heap).
Tipurile pointerilor sunt diferite n funcie de tipul variabilei dinamice referite. De
exemplu, pointerii a i reftab din exemplul precedent sunt de tipuri diferite, iar a i
b au acelai tip.

B. Atribuirea
Unei variabile de tip pointer i putem atribui valoarea unui alt pointer care are acelai
tip sau putem s-i atribuim valoarea nil. De exemplu: a:=b; b:=nil.
Amintim c pointerii pot primi valoare i altfel, n momentul crerii unei variabile
dinamice n heap, prin intermediul procedurii predefinite New(ref), unde ref este o
variabil de tip referin.

C. Compararea pointerilor
Dou variabile de tip referin pot fi comparate cu ajutorul operatorilor relaionali =
(egal) i <> (diferit). Rezultatul este o constant logic (adevrat sau fals).
Operaii, precum citirea i scrierea unui pointer nu sunt posibile n limbajul Pascal.
De asemenea, n Pascal nu putem avea operand pointer ntr-o expresie aritmetic.
9. Alocarea dinamic 241

9.5.2. Variabile dinamice

A. Referirea variabilei dinamice prin intermediul pointerului
Variabila dinamic, neavnd nume propriu, se refer folosind variabila de tip pointer
ntr-o construcie de forma ^variabil_de_tip_referin.
La un moment dat, n timpul prelucrrii, un pointer poate referi o singur variabil
dinamic, n schimb o variabil dinamic poate fi referit de mai muli pointeri. n fi-
gura urmtoare avem o variabil dinamic referit att de p ct i de q, ceea ce nseam-
n c variabila dinamic poate fi numit att p^ ct i q^, deoarece valorile acestora
sunt egale i conin adresa variabilei dinamice.

p p^ (sau q^)
q


B. Crearea unei variabile dinamice
Am amintit deja c spaiul necesar unei variabile dinamice se aloc prin intermediul
procedurii predefinite New(ref). Aceast alocare are loc n timpul execuiei progra-
mului. n urma apelului mai nti se caut un spaiu liber n heap, suficient de mare
pentru a cuprinde o variabil dinamic de tipul specificat n declararea variabilei ref.
Dac un astfel de spaiu liber exist, atunci adresa de nceput al acestuia se reine n
variabila ref i se trece, mpreun cu lungimea spaiului alocat, n lista adreselor ocu-
pate. Zona respectiv poate fi ocupat de valoarea variabilei dinamice ref^. Valoarea
variabilei dinamice poate fi citit sau calculat similar variabilelor statice, cu condiia
s ne referim la ea cu construcia ref^. Dac un astfel de spaiu nu exist (heapul este
ocupat n ntregime sau este mult prea fragmentat), execuia programului se ntrerupe
cu mesajul Heap overflow error.
n astfel de situaii mai nti vom verifica dimensiunea structurilor de date, deoare-
ce este posibil c ntr-adevr este nevoie de mai mult spaiu. n acest caz se poate n-
cerca mrirea dimensiunii heapului din mediul Borland Pascal-ului pn la valoarea
maxim de 655360 B. Dac mesajul de eroare nu se justific prin dimensiunea datelor
alocate n heap, trebuie s cutm greeala n algoritm sau n implementarea lui.

C. Anularea unei variabile dinamice
Prin anulare nelegem operaia prin care eliberm spaiul alocat unei variabile dinami-
ce. Coninutul acestui spaiu nu se terge fizic, n schimb la o eventual alocare nou
este foarte probabil c n acel spaiu se rezerv loc altor variabile dinamice, ca atare
valorile respective se vor suprascrie cu valorile variabilelor alocate ultima dat.

242 9. Alocarea dinamic

Procedura predefinit cu care realizm eliberarea unui spaiu alocat unei variabile
dinamice referite de pointerul ref este Dispose(ref). Dac valoarea pointerului
ref este nil, apelul procedurii va conduce la mesajul de eroare Invalid pointer
operation.

n limbajul Pascal exist nc o modalitate de a elibera spaiul alocat n heap. Dac
marcm un moment n timpul executrii programului cu procedura Mark(point) i
mai trziu apelm procedura Release(point), putem elibera ntreg spaiul alocat de
ctre program ntre momentul marcrii i apelul procedurii Release(point). Aici
pointerul point este de tip Pointer, care este un tip referin predefinit n unit-ul
System al mediului Borland Pascal. Acest tip este compatibil cu toate tipurile referin
declarate n program, deci procedura Release(point) va elibera spaiile ocupate din
heap, indiferent de tipul pointerilor utilizai.
n cazul unor incertitudini pot fi utile funciile MemAvail i MaxAvail. Prima re-
turneaz dimensiunea total a spaiului liber din heap, a doua returneaz dimensiunea
spaiului liber contiguu. Aceast valoare difer de prima datorit faptului c n urma
diferitelor alocri i eliberri de spaiu, heapul se poate frmia. Astfel putem afla di-
mensiunea maxim a unei variabile dinamice care se poate aloca ntr-un moment dat.

9.5.3. Implementarea dinamic a stivelor

A. Declararea stivei
S considerm o stiv n care elementele sunt numere ntregi. Declararea o realizm
recursiv, astfel nct s se oglindeasc nlnuirea. Variabilele dinamice vor avea tipul
record, deoarece n fiecare vom reine valoarea numrului (informaia propriu-zis)
i valoarea adresei (informaia de legtur) urmtorului element de pe stiv (cel care se
afl imediat sub primul element).

type Stiva=^Element; { tipul pointerului }
Element=record { tipul variabilei dinamice }
nr:Integer;
leg:Stiva
end;
var cap,p:Stiva; { cap va fi pointerul ctre primul element din stiv }

B. Inserarea unui element nou ntr-o stiv alocat dinamic
n stiv orice adugare se face la nceput, astfel nct pointerul cap va indica ntotdea-
una elementul care se vede, adic ultimul inserat, respectiv primul care se poate eli-
mina. Secvena de program cu care realizm o astfel de adugare este urmtoarea:

9. Alocarea dinamic 243

procedure InserareStiva(var cap:Stiva; ce:Integer);
{ ce conine valoarea care se adaug n stiva referit de pointerul cap }
var p:Stiva; { p este un pointer ajuttor }
begin
New(p); { 1 }
p^.nr:=ce; { 2 }
p^.leg:=cap; { 3 }
cap:=p { 4 }
end;

Exemplu
S presupunem c n momentul apelrii subprogramului de inserare stiva are urm-
torul coninut:


cap
9 8 2


n urma apelului procedurii de inserare mai nti se aloc spaiu variabilei dinamice
p^ (instruciunea marcat cu {1}). Deocamdat nici un cmp de-al su nu a primit nici
o valoare:

cap
9 8 2
p
{1}
p^


La pasul {2} i {3} se atribuie valori cmpurilor variabilei dinamice p^. Fie ce = 7.
Deoarece noul element se insereaz n vrful stivei, cmpul de legtur a variabile di-
namice p^ va indica acelai element pe care l indic pointerul cap.


cap
9 8 2
p
{2-3} 7
p^


Pasul {4} finalizeaz operaia prin actualizarea pointerului cap, astfel nct acesta
s indice ultimul element inserat:
244 9. Alocarea dinamic


cap
9 8 2
p
{4}
7
p^


C. Crearea unei stive alocate dinamic
Dac dorim s crem o stiv, vom apela n mod repetat subprogramul de inserare ntr-o
instruciune repetitiv. Capul stivei trebuie iniializat anterior apelului cu valoarea nil.

D. Eliminarea unui element dintr-o stiv alocat dinamic
Dintr-o stiv se va elimina ntotdeauna elementul referit de capul stivei. Urmtorul
subprogram realizeaz eliminarea acestui element i elibereaz spaiul alocat lui. n
parametrul ce vom pstra informaia propriu-zis din elementul eliminat din stiv.
Este important ca un astfel de subprogram s se apeleze doar dup ce n prealabil
ne-am asigurat c stiva nu este vid.

procedure EliminareStiva(var cap:Stiva; var ce:Integer);
{ ce va conine valoarea care a existat n vrful stivei referite de pointerul cap }
var p:Stiva; { p este un pointer ajuttor }
begin
ce:=cap^.nr; { 1 }
p:=cap; { 2 }
cap:=cap^.leg; { 3 }
Dispose(p) { 4 }
end;

Exemplu
Fie stiva:

cap
9 8 2

S presupunem c din aceasta dorim s eliminm un element. Paii subprogramului
de eliminare se pot urmri n figurile urmtoare:


cap
9 8 2
p
{1-2}
ce = 8
9. Alocarea dinamic 245


cap
9 8 2
p
{3-4}


Spaiul haurat rmne deocamdat ocupat de elementul aflat acolo, dar adresa
acestuia se returneaz sistemului de gestiune a heap-ului care la o cerere viitoare poate
aloca acest spaiu unei alte variabile. Capul stivei acum indic elementul care conine
numrul 9.

Observaie
Operaia de cutare, de inserare sau de eliminare n/din alt parte dect n/din vr-
ful stivei nu au sens, deoarece contravin definiiei acestei structuri de date. De aseme-
nea, afiarea coninutului stivei se consider ca fiind o prelucrare n care se va afia
elementul din vrf, apoi acesta se elimin, astfel se va vedea urmtorul element i se
va afia etc.

9.5.4. Implementarea dinamic a cozilor

A. Declararea cozii
Declararea cozii este identic cu cea a stivei, dar n afar de pointerul care refer capul
cozii vom avea nevoie de nc unul care s refere ultimul element, deoarece inserarea
se va face dup ultimul element. Eliminarea din coad este identic cu eliminarea din
stiv, deoarece i din cozi se elimin elementul din vrf.

B. Inserarea unui element nou ntr-o coad alocat dinamic
n coad orice adugare se face dup ultimul element inserat, astfel nct pointerul ult
va indica ntotdeauna ultimul element inserat. Totodat pointerul cap va indica primul
element care se poate elimina. Secvena de program corespunztoare este urmtoarea:

procedure InserareCoada(var cap,ult:Coada; ce:Integer);
{ ce conine valoarea care se adaug n coada referit de pointerul cap }
var p:Coada; { p este un pointer ajuttor }
begin
New(p); { 1 }
p^.nr:=ce; { 2 }
p^.leg:=nil; { 3 }
ult^.leg:=p { 4 }
ult:=p { 5 }
end;
246 9. Alocarea dinamic

Exemplu
S presupunem c n momentul apelrii subprogramului de inserare coada are ur-
mtorul coninut:

cap
4 7 1
ult


n urma apelului procedurii de inserare mai nti {1} se aloc spaiu variabilei dina-
mice p^. Deocamdat nici un cmp de-al su nu a primit nici o valoare.


cap
9 8 2
p
{1}
p^
ult

La paii {2-3} se atribuie valori cmpurilor variabilei dinamice p^. Fie ce = 7. De-
oarece noul element se insereaz la sfritul cozii, cmpul de legtur a variabile dina-
mice p^ va primi valoarea nil.

cap
9 8 2
p
{2-3} 7
p^
ult

La pasul {4} se leag variabila dinamic p^ dup ultimul element al cozii:


cap
9 8 2
p
{4} 7
p^
ult

n final se actualizeaz pointerul ult astfel nct s indice ultimul element inserat:

7
cap
9 8 2
p
{5}
ult^
ult

9. Alocarea dinamic 247

C. Crearea unei cozi alocate dinamic
Dac dorim s crem o coad, vom apela n mod repetat subprogramul de inserare
ntr-o instruciune repetitiv. Pentru a realiza fiecare inserare, deci i inserarea primu-
lui element, avem nevoie de un ultim element, dup care se realizeaz inserarea. Re-
zult c iniializarea cozii nseamn crearea mai nti a unui element care s fie i pri-
mul i ultimul.

procedure InitCoada(var cap,ult:Coada; ce:Integer);
{ crem o coad format dintr-un singur element care conine valoarea ce }
begin
New(cap);
cap^.nr:=ce;
cap^.leg:=nil;
ult:=cap
end;

Faptul c primul element trebuie inserat altfel dect restul elementelor constituie un
oarecare inconvenient care poate fi nlturat prin introducerea n coad a unui element
fictiv, numit frecvent santinel. Acest element se creeaz nainte de apelul subprogra-
mului de creare propriu-zis a cozii:

...
New(santinela);
santinela^.leg:=nil;
ult:=santinela
...

Coada vid astfel creat conine doar acest element fictiv:


santinela
ult
santinela^ = ult^

Crearea cozii n acest caz se realizeaz cu subprogramul InserareCoada apelat
cu parametri actuali santinela, ult i ce.

D. Eliminarea unui element dintr-o coad alocat dinamic
Dintr-o coad se va elimina ntotdeauna elementul referit de capul cozii. Subprogra-
mul care realizeaz eliminarea acestui element i elibereaz spaiul alocat lui este
identic cu subprogramul de eliminare din stiv.

248 9. Alocarea dinamic

Eliminarea elementului n cazul n care am lucrat cu santinel, trebuie s in cont
de faptul c primul element din coad este santinela. n concluzie se va elimina varia-
bila dinamic santinela^.leg^. Procedura Eliminare2Coada va fi apelat doar dac
santinela^.leg nu este nil, adic n cazul n care avem cel puin un element n coad
n afara santinelei.

procedure Eliminare2Coada(santinela,ult,ce);
var p:Coada;
begin
p:=santinela; { 1 }
{ elementul care trebuie eliminat devine santinel }
santinela:=santinela^.urm; { 2 }
Dispose(p) { 3 se elimin santinela veche }
end;

Exemplu
Fie coada:

santinela
7 1
ult


S presupunem c din aceasta dorim s eliminm un element. Paii subprogramului
de eliminare se pot urmri n figura urmtoare:

7 1
ult
p
santinela
{1}
{2}
{3}


Observaie
Operaii de cutare, inserare sau eliminare n/din alt parte dect n/de la vrful/
sfritul cozii nu au sens, deoarece contravin definiiei acestei structuri de date. De
asemenea, afiarea coninutului cozii se consider ca fiind o prelucrare n care se va
afia elementul din vrf, apoi acesta se elimin, astfel se va vedea urmtorul element,
care se va afia etc.

9.5.5. Implementarea listelor liniare simplu nlnuite
O list simplu nlnuit poate fi creat similar stivelor sau cozilor, dar pot aprea i
situaii n care inserarea trebuie fcut undeva n interiorul listei ntre dou elemente
existente. La fel, i eliminarea se poate realiza de oriunde din list. Aceste operaii au
9. Alocarea dinamic 249

sens dac lista conine elementele ntr-o anumit ordine, unde se poate cuta, de exem-
plu, elementul dup care (sau eventual n faa cruia) vrem s inserm elementul nou.
De asemenea, eliminrile se realizeaz nu neaprat dintr-un capt al listei, ci se poate
elimina elementul avnd o anumit proprietate, eventual cel din faa unui element sau
aflat dup un anumit element.

A. Cutarea unui element ntr-o list simplu nlnuit, ordonat cresctor
Cutarea n liste alocate dinamic se realizeaz secvenial, chiar dac acestea sunt ordo-
nate. Dac lista este neordonat algoritmul se termin fie n momentul n care am gsit
elementul cutat, fie dup ce am verificat i ultimul element:

function Cauta(prim:list; cautat:Integer):Boolean;
var p:list; { list: tipul referin }
gasit:Boolean;
begin
p:=prim; { primul element este indicat de pointerul prim }
gasit:=false; { gasit: variabil auxiliar }
while not gasit and (p <> nil) do
if p^.info = cautat then { se caut elementul avnd valoarea cautat }
gasit:=true
else
p:=p^.leg;
Cauta:=gasit
end;

Dac avem o list n care exist un cmp cheie, valorile cruia sunt ordonate cres-
ctor sau descresctor n cadrul succesiunii elementelor, cutarea se va face de aseme-
nea secvenial, dar algoritmul se termin fie cnd am gsit elementul cutat, fie cnd
ne-am dat seama c am depit locul unde ar fi trebuit s se afle acesta, deci nu mai
are rost s-l cutm. n acest caz, algoritmul de cutare este urmtorul:

function Cauta(prim:list; cautat:Integer):Boolean;
var p:list;
gasit:Boolean;
begin
p:=prim;
gasit:=false;
while not gasit and (p <> nil) do
if p^.info=cautat then gasit:=true
else
if p^.info < cautat then p:=p^.leg
else Break; { echivalent aici cu p:=nil; }
Cauta:=gasit
end;
250 9. Alocarea dinamic

B. Inserarea unui element ntr-o list simplu nlnuit, n faa elementului
referit de pointerul p
S considerm cazul n care dorim s construim o list n care elementele s fie ordo-
nate cresctor dup cmpul cheie info. Avnd n vedere c pe parcursul inserrii ele-
mentelor va trebui s cutm locul fiecruia, vom avea nevoie de un subprogram care
s returneze, de exemplu, valoarea pointerului p, care refer elementul n faa cruia
dorim s inserm noul element. Locul acestui element nou poate fi n faa primului
element, n faa unuia n interiorul listei, dar este posibil ca noul element s trebuiasc
pus ultimul n list, caz n care valoarea lui p este nil. Observm c n aceste condiii,
pe parcursul crerii listei vom fi nevoii s inem evidena att a pointerului care indic
primul element, ct i referina la ultimul.

Algoritmul de cutare a elementului n faa cruia se va insera noul element se poa-
te implementa n Pascal cu funcia Cauta. Variabila auxiliar gsit ne ajut s evitm
o expresie logic (n instruciunea while) n care s verificm coninutul unui cmp
dintr-o variabil dinamic inexistent (dac p este egal cu nil, p^.info nu exist).

function Cauta(prim:list; cautat:Integer):list;
{ returneaz pointerul p care indic elementul n faa cruia se face inserarea }
var p:list;
gasit:Boolean;
begin
p:=prim;
gasit:=false;
while not gasit and (p <> nil) do
if cautat>=p^.info then
p:=p^.leg
else
gasit:=true;
Cauta:=p
end;

Inserarea elementului se realizeaz n funcie de valoarea pointerului returnat de
funcia Cauta.
1. Dac acesta este egal cu prim, noul element se insereaz n vrful listei (inserare
ca n stive) i pointerul care l indic devine noul prim.
2. Dac valoarea pointerului returnat este egal cu nil, noul element se insereaz la
sfritul listei (inserare ca n cozi) i pointerul care l indic devine noul ultim.
3. Dac valoarea pointerului returnat nu este egal nici cu prim, nici cu ultim, avem o
inserare ntre dou elemente n interiorul listei.

9. Alocarea dinamic 251

Problema care apare se datoreaz faptului c aparent degeaba cunoatem pointerul
la elementul n faa cruia dorim s inserm noul element, deoarece nu mai cunoatem
pe cel dup care va urma acesta i n concluzie, nu avem cum s legm elementul nou
de prima parte a listei. n acest caz inserarea se realizeaz cu urmtoarea secven de
instruciuni:

...
New(q); {1}
q^:=p^; {2}
p^.info:=ce; {3}
p^.leg:=q; {4}
if p=ultim then { 5 dac am inserat n faa lui ultim }
ultim:=q;
...

Exemplu
Fie lista n momentul n care vrem s inserm valoarea ce = 5. Funcia Cauta a re-
turnat valoarea pointerului p care indic elementul n faa cruia vrem s-l punem (ca-
re are valoarea cmpului info egal cu 8).

prim
8 3 11
ult p


S urmrim modul n care acioneaz instruciunile din secvena de mai sus. La pri-
mul pas se aloc spaiu pentru o variabil dinamic nou, care se va referi de pointerul q:

q
{1}
q^
prim
8 3 11
ult p

La pasul urmtor copiem coninutul variabilei dinamice referite de p n variabila in-
dicat de q (se copiaz toate cmpurile):

q
{2}
q^
prim
8 3 11
ult p
8

252 9. Alocarea dinamic

Astfel am eliberat elementul indicat de p (am salvat valoarea 8 n elementul in-
dicat de q) i atribuim cmpului info din variabila dinamic p^ valoarea 5. De aseme-
nea, legm elementul indicat de p de elementul referit de q:

q
{3-4}
q^
prim
5 3 11
ult p
8

Pasul {5} este necesar, deoarece se observ c pe parcursul procesului descris am
mutat elementul n faa cruia am realizat inserarea din locaia unde a fost pstrat
ntr-o locaie nou. Dac acest element, a fost ultimul, (deci pointerul ultim coninea
adresa lui), acum, mutndu-l, trebuie s actualizm i valoarea pointerului ultim. Aces-
ta ar fi fost cazul, dac am fi dorit s inserm un element nou avnd valoarea 10.
Locul acestuia ar fi fost n faa elementului avnd valoarea cmpului info egal cu 11.
Crend un element nou pentru 11, practic s-ar fi creat un nou ultim element.

C. Inserarea unui element ntr-o list simplu nlnuit, dup elementul
referit de p
Dac avem o funcie Cauta care returneaz valoarea pointerului dup care se dorete
inserarea, secvena de program este mai simpl, n schimb cutarea este uor mai com-
plicat. De exemplu, dac primul element din list conine valoarea 1 i vrem s gsim
locul lui 0, nu exist acel element dup care 0 trebuie inserat. n concluzie acest caz l
tratm separat i atribuim pointerului returnat valoarea nil, care va nsemna c noul
element va fi noul prim.
n structura repetitiv vom sri peste elementul indicat de pointerul prim, deoarece
acesta a fost deja testat. Astfel vom verifica valoarea variabilei p^.leg^.info. n aceste
condiii va trebui s asigurm ieirea din structura repetitiv n momentul n care am
verificat i ultimul element, i s nu verificm valoarea cmpului info a unui element
inexistent. (Bineneles, exist i alte modaliti de a rezolva problema.)

function Cauta(prim:list; cautat:Integer):list;
{ returneaz pointerul p care indic elementul dup care se face inserarea }
var p:list;
begin
if prim^.info > cautat then
p:=nil { vom avea un nou prim }
else begin
p:=prim; { prim va fi srit; l-am verificat deja }
while p^.leg^.info<cautat do begin
p:=p^.leg;
9. Alocarea dinamic 253

if p^.leg = nil then { p^.leg^.info nu exist }
Break
end
end;
Cauta:=p
end;

Secvena de program cu care inserm un element ntre dou elemente din list,
atunci cnd se cunoate pointerul elementului dup care se dorete inserarea:

...
New(q); {1}
q^.info:=ce; {2}
q^.leg:=p^.leg; {3}
p^.leg:=q {4}
...

Exemplu
Fie lista n momentul n care vrem s inserm valoarea ce = 5. Funcia Cauta a re-
turnat valoarea pointerului p care indic elementul dup care vrem s-l punem (care
are valoarea cmpului info egal cu 3).

prim
8 3 11
ult
p

S urmrim modul n care acioneaz instruciunile din secvena de mai sus. La pri-
mul pas se aloc spaiu pentru o variabil dinamic nou, care se va referi de pointerul q:

prim
8 3 11
ult
p
q
{1}
q^

Acum copiem valoarea variabilei ce n variabila indicat de q, iar cmpul de leg-
tur al acestui element va indica elementul care urmeaz dup cel indicat de p:

prim
8 3 11
ult
p
q
{2-3}
q^
5

254 9. Alocarea dinamic

Acum trebuie s legm elementul nou creat de prima parte a listei, adic s modifi-
cm cmpul de legtur a variabilei dinamice p^:

prim
8 3 11
ult
p
q
{4}
q^
5


D. Eliminarea elementului referit de pointerul p
Presupunem c pointerul p conine referina la elementul pe care trebuie s-l eliminm
din list. O astfel de eliminare se poate realiza n felul urmtor:

procedure Sterge(var prim,ultim:list; p:list);
var q:list;
begin
if p <> ultim then begin { dac suntem n interiorul listei }
q:=p^.leg; { asigurm accesul la elementul care va fi ters }
p^:=p^.leg^; { peste elementul care trebuie ters l copiem pe urmtorul }
if q = ultim then { este posibil s tergem de fapt ultimul element }
ultim:=p; { actualizm pointerul ultim }
Dispose(q) { tergem vechiul urmtor }
end else begin
{ dac avem mai multe elemente, i trebuie ters ultimul }
if prim <> ultim then begin
q:=prim;
while q^.leg <> ultim do { l cutm pe penultimul }
q:=q^.leg;
q^.leg:=nil; { actualizm vechiul penultim care va fi noul ultim }
ultim:=q; { actualizm pointerul ultim }
Dispose(p) { eliberm spaiul referit de p }
end else begin { avem un singur element n list }
Dispose(prim);
prim:=nil;
end
end
end;

E. Traversarea listelor simplu nlnuite
Traversarea se realizeaz cu un subprogram simplu, n care trecem de la element la
element, urmnd nlnuirea, pn la elementul care n cmpul de legtur are nil.
9. Alocarea dinamic 255

procedure Afiseaza(prim:list);
var p:list;
begin
p:=prim;
while p <> nil do begin
WriteLn(p^.info);
p:=p^.leg
end
end;

9.5.6. Liste simplu nlnuite implementate cu santinele
Am vzut c realizarea operaiilor de adugare, respectiv eliminare se realizeaz ane-
voios datorit faptului c n funcie de poziia elementului, algoritmii se ramific n
funcie de o mulime de cazuri. Dac lista se implementeaz cu santinele (unul la nce-
put i unul la sfrit, operaiile se pot realiza mult mai uor, deoarece dispar cazurile
de inserare n faa primului element, respectiv dup ultimul precum tergerea ultimului
sau penultimului.

A. Inserarea unui element ntr-o list simplu nlnuit, n faa elementului
referit de pointerul p
n cazul unei implementri cu dou santinele lista vid are urmtoarea structur:

sant1
sant2
sant2^


Presupunem c dorim s crem o list ordonat cresctor. Lista vid se creeaz cu
secvena de instruciuni:

...
New(sant1); New(sant2);
sant2^.info:=MaxInt; { o valoare mare, astfel nct celelalte s fie mai mici }
sant1^.leg:=sant2;
...

Exemplu
Fie lista:

sant1
sant2
2 8



Dorim s inserm un element nou pentru valoarea 5 i avem la dispoziie un pointer
p care indic elementul care n cmpul info are valoarea 8:
256 9. Alocarea dinamic


sant1
sant2
2 8
p

La pasul urmtor alocm spaiu noului element:


sant1
sant2
2 8
q
{1}
q^
p


Deoarece avem la dispoziie adresa unui element care este dup locul n care dorim
inserarea, mai nti vom copia acest element n q^:


sant1
sant2
2
q
{2}
q^
p

8
8

La ultimul pas introducem valoarea nou n elementul p^ i legm noul element de
prima parte a listei:


sant1
sant2
2
q
{3-4}
q^
p

8
5

Folosind santinela, este posibil ca noul element s trebuiasc inserat n faa santine-
lei sant2. n acest caz, n q^ vom copia variabila dinamic sant2^, deci va trebui s ac-
tualizm valoarea acestuia {5}.
Secvena de program cu care realizm inserarea noului element este urmtoarea:

...
New(q); {1}
p^:=q^; {2}
p^.info:=ce; {3}
p^.leg:=q; {4}
if p=sant2 then sant2:=q; {5}
...
9. Alocarea dinamic 257

B. Inserarea unui element ntr-o list simplu nlnuit, dup elementul
referit de p
Fie o list n care cheile sunt ordonate cresctor, avnd urmtorul coninut:

sant1
sant2
2 8

Dorim s inserm un element nou avnd valoarea 5. Adugm elementul nou cu ur-
mtoarea secven de program:

...
New(q); {1}
q^.info:=ce; {2}
q^.leg:=p^.leg; {3}
p^.leg:=q; {4}
...

S urmrim efectul secvenei de instruciuni pe figurile urmtoare. Pointerul p indi-
c elementul care conine valoarea 2, dup care dorim s inserm valoarea 5. La pri-
mul pas se aloc spaiu pentru o variabil dinamic nou q^:

sant1
sant2
2 8
q
{1}
q^
p


Urmeaz ncrcarea cmpurilor variabilei q^:

sant1
sant2
2 8
q
{2-3}
q^
p

5


La pasul urmtor legm prima parte a listei de elementul nou creat:

sant1
sant2
2 8
q
{4}
q^
p

5


258 9. Alocarea dinamic

D. Eliminarea elementului referit de pointerul p
Atunci cnd vrem s eliminm elementul p^, practic trebuie s legm elementul aflat
n faa lui de cel care urmeaz dup el. Vom proceda similar cu adugarea, respectiv
cu eliminarea din liste simplu nlnuite i anume copiem n p^, elementul p^.leg^ i
eliberm zona de memorie alocat acestuia din urm:

...
q:=p^.leg; { pstrm adresa zonei care se va elibera }
p^:=q^; { copiem elementul urmtor n elementul care trebuie eliminat }
if q=sant2 then { dac am copiat sant2, actualizm valoarea acestei santinele }
sant2:=p;
Dispose(q); { eliberm spaiul ocupat }
...

E. Traversarea listelor simplu nlnuite
Traversarea se realizeaz ca n cazul listelor implementate fr santinele, dar n acest
caz nu se traverseaz santinelele:

procedure Afiseaza(sant1,sant2:list);
var p:list;
begin
p:=sant1^leg;
while p <> sant2 do begin
WriteLn(p^.info);
p:=p^.leg
end
end;

9.5.7. Liste dublu nlnuite
Dac lista este astfel construit nct nlnuirea este pstrat n informaii de legtur
care rein att adresa elementului precedent, ct i al succesorului, avem liste dublu n-
lnuite. Din nou accentum avantajul utilizrii santinelelor, astfel nemaifiind necesar
tratarea separat a cazurilor cnd inserarea, respectiv eliminarea se face la nceputul
sau la sfritul listei.
Iniializarea listei ncepe cu crearea unei liste vide care conine doar cele dou san-
tinele:

...
New(sant1);
New(sant2);
sant2^.pred:=sant1;
sant1^.urm:=sant2;
...
9. Alocarea dinamic 259

A. Declararea listei dublu nlnuite
n declararea listei dublu nlnuite, pe lng cmpurile care cuprind informaiile pro-
priu-zise, vom specifica dou cmpuri de legtur: a predecesorului i a succesorului
(pred i urm).

B. Inserarea unui element nou ntr-o list dublu nlnuit
S presupunem c n pointerul p avem la dispoziie adresa elementului n faa cruia
trebuie s inserm noul element.
Secvena de program este urmtoarea:

...
New(q);
q^.info:=...
q^.urm:=p;
q^.pred:=p^.pred;
p^.pred^.urm:=q;
p^.pred:=q;
...

C. Eliminarea unui element nou ntr-o list dublu nlnuit
Avnd la dispoziie att legtura ctre elementul predecesor ct i ctre cel urmtor,
eliminarea elementului referit de pointerul p se realizeaz simplu:

...
p^.pred^.urm:=p^.urm;
p^.urm^.pred:=p^.pred;
Dispose(p);
...

D. Traversarea listelor dublu nlnuite
Traversarea listelor dublu nlnuite se realizeaz similar cu cea a listelor simplu nln-
uite, cu precizarea c acestea se pot parcurge de la primul element spre ultimul, ur-
mnd nlnuirea din cmpul urm, sau de la ultimul spre primul, conform legturii din
cmpul pred.

9.5.8. Liste circulare
Dac ntr-o list simplu sau dublu nlnuit cmpul de legtur a ultimului element
indic primul, avem list circular. n cazul listelor dublu nlnuite, n plus, cmpul
pred al primului element va indica ultimul element.

260 9. Alocarea dinamic

9.6. Structuri arborescente

9.6.1 Reprezentarea structurilor arborescente

A. Reprezentarea arborilor binari
Arborele binar este o structur de date care permite o descriere recursiv. Am putea da
o descriere intuitiv n felul urmtor:

type arbore = nil or
record
info:T;
stng,drept:arbore
end;

adic un arbore este fie vid, fie este format dintr-un nod care conine informaii de tip
T i din dou alte componente care la rndul lor sunt arbori i anume subarborele
stng, respectiv cel drept.
O astfel de descriere recursiv este incomod n cadrul oricrui limbaj de progra-
mare. n limbajele de tip Pascal descrierea arborilor se realizeaz n felul urmtor:

type ref=^varf;
varf=record
info:T;
stng,drept:ref
end;

Aceast descriere corespunde reprezentrii arborelui cu referine descendente (ctre
fiii si).

B. Reprezentarea arborilor pe suportul de ieire (vizualizarea)
n practic, pot s apar situaii n care o structur arborescent trebuie vizualizat pe
un suport de ieire. De regul se prefer afiarea cu indentare. Fie arborele din figura
urmtoare:


A
B C
D F G E
I H J K

9. Alocarea dinamic 261

Afind acest arbore cu indentare, l obinem sub una din urmtoarele trei forme:

Preordine Inordine Postordine
A D D
B B H
D H I
E E E
H I B
I A F
C F J
F C K
G J G
J G C
K K A

C. Reprezentarea pe suportul de intrare (citirea arborelui)
Introducerea unei structuri arborescente n memorie este o problem care se rezolv n
general mai uor dect vizualizarea. Dac arborele este binar, una dintre posibiliti
este aceea de a citi informaiile ataate nodurilor n preordine; lipsa subarborelui stng,
respectiv drept se semnaleaz printr-o valoare special, aleas n funcie de tipul chei-
lor ataate nodurilor.
n scopul introducerii unui arbore oarecare n memorie, se pot citi informaiile ata-
ate nodurilor n postordine, pentru fiecare nod aceste informaii fiind nsoite de nu-
mrul descendenilor nodului respectiv.

9.6.2. Operaii elementare pe arbori
Se consider operaii elementare pe structuri arborescente: construirea, traversarea
arborelui i cutarea, inserarea i tergerea unor elemente ntr-o structur dat. Dat
fiind faptul c n scopul sortrii unei mulimi s-au dovedit a fi utile structurile arbores-
cente, n cele ce urmeaz vom aminti i operaia de sortare.

A. Construirea unui arbore binar total echilibrat
Un arbore binar total echilibrat are toate vrfurile terminale pe ultimele dou niveluri
astfel nct, pentru orice nod, numrul nodurilor din subarborele stng difer cel mult
cu unu de numrul nodurilor din subarborele drept.
Procedeul de construire este urmtorul:
1. un vrf va fi rdcin;
2. se pregtesc ns = [n/2] vrfuri pentru subarborele stng i se trece la construirea lui
(pasul 1);
3. se pregtesc nd = n ns 1 vrfuri pentru subarborele drept i se trece la construi-
rea lui (pasul 1);
262 9. Alocarea dinamic

Funcia Arbore(n) realizeaz construirea unui arbore binar total echilibrat (func-
ia returneaz pointer la rdcina arborelui).

function Arbore(n):ref; { generare arbore }
var varfnou:ref;
x,ns,nd:Integer;
begin
if n = 0 then
Arbore:=nil { nici un nod arbore vid }
else begin
ns:=n div 2 { numrul nodurilor din subarborele stng }
nd:=n-ns-1 { respectiv din cel drept }
New(varfnou) { creare nod curent }
with varfnou^ do begin
Read(f,cod) { informaia propriu-zis pentru nodul nou }
stang:=arbore(ns) { construire subarbore stng }
drept:=arbore(nd) { respectiv drept }
end;
Arbore:=varfnou
end
end;

B. Traversarea arborilor binari
Traversarea unui arbore binar const n parcurgerea pe rnd a vrfurilor arborelui i
punerea n eviden a fiecrui nod la numai una din atingerile sale (spunem c ,,vizi-
tm vrful respectiv, prin aceasta nelegnd efectuarea unor prelucrri ale informaii-
lor ataate vrfului, prelucrri care pot fi oricnd inserate n algoritmul traversrii).
Exist dou posibiliti de parcurgere a arborilor: n adncime i n lime. Cea mai fo-
losit este cea n adncime, care la rndul ei poate fi de trei feluri, cunoscute sub denu-
mirile de parcurgere n inordine, preordine i postordine.
Aceste denumiri sugereaz poziia rdcinii (n traversare) fa de cei doi descen-
deni (respectiv ntre ei, naintea lor sau dup ei). Algoritmul de traversare este des uti-
lizat, deoarece prelucrrile care vizeaz toate datele unei structuri arborescente necesi-
t un drum prin structur, astfel nct fiecare nod s fie vizitat o singur dat.

1. Traversarea n adncime
Dat fiind faptul c cele trei tipuri de traversri n adncime se realizeaz pe baza a trei
algoritmi asemntori, vom prezenta doar algoritmul recursiv corespunztor traversrii
n preordine:
1. se viziteaz rdcina;
2. se traverseaz subarborele stng;
3. se traverseaz subarborele drept.
9. Alocarea dinamic 263

Implementarea n Pascal a subalgoritmilor de traversare o prezentm realiznd tot-
odat i o afiare a arborelui binar cu indentare pentru cele trei tipuri de parcurgere.
Procedura Traversare(modtrav) realizeaz traversarea arborelui binar n toate ce-
le trei moduri de traversare n adncime. n limbajul Pascal, parametrul modtrav este
de tip enumerat i are valorile preordine, inordine i postordine.

procedure Afiseaza(nod:ref; nivel:Integer);
begin { tiprirea nodului cu indentare }
while nivel > 0 do begin { indentare n funcie de nivelul nodului }
Write(g,' ');
Dec(nivel)
end;
WriteLn(g,nod^.cod) { informaia propriu-zis ataat nodului }
end;

procedure Travers(modt:trav; rad:ref; nivel:Integer);
begin { traversarea propriu-zis }
if rad <> nil then begin
if modt = preordine then
Afiseaza(rad,nivel);
Travers(modt,rad^.stang,nivel+1);
if modt = inordine then
Afiseaza(rad,nivel);
Travers(modt,rad^.drept,nivel+1);
if modt = postordine then
Afiseaza(rad,nivel)
end
end;

procedure Traversare(modtrav:trav); { traversare n adncime }
begin
Travers(modtrav,radacina,0)
end;

2. Traversarea nerecursiv
Evident, se poate obine uor o variant de traversare nerecursiv, pornind de la o vari-
ant recursiv i ridicnd recursivitatea printr-una din metodele cunoscute, de exemplu
utiliznd o stiv.
Orice traversare necesit o zon de memorie suplimentar n care se pstreaz anu-
mite informaii necesare traversrii. Dac aceast zon crete odat cu creterea com-
plexitii structurii (cum este cazul stivei), apare pericolul de a nu putea traversa un ar-
bore existent deja n memorie din cauza epuizrii la un moment dat a memoriei dispo-
nibile.
264 9. Alocarea dinamic

Vom prezenta un algoritm care necesit pentru traversare o memorie suplimentar
constant, dar care folosete memorie suplimentar n fiecare nod al arborelui.
n subprogramul Traversare_nerecursiva(modtrav) arborele este considerat
ca fiind reprezentat prin referinele ascendente ct i cele descendente ale sale i n
plus, n fiecare nod pstrndu-se un contor care numr de cte ori s-a trecut deja prin
nodul respectiv n decursul traversrii (pentru a putea realiza mai multe traversri suc-
cesive, contorul va fi un numrtor modulo 3, vezi figura).

n subprogramul Traversare_nerecursiva(modtrav)
structura arborelui este definit n felul urmtor:
type tipref = 0.. 2;
ref = ^varf;
varf = record
cod:Integer;
refs:array[tipref] of ref;
1
2
3

indice:0..3
end;

Vectorul refs va conine cele trei referine: una ctre nodul tat, celelalte dou c-
tre fii. Trecerea de la un nod la altul, n traversarea arborelui, este dirijat de valoarea
contorului indice din nodul curent, care n afar de rolul su menionat (numr atin-
gerile) va fi i indice n vectorul de referine refs. Cmpul indice se iniializeaz la
crearea nodului cu valoarea 3, ceea ce permite ca actualizarea referinei la predecesor
(care s-ar face greoi la construire) s se fac la prima vizitare a nodului (se verific
uor c aceast valoare nu mai poate fi atins datorit funciei modulo).

procedure Traversare_nerecursiva(modtrav:trav);
var p,q:ref;
gasit:Boolean;
begin
p:=radacina; { p este nodul curent }
q:=nil; { iar q este tatl nodului curent }
while p <> nil do
with p^ do begin
repeat
if indice = 3 then begin { prima atingere a nodului }
refs[0]:=q; { se actualizeaz referina la tata }
q:=p;
indice:=0
end;
if indice = modtrav then
Write(g,cod:3); { prelucrare nod }
indice:=(indice+1) mod 3;
9. Alocarea dinamic 265

if indice=0 then begin { se urc la tata }
q:=refs[0];
gasit:=true
end else
gasit:=refs[indice] <> nil
until gasit;
p:=refs[indice]
end;
WriteLn(g)
end;

Procedura Traversare_nerecursiva(modtrav) se va apela cu valori diferite
ale parametrului modtrav i anume pentru modtrav = 0 obinem o traversare n
preordine, pentru 1 n inordine, iar pentru 2 n postordine.
Acest algoritm are meritul c este relativ simplu i rapid, dar are dezavantajul c
necesit o memorie suplimentar destul de mare n fiecare nod (referina ascendent i
contorul).

C. Traversarea arborilor oarecare

1. Traversarea pe orizontal
Construirea arborelui se realizeaz n procedura Citeste_arbore prin citirea lui n
modul urmtor: pentru fiecare nod se citete perechea (informaie util, numr descen-
deni). Nodurile sunt citite n postordine i sunt pstrate ntr-o stiv pn cnd apare
nodul ai crui fii sunt. n acest moment fiii sunt scoi din stiv i se actualizeaz refe-
rinele descendente (de la tat la fii) dup care tatl este pus n stiv; n final singurul
nod din stiv va fi rdcina, iar arborele va fi gata construit.
n Pascal structura se descrie n modul urmtor:

type ref=^nod;
fiu=0..max;
nod=record
info:Integer;
refs:array[1..max] of ref;
fii:fiu
end;

unde max este o constant care reprezint numrul maxim de descendeni. Procedura
Traversare_pe_orizontala_arbori_oarecare utilizeaz o coad pentru ps-
trarea nodurilor care urmeaz s fie prelucrate. Iniial se introduce rdcina n coada
vid, dup care se scoate pe rnd cte un nod din coad, introducndu-se descendenii
lui. Se verific uor c acest mecanism asigur ntr-adevr parcurgerea arborelui nivel
dup nivel.
266 9. Alocarea dinamic

procedure Citeste_arbore; { citire arbore n postordine }
const max1=11; { adncimea maxim a stivei de noduri }
var n:Integer; { numr total de noduri }
r:ref;
stiva:array[1..max1] of ref;
ist:0..max1; { indicator de stiv }
fi:fiu;

procedure Push(p:ref); { introducere nod n stiv }
begin
if ist = max1 then WriteLn(g,'Stiva plina!')
else begin
ist:=ist + 1;
stiva[ist]:=p
end
end;

function Pop:ref; { scoatere nod din stiv }
begin
if ist = 0 then
Pop:=nil { arborele poate fi vid }
else begin
Pop:=stiva[ist];
ist:=ist - 1
end
end;

begin { procedura Citeste_arbore }
ist:=0; { iniializare stiv }
ReadLn(f,n); { numr total de noduri }
WriteLn(g,'S-au citit perechile:');
while n > 0 do begin
New(r); { creare nod nou }
with r^ do begin
ReadLn(f,info,fii);
for fi:=fii downto 1 do
refs[fi]:=Pop { se scot descendenii de pe stiv }
end;
Write(g,'(',r^.info:3,',',r^.fii:3,') ');
Push(r); { noul nod se pune pe stiv }
n:=n-1;
if n mod 4=0 then WriteLn(g)
end;
rad:=Pop { singurul nod pe stiv trebuie s fie rdcina arborelui }
end;
9. Alocarea dinamic 267

Traversarea n lime (nivel dup nivel) a unui arbore oarecare (pentru a-l afia) es-
te prezentat n procedura Traversare_pe_orizontala_arbori_oarecare.

procedure Traversare_pe_orizontala_arbori_oarecare;
const max2=20; { lungimea maxim a cozii }
var r:ref;
coada:array[0..max2] of ref;
inda,inde:0..max2; { indicator de adugare, respectiv de eliminare }
fi:fiu;

procedure Adaug(p:ref); { adugare nod n coad }
begin
if inde = (inda+1) mod max2 then WriteLn(g,'Coada plina!!')
else begin
coada[inda]:=p;
inda:=(inda+1) mod max2
end
end;

function Elim:ref; { scoatere nod din coad }
begin
if inda = inde then elim:=nil { s-a parcurs ntregul arbore }
else begin
Elim:=coada[inde];
inde:=(inde+1) mod max2
end
end;

begin { instruciunea compus a procedurii de traversare (vom afia arborele) }
inda:=0;
inde:=0; { iniializare coad }
WriteLn(g);
WriteLn(g,'Arborele tiparit nivel dupa nivel:');
WriteLn(g);
Adaug(rad); { se ncepe cu rdcina }
repeat
r:=elim; { urmtorul nod }
if r <> nil then
with r^ do begin
Write(g,info:3);
for fi:=1 to fii do
Adaug(refs[fi]) { introducerea descendenilor n coad }
end
until r=nil;
WriteLn(g)
end;
268 9. Alocarea dinamic

2. Traversarea n adncime
Spre deosebire de traversarea n inordine, care are un sens bine determinat doar pentru
arbori binari, traversrile n preordine i postordine se pot folosi i n cazul arborilor
oarecare. Dac arborele oarecare este reprezentat cu referine descendente, atunci tra-
versarea se realizeaz printr-o simpl generalizare a metodei care se aplic la arborii
binari.
Dac arborele este reprezentat prin arborele binar echivalent, atunci se poate arta
c parcurgerea n preordine a arborelui binar echivalent corespunde parcurgerii n
preordine a arborelui iniial, iar parcurgerea n inordine a arborelui binar ataat cores-
punde parcurgerii n postordine a arborelui oarecare. Observm c traversrii n post-
ordine a arborelui echivalent nu i putem gsi un corespondent.

9.6.3. Arbori de cutare
Structurile arborescente sunt foarte des folosite pentru memorarea i regsirea rapid a
unor informaii. Informaiile memorate pot fi nregistrri orict de complexe, dar ele
conin un cmp numit cheie, care servete la identificarea nregistrrii. Din punctul de
vedere al operaiilor care se efectueaz asupra arborilor de cutare, doar cmpul cheie
este important, de celelalte putem face abstracie.

Fie C mulimea cheilor posibile ale nregistrrilor care vor trebui regsite cu ajuto-
rul arborelui de cutare. Dac arborele de cutare este astfel construit nct folosete o
relaie de ordine total pe C, atunci vom spune c arborele de cutare este bazat pe or-
dinea cheilor. n esen, ntr-un arbore de cutare bazat pe ordinea cheilor cutarea de-
curge n felul urmtor: se compar cheia cutat cu cheile din nodurile arborelui, de fi-
ecare dat alegnd un subarbore sau altul n funcie de rezultatul comparrii (mai mic,
mai mare) pn cnd este gsit nregistrarea cutat (sau se ajunge la concluzia c
lipsete din arbore, caz n care avem cutare fr succes).
n continuare, ne vom ocupa de arborii de cutare bazai pe ordinea cheilor.

Fie S C mulimea cheilor care conduc la o cutare cu succes n arbore. Un arbore
de cutare bazat pe ordinea cheilor este aadar un arbore n nodurile cruia vom gsi
elemente din C astfel ataate nodurilor nct s ne putem folosi de relaia de ordine din
C pentru regsirea elementelor din S. Exist dou abordri posibile:
1. n abordarea cu informaia n frunze (leaf-oriented) frunzele corespund nregistrri-
lor memorate n arbore, iar n nodurile neterminale apar elemente din C care au rolul
de a dirija cutarea care se termin ntotdeauna la o frunz. n aceast abordare este
preferabil ca arborele s fie neomogen, adic frunzele s corespund nregistrrilor
memorate n arbore (identificate prin cmpul cheie), iar nodurile neterminale s fie n-
registrri care conin referinele ctre celelalte noduri din arbore cu care sunt n legtu-
r i o cheie din C (sau mai multe la arborii multici).

9. Alocarea dinamic 269

2. n abordarea cu informaia n noduri (node-oriented) att n nodurile terminale ct
i n cele neterminale vom avea nregistrri memorate n arbore (aceast structur este
omogen).

Arborii de cutare, bazai pe ordinea cheilor, pot fi de dou feluri: binari sau multi-
ci. Arborii binari de cutare au cte o singur cheie asociat fiecrui nod, arborii mul-
tici au mai multe.
n continuare ne vom ocupa de arborii binari de cutare.
ntr-un arbore binar de cutare cheile sunt astfel ataate nodurilor nct traversnd
arborele n inordine, cheile vor fi ntlnite n ordine cresctoare (strict cresctoare n
cazul abordrii cu informaia n noduri).

n continuare, arborii binari de cutare se vor trata n abordarea cu informaia n
noduri. Se tie c dac mulimea S are n elemente, atunci pentru aceast mulime se
poate construi un arbore binar de cutare total echilibrat de nlime [log
2
n]. (nlimea
unui arbore reprezint numrul de niveluri n arborele respectiv.) Rezult c se vor fa-
ce cel mult [log
2
n] comparaii de chei pentru a gsi nodul cu cheia cutat sau pentru a
constata c elementul lipsete.
Amintim c i n cazul abordrii cu informaia n frunze nlimea arborelui total
echilibrat este tot O(log
2
n) deoarece se verific uor c un arbore binar cu n frunze are
n 1 noduri neterminale.
Algoritmul de cutare ntr-un arbore binar de cutare poate fi descris recursiv n
felul urmtor:
1. dac arborele este vid avem cutare fr succes, deci algoritmul se termin;
2. se compar cheia x cu cheia c a rdcinii; dac x = c avem cutare cu succes (algo-
ritmul se termin);
3. dac x < c, atunci se continu cutarea n subarborele stng;
4. altfel x > c i continum cutarea n subarborele drept.

A. Inserarea n arbori de cutare
n practic exist foarte multe aplicaii n care cutarea se combin cu inserarea. n
momentul n care cutarea se termin fr succes, elementul cutat se introduce ntr-un
nod nou, care se leag n arbore de locul unde ar fi trebuit s se gseasc (unde a euat
cutarea).
Se observ c inserarea nu se poate face eficient n cazul reprezentrii liniare, deoa-
rece ar necesita deplasarea unor elemente, n schimb, n cazul unei reprezentri semi-
statice sau dinamice aceast operaie este foarte simpl.
Algoritmul urmtor dat fiind faptul c pleac de la arborele vid n care insereaz
primul element cutat (pe care evident nu-l gsete) .a.m.d. este de fapt un algoritm
de construire pentru arborele de cutare:

270 9. Alocarea dinamic

1. dac arborele este vid, se creeaz un nou nod care va fi nodul rdcin; cheia va
avea valoarea cutat x;
2. dac cheia c a rdcinii este egal cu x, algoritmul se termin;
3. dac x < c, se reia algoritmul pentru subarborele stng;
4. altfel x > c; se reia algoritmul pentru subarborele drept.

Cutarea unei chei x ntr-un arbore total echilibrat cu n noduri necesit cel mult
N
c
= [log
2
n] comparaii. n general, un arbore de cutare nu este total echilibrat, deci n
procedura Cauta(x,p) numrul comparaiilor necesare n cutare va fi mai mare de-
ct N
c
. Cazul cel mai defavorabil este acela cnd toate cheile care urmeaz s fie inse-
rate sosesc n ordine cresctoare sau descresctoare, deoarece n acest caz arborele de-
genereaz ntr-o list liniar n care numrul de comparaii necesare pentru o cutare
este n medie n/2. Prin urmare, dei revenirea la forma de arbore total echilibrat dup
inserare ar reduce lungimea medie a cutrii, totui transformrile necesare fiind labo-
rioase, ele nu se justific n general. De aceea, n practic se folosesc concepte mai
puin restrictive de echilibru, ceea ce permite obinerea unor algoritmi eficieni.

procedure Cauta(x:Integer; var p:ref);
{ x este cheia nou; se va returna p, pointer la nodul nou inserat }
begin { construirea arborelui de cutare prin inserare }
if p = nil then begin { cutare fr succes }
New(p);
with p^ do begin
cod:=x;
stang:=nil;
drept:=nil
end
end else
if x < p^.cod then
Cauta(x,p^.stang)
else
if x > p^.cod then
Cauta(x,p^.drept)
else { exist nod avnd cheia x, nu l mai introducem }
end;

B. tergerea n arbori de cutare
Problema tergerii se rezolv simplu dac dorim s tergem o cheie care este ataat
unui vrf terminal sau unui vrf cu un singur descendent. Dac vrful respectiv are doi
descendeni, elementul ters va fi nlocuit ori cu nodul cel mai din dreapta al subarbo-
relui stng, ori cu nodul cel mai din stnga al subarborelui drept, astfel pstrndu-se
relaia de ordine ntre nodurile arborelui.
9. Alocarea dinamic 271

Procedura Sterge(x,p) face distincie, ntre urmtoarele trei cazuri:
1) cheia x nu se gsete n arbore;
2) nodul cu cheia x are cel mult un descendent;
3) nodul cu cheia x are doi descendeni.

procedure Sterge(x:Integer; var p:ref);
var q:ref; { tergerea unui element din arborele de cutare }

procedure Ster(var r:ref); { se terge fizic cel mai din dreapta nod al }
begin { subarborelui stng al nodului care se terge logic }
if r^.drept <> nil then
Ster(r^.drept)
else begin
q^.cod:=r^.cod;
q:=r;
r:=r^.stang;
Dispose(q)
end
end;

begin { procedura Sterge }
if p = nil then
Write(g,'Nodul ',x,' nu exista, deci nu poate fi sters).')
else
if x < p^.cod then
Sterge(x,p^.stang)
else
if x > p^.cod then
Sterge(x,p^.drept)
else begin
q:=p;
if q^.drept = nil then begin { nodul nu are fiu drept }
p:=q^.stang;
Dispose(q)
end else
if q^.stang = nil then begin { nodul nu are fiu stng }
p:=q^.drept;
Dispose(q)
end else
Ster(q^.stang) { nodul are doi fii }
end
end;


272 9. Alocarea dinamic

9.6.4. Sortarea cu arbori binari
Considerm o mulime de articole pe care dorim s le sortm cresctor dup valorile
unui anumit cmp.
Exist un numr foarte mare de metode de sortare; vom aminti doar dou dintre
ele, care realizeaz iruri ordonate de elemente utiliznd o structur arborescent.
Prima metod, metoda sortrii prin inserare binar, const n a insera elementul
a
k
, k{2, ..., n} n irul ordonat a
1
a
2
... a
k-1
, determinndu-i locul prin cutare
binar. Mai general, sortarea se poate realiza insernd pe rnd elementele ntr-un arbo-
re de cutare bazat pe ordinea cheilor, iniial vid i traversnd dup aceea arborele n
inordine. Subprogramul Cauta(x,p) poate fi considerat o procedur de sortare prin
inserare.

Fie a
1
, a
2
, ..., a
n
, un vector cu n componente. Alegnd arbitrar un arbore binar cu n
vrfuri, putem ataa vrfurilor sale cte o component a vectorului dat, astfel nct
pentru orice vrf i, cheia ataat lui s fie mai mic sau egal cu cheile ataate descen-
denilor si. Odat realizat acest lucru, cheia ataat rdcinii are valoarea cea mai mi-
c dintre elementele vectorului. Bazndu-ne pe acest lucru, vom descrie urmtorul al-
goritm de sortare: memorm valoarea ataat rdcinii i printr-un sistem de ,,promo-
vare n ierarhia determinat n arbore, obinem un arbore cu n 1 vrfuri care ps-
treaz proprietile enunate anterior, proprieti ce caracterizeaz o grupare denumit
ansamblu (heap). Repetnd procedeul de n ori, obinem elementele vectorului n ordi-
ne cresctoare.
Numim ansamblu un arbore binar n care pentru orice vrf i valoarea a
i
ataat lui
este mai mic sau egal cu oricare dintre valorile ataate descendenilor si.
Un ansamblu poate fi reprezentat foarte eficient n memorie cu ajutorul unei repre-
zentri liniare ntr-un tablou cu n elemente. Rdcina arborelui este primul element
din tablou, adic elementul cu indicele 1. Fiul drept al elementului cu indicele i este
elementul cu indicele 2i + 1, iar fiul stng este cel cu indicele 2i, pentru orice i 1
pentru care 2i + 1 n, respectiv 2i n. Se poate verifica foarte uor c fiecrui ele-
ment al tabloului i corespunde un nod n arbore, deci numrul nodurilor din arbore va
fi n. Aadar, structura arborescent apare numai implicit i numai vectorul (a
i
), i =
1, ..., n are nevoie de spaiu n memorie. Putem spune c un vector (a
i
), i = 1, ..., n
formeaz un ansamblu dac:
pentru orice i {1, 2, ..., [n/2]} avem a
i
a
2i
i
pentru orice i, 1 < 2i + 1 n avem a
i
a
2i+1
.

Introducerea noiunii de ansamblu determin necesitatea soluionrii a dou proble-
me importante:
1) fiind dat un vector cu n componente, s se formeze din el un ansamblu.
2) fiind dat un ansamblu a
1
, a
2
, ..., a
i
, s se schimbe a
1
cu a
i
i s se reconstituie din
primele i 1 componente ale vectorului (a
i
), i = 1, ..., n un ansamblu.
9. Alocarea dinamic 273

procedure Sort(n:Byte; var x:sir);
var i:Byte;

procedure Sch(i,j:Byte); { interschimbarea a dou elemente }
var a:Integer;
begin
a:=x[i];
x[i]:=x[j];
x[j]:=a
end;

procedure Coborare(i,n:Byte);
{ al i-lea element adugat poate cobor n structur, }
var j:Byte; { astfel nct irul x s formeze un ansamblu }
stop:Boolean;
begin
stop:=false; { la apel n 2i }
while not stop do begin
j:=2*i; { descendent stng }
if j < n then { exist i descendent drept }
if x[j+1] > x[j] then
j:=j+1; { j este indicele elementului maxim }
if x[i] >= x[j] then
stop:=true { nu mai avem ce modifica }
else begin
sch(i,j); { elementul i coboar n locul lui j }
i:=j;
stop:=2*i>n { coborrea poate continua dac sunt descendeni }
end
end
end;

begin { procedura Sort }
for i:=n div 2 downto 2 do
Coborare(i,n); { elementele se adaug n vrf unul cte unul }
for i:=n downto 2 do begin
Coborare(1,i);
sch(1,i) { scoaterea elementului maxim i adugarea unui element n vrf }
end
end;



274 9. Alocarea dinamic

9.6.5. Arbori echilibrai AVL
Avnd n vedere c algoritmul de inserare ntr-un arbore binar de cutare reprezentat
cu referine explicite este foarte complicat n cazul n care se dorete s se pstreze
structura de arbore total echilibrat, Adelson-Velskii i Landis au propus utilizarea unei
noiuni mai puin restrictive, cea de arbore echilibrat dup nlime la care lungimea
medie de cutare nu difer n mod substanial fa de arborele total echilibrat.
Un arbore binar se va numi echilibrat dup nlime dac diferena de nlime din-
tre cei doi subarbori ai oricrui vrf este cel mult 1. Aceti arbori se mai numesc i
arbori AVL dup numele celor care i-au definit. Din definiie reiese clar c orice sub-
arbore al unui arbore AVL este tot un arbore AVL.
Observm c orice arbore total echilibrat este i echilibrat. Rezult c arborii total
echilibrai sunt un caz particular al celor echilibrai.
Un arbore binar AVL va fi cu cel mult 45% mai nalt dect unul total echilibrat cu
acelai numr de noduri (afirmaie demonstrat de Adelson-Velskii i Landis). Deci
lungimea drumului de cutare nu crete considerabil. Numrul de comparaii realizate
prin operaiile de cutare, inserare i tergere va fi tot de ordinul O(log
2
n).

A. Inserarea ntr-un arbore AVL
S presupunem c trebuie s inserm un nod n subarborele stng al unui arbore AVL
i c n urma acestei inserri nlimea subarborelui stng ar crete. Dac notm cu
i(st) respectiv i(dr) nlimea celor doi subarbori (stng respectiv drept) nainte de in-
serare, putem distinge urmtoarele trei cazuri:
1) dac i(st) = i(dr) prin inserare se va strica egalitatea dintre nlimile subarbori-
lor, dar proprietatea de echilibru se menine;
2) dac i(st) < i(dr) dup inserare cei doi subarbori vor avea aceeai nlime, deci
caracteristica de arbore echilibrat se pstreaz;
3) dac i(st) > i(dr) prin inserare se stric proprietatea de echilibru, trebuie reechili-
brat arborele. Se poate demonstra c problema reechilibrrii se reduce la cele dou
cazuri i simetricele lor pentru inserarea n subarborele drept, prezentate mai jos.

Fie nodul nou inserat care i-a gsit loc n subarborele stng al arborelui:
Cazul I.
Citind cheile n inordine obinem:
, 1, A, 2, B, 3

B
A
2
3
1

Caracteristica de arbore echilibrat se poate restabili n felul urmtor:
9. Alocarea dinamic 275


B
A
2 3
1

irul cheilor n inordine rmne neschimbat:
, 1, A, 2, B, 3


Cazul II.
irul cheilor n inordine:
1, 2, 3, A, , 4, B, 5, C, 6, 7, 8

B
A
4 5
2
1 3
6
7
C
8


n acest caz rotaiile pentru restabilirea proprietii de echilibru duc la urmtoarea
structur:

A
4
6
2
1 3
5
8
7
B
C

irul cheilor rmne acelai:
1, 2, 3, A, , 4, B, 5, C, 6, 7, 8

Se observ c vrfurile se mut de pe un nivel pe altul dintr-un subarbore n altul,
dar traversarea n inordine realizeaz aceeai rut prin vrfuri, (deci relaia de ordine
total definit de arborele de cutare se pstreaz).

Vom introduce noiunea de factor de echilibru al unui nod, care reprezint diferen-
a dintre nlimea subarborelui stng i al celui drept, deoarece algoritmul de inserare
n arbori AVL depinde n mare msur de valoarea acestui factor n nodurile care se
afl ntre rdcin i locul unde trebuie inserat noul nod.

Astfel algoritmul se va desfura n trei faze:
1) cutarea elementului pn cnd se constat absena lui;
2) inserarea noului nod i stabilirea factorului de echilibru;
3) verificarea factorilor de echilibru pe drumul cutrii (parcurs de jos n sus) i efec-
tuarea eventualei reechilibrri.

276 9. Alocarea dinamic

Cele trei situaii referitoare la nlimile subarborilor afectai corespund valorii fac-
torului de echilibru respectiv egal cu 0, -1, +1. n cea de-a treia situaie este necesar i
ramificarea algoritmului n funcie de tipurile de dezechilibrare (tip I sau II) tratate an-
terior. Operaia de restabilire a echilibrului se realizeaz printr-o permutare circular a
referinelor, permutare care implic dou vrfuri n cazul I (rotaie simpl) sau trei vr-
furi n cazul II (rotaie dubl). Aceast modificare a referinelor determin i modifica-
rea factorului de echilibru n fiecare nod implicat n rotaie.

Structura arborescent o vom descrie ntr-un mod asemntor celui din programele
precedente, adugndu-se informaia referitoare la factorul de echilibru, utilizat n
algoritm pentru reechilibrare, dac este cazul. Avem aadar:

const stanga=-1;
echilibru=0;
dreapta=1;
st=0;
dr=1;

type dir=st..dr;
ech=stanga..dreapta;
ref=^varf;
varf=record
cod:Integer;
fin:array[dir] of ref;
fact:ech
end;

function Cauta(x:Integer):ref; { construirea arborelui echilibrat }
var m:Boolean;
rez:ref;

procedure Reechil(var p:ref; d:ech);
var stang,drept:dir;
p1,p2:ref;
e:ech;
begin
if m then begin { s-a modificat factorul de echilibru }
if d = stanga then begin
stang:=st;
drept:=dr
end else begin
stang:=dr;
drept:=st
end;
9. Alocarea dinamic 277

e:=p^.fact;
if e = echilibru then
p^.fact:=d { modificarea se propag mai sus }
else begin
m:=false; { modificarea nu se propag }
if e*d = stanga then
p^.fact:=echilibru
else begin { reechilibrare }
p1:=p^.fiu[stang];
if p1^.fact = d then begin { rotaie simpl }
p^.fiu[stang]:=p1^.fiu[drept];
p1^.fiu[drept]:=p;
p^.fact:=echilibru;
p:=p1
end else begin { rotaie dubl }
p2:=p1^.fiu[drept];
p1^.fiu[drept]:=p2^.fiu[stang];
p2^.fiu[stang]:=p1;
p^.fiu[stang]:=p2^.fiu[drept];
p2^.fiu[drept]:=p;
if p2^.fact = d then
p^.fact:=-d
else
p^.fact:=echilibru;
if p2^.fact = -d then
p1^.fact:=d
else
p1^.fact:=echilibru;
p:=p2
end;
p^.fact:=echilibru
end
end
end
end; { sfrit procedura Reechil }

procedure Caut(var p:ref);
begin
if p = nil then begin { elementul cutat nu a fost gsit }
New(p); { se creeaz noul nod }
m:=true; { se atenioneaz nivelul superior c eventual }
{ s-a produs o dezechilibrare }
with p^ do begin
cod:=x;
278 9. Alocarea dinamic

fiu[st]:=nil; { noul nod este frunz }
fiu[dr]:=nil;
fact:=echilibru
end;
rez:=p
end else
if x < p^.cod then begin
Caut(p^.fiu[st]); { cutarea se continu n subarborele stng }
Reechil(p,stanga)
end else
if x > p^.cod then begin
Caut(p^.fiu[dr]); { cutarea se continu n subarborele drept }
Reechil(p,dreapta)
end else begin
m:=false; { cheia a fost gsit }
rez:=p
end
end; { sfrit procedura Caut }

begin { procedura Cauta }
Caut(radacina);
Cauta:=rez
end;

B. tergerea n arbori echilibrai AVL
Arborele poate s se dezechilibreze i n urma unei tergeri. Echilibrul pierdut poate fi
restabilit i n cazul acesta prin rotaia referinelor.
Din punct de vedere al numrului de rotaii necesare pentru reechilibrare, diferena
esenial dintre inserare i tergere const n faptul c inserarea necesit cel mult o ro-
taie care implic dou sau trei vrfuri, iar tergerea poate necesita rotaii pentru nodu-
rile de pe ntreg drumul de cutare. Este de remarcat c dup aproximativ fiecare a
doua inserare i dup aproximativ fiecare a cincea tergere trebuie aplicate rotaii de
reechilibrare.

procedure Sterge(x:Integer);
var m:Boolean;
q:ref;

procedure Reech_s(var p:ref; d:ech);
{ modificarea factorului de echilibru i eventual de reechilibru }
var stang,drept:dir;
p1,p2:ref;
e1,e2:ech;
9. Alocarea dinamic 279

begin
if m then begin
e1:=p^.fact;
if e1 = d then
p^.fact:=echilibru { modificarea se propag mai sus }
else
if e1 = echilibru then begin { modificarea nu se propag }
p^.fact:=-d;
m:=false
end else begin
if d = stanga then begin
stang:=st;
drept:=dr
end else begin
stang:=dr;
drept:=st
end;
p1:=p^.fiu[drept];
e1:=p1^.fact;
if e1 <> d then begin { rotaie simpl dreapta }
p^.fiu[drept]:=p1^.fiu[stang];
p1^.fiu[stang]:=p;
if e1 = echilibru then begin { nu se propag modificarea }
p^.fact:=-d;
p1^.fact:=d;
m:=false
end else begin { modificarea se propag }
p^.fact:=echilibru;
p1^.fact:=echilibru
end;
p:=p1
end else begin { rotaie dubl }
p2:=p1^.fiu[stang];
p1^.fiu[stang]:=p2^.fiu[drept];
p2^.fiu[drept]:=p1;
p^.fiu[drept]:=p2^.fiu[stang];
p2^.fiu[stang]:=p;
e2:=p2^.fact;
if e2 = -d then p^.fact:=d
else p^.fact:=echilibru;
if e2 = d then p1^.fact:=-d
else p1^.fact:=echilibru;
p:=p2;
p2^.fact:=echilibru
end
end
end
end; { sfrit procedura Reech_s }
280 9. Alocarea dinamic

procedure Ster(var r:ref); { eliminarea celui mai din dreapta nod }
begin { al subarborelui stng, cu reechilibrare pe calea de revenire }
if r^.fiu[dr] <> nil then begin
Ster(r^.fiu[dr]); { avansare spre dreapta }
Reech_s(r,dreapta) { se modific factorul de echilibru }
end else begin { suntem n nodul care se va terge fizic }
q^.cod:=r^.cod; { coninutul lui se pune n nodul care se terge logic }
q:=r;
r:=r^.fiu[st];
Dispose(q);
m:=true
end
end; { sfrit procedura Ster }

procedure Caut_s(var p:ref);
begin
if p = nil then begin
Write(g,' Codul nu este in arbore!');
m:=false
end else
if x < p^.cod then begin
Caut_s(p^.fiu[st]); { cutare n subarborele stng }
Reech_s(p,stanga) { modificarea factorului de echilibru }
end else
if x > p^.cod then begin
Caut_s(p^.fiu[dr]); { cutare n subarborele drept }
Reech_s(p,dreapta)
end else begin { suntem n nodul care trebuie ters logic }
q:=p;
if q^.fiu[dr] = nil then begin { nu are subarbore drept }
p:=q^.fiu[st];
Dispose(q);
m:=true
end else
if q^.fiu[st] = nil then begin { nu are subarbore stng }
p:=q^.fiu[dr];
Dispose(q);
m:=true
end else begin { doi descendeni }
Ster(q^.fiu[st]);
Reech_s(p,stanga)
end
end
end; { sfrit procedura Caut_s }
begin { procedura Sterge }
Caut_s(radacina)
end;
9. Alocarea dinamic 281

9.7. Implementri sugerate
Modul de lucru n condiiile alocrii dinamice difer esenial de modul n care s-a lu-
crat n alocarea static. Recomandm, pentru a forma deprinderile necesare rezolvrii
de probleme, s implementai urmtoarele structuri de date, astfel nct s exersai re-
alizarea operaiilor specifice acestora. Ar fi util crearea unui unit pentru prelucrarea
structurilor de tip:
1. stiv;
2. coad;
3. coad cu santinel;
4. list simplu nlnuit;
5. list ordonat;
6. list dublu nlnuit;
7. list dublu nlnuit cu santinele;
8. list circular;
9. arbore binar total echilibrat;
10. arbore oarecare;
11. arbore binar de cutare;
12. arbore AVL.

9.8. Probleme propuse

9.8.1. Polinom
Fie dou polinoame P(X) i Q(X). Afiai polinomul S(X) = P(X) + Q(X).

Date de intrare
Fiierul POLINOM.IN conine date referitoare la monoamele celor dou polinoame. Pe
prima linie a fiierului se afl un numr natural m, reprezentnd numrul monoamelor
primului polinom. Pe urmtoarele m linii sunt descrise cele m monoame. Un monom
este caracterizat prin coeficientul su i gradul necunoscutei. Cele dou numere sunt
desprite printr-un spaiu. Pe urmtoarea linie se afl un numr natural n, reprezen-
tnd numrul monoamelor celui de al doilea polinom. Pe urmtoarele n linii sunt de-
scrise cele n monoame n mod similar primului polinom.

Date de ieire
Fiierul de ieire POLINOM.OUT va conine datele referitoare la monoamele polinomu-
lui sum. Fiecare linie a fiierului va conine descrierea unui monom n acelai format
ca n fiierul de intrare.


282 9. Alocarea dinamic

Restricii i precizri
Coeficienii sunt numere ntregi (-32000 coef 32000);
Gradele sunt numere naturale (0 grad 1000);
Ordinea monoamelor este descresctoare dup gradul lui X.

Exemplu
POLINOM.IN
3
-2 2
1 1
-3 0
2
2 2
1 1
POLINOM.OUT
2 1
-3 0

9.8.2. Farfurii
Modelai urmtoarea activitate:
a) Se despacheteaz o lad n care au fost ambalate mai multe farfurii i se aeaz n
vraf. Farfuriile sunt caracterizate de modelul cu care au fost ornate; modelele sunt
distincte.
b) Se caut o farfurie cu un anumit model dat. Farfuriile verificate se aeaz ntr-un
vraf nou pn cnd se gsete farfuria cutat sau se constat c ea nu exist
printre farfuriile despachetate (deci s-a spart).

Date de intrare
n fiierul de intrare VRAF.IN se afl un numr necunoscut de iruri de caractere, cte
un ir pe o linie, reprezentnd modele de farfurii. Primul model este cel cutat, restul
sunt modelele de pe farfuriile scoase din lad care se vor pune n vraf.

Date de ieire
Pe prima linie a fiierului de ieire VRAF.OUT se va scrie cuvntul 'DA' sau 'NU', n
funcie de rezultatul cutrii farfuriei cu modelul dat (dac s-a gsit farfuria se scrie
'DA'). Pe a doua linie se va scrie un numr natural k, reprezentnd numrul farfuriilor
verificate i mutate n noul vraf n timpul cutrii farfuriei avnd modelul dat. Pe ur-
mtoarele k linii se vor scrie modelele farfuriilor mutate n noul vraf n timpul cutrii.
Ordinea lor de afiare este de la vrf spre baz n noul vraf.

Restricii i precizri
un model este format din cel mult 20 de caractere litere mici ale alfabetului engle-
zesc;
n lad sunt cel mul 1000 de farfurii.
9. Alocarea dinamic 283

Exemplu
VRAF.IN VRAF.OUT
maci
viorele
trandafiri
maci
ghiocei
fructe
DA
2
fructe
ghiocei

9.8.3. Agenie
O agenie organizeaz o excursie n Hawai cu preuri promoionale la care doresc s
participe n persoane. Agenia a decis ca n momentul n care afl numrul de locuri k,
s trimit n excursie primele k persoane n ordinea nscrierii. Afiai participanii la
excursie.

Date de intrare
Pe prima linie a fiierului de intrare HAWAI.IN se afl un numr natural n, reprezen-
tnd numrul persoanelor nscrise la agenie. Pe urmtoarele n linii se afl cte un ir
de caractere, reprezentnd numele persoanelor nscrise la excursie. Pe urmtoarea linie
se afl un numr natural k, reprezentnd numrul de locuri.

Date de ieire
Fiierul de ieire HAWAI.OUT va conine numele persoanelor care vor participa la ex-
cursie, n ordinea nscrierii.

Restricii i precizri
1 n, k 1000;
numele persoanelor sunt formate din cel mult 20 de caractere, litere mici ale alfabe-
tului englezesc.

Exemplu
HAWAI.IN
7
Popescu Nicolae
Bara Mircea
Bara Maria
Lungu Vasile
Petrescu Ana
Mircescu Adrian
Ionescu Ilie
5
HAWAI.OUT
Popescu Nicolae
Bara Mircea
Bara Maria
Lungu Vasile
Petrescu Ana


284 9. Alocarea dinamic

9.8.4. Mesaje
Vasile vrea s trimit mesaje de felicitare prietenilor si pe Internet. Deoarece numrul
n de adrese pe care le are este mare, decide s trimit mesaje doar la fiecare a k-a adre-
s din lista de adrese. Atunci cnd ajunge la sfritul listei, continu numrtoarea de
la nceput, ca i cum adresele ar fi aezate n cerc. Se oprete n momentul n care ar
trebui s trimit un mesaj la cineva a doua oar. Afiai numele prietenilor lui Vasile
care au primit mesaj de felicitare.

Date de intrare
Pe prima linie a fiierului de intrare INTERNET.IN se afl dou numere naturale n i
k, avnd semnificaiile din enun. Pe fiecare din urmtoarele n linii se afl un ir de ca-
ractere, reprezentnd numele unui prieten.

Date de ieire
n fiierul de ieire INTERNET.OUT se vor scrie numele prietenilor lui Vasile care au
primit mesaj de felicitare.

Restricii i precizri
1 n, k 10000;
n timpul numrtorii se numr i persoanele care au primit deja mesaj;
numele sunt formate din cel mult 20 de litere mici ale alfabetului englezesc.

Exemplu
INTERNET.IN
6 4
petru
ana
mircea
maria
mihai
cristina
INTERNET.OUT
maria
ana
cristina


9.8.5. Admitere
La admiterea de la Facultatea de Informatic s-au nscris n candidai pe k locuri exis-
tente n anul I. Dup examen s-au ntocmit listele reuiilor n funcie de medii, dar lis-
tele devin definitive doar dup ce candidaii i depun diploma de bacalaureat n origi-
nal la secretariatul facultii. Cunoscnd numele celor care s-au nscris precum i me-
diile obinute de ei, afiai lista corespunztoare situaiei imediat dup examen, apoi,
pe baza numelor celor care i-au prezentat diploma de bacalaureat n original, afiai
lista studenilor de anul I.

9. Alocarea dinamic 285

Date de intrare
Pe prima linie a fiierului de intrare ADMITERE.IN se afl dou numere naturale n i
k, avnd semnificaia din enun. Pe urmtoarele n perechi linii se afl numele candida-
ilor nscrii i mediile lor (vezi exemplu). Pe urmtoarele linii se afl numele celor ca-
re au prezentat diploma de bacalaureat.

Date de ieire
n fiierul de ieire se vor scrie numele studenilor nscrii n anul I, cte un nume pe o
linie, n ordinea mediilor.

Restricii i precizri
1 n, k 1000;
numele sunt formate din cel mult 20 de caractere, litere mici ale alfabetului engle-
zesc;
mediile sunt numere reale, cu dou zecimale exacte;
chiar dac mai sunt locuri libere, pentru a fi nscris, candidatul trebuie s aib me-
dia egal cu cel puin 5;
n caz de medii egale, numele se vor afia n ordine alfabetic.

Exemplu
ADMITERE.IN
7 5
Popescu Nicolae
7.50
Bara Mircea
8.25
Bara Maria
4.00
Lungu Vasile
3.70
Petrescu Ana
10.00
Mircescu Adrian
9.75
Ionescu Ilie
5.50
Popescu Nicolae
Mircescu Adrian
Bara Mircea
Bara Maria
Lungu Vasile
Ionescu Ilie
ADMITERE.OUT
Mircescu Adrian
Bara Mircea
Popescu Nicolae
Ionescu Ilie



286 9. Alocarea dinamic

9.8.6. Pregtirea mesei
Bubulina dorete s fac o surpriz prinilor ei i s pregteasc masa de prnz. S-a
narmat cu o carte de bucate, alimente i condimente, un or mare i e nedumerit de
ordinea n care trebuie s realizeze operaiile pentru a pregti o mas bun.
tie c anumite operaii se realizeaz naintea altora, de exemplu cartofii se spal
nainte de a-i fierbe i doar dup ce au fiert se prepar.
Cunoscnd numrul operaiilor pe care trebuie s le realizeze Bubulina i prioriti-
le unor operaii fa de altele, realizai o ordonare liniar a operaiilor astfel nct masa
s fie pregtit foarte bine.

Date de intrare
Pe prima linie a fiierului de intrare PAPA.IN se gsete un numr n care reprezint
numrul de operaii pe care Bubulina trebuie s le realizeze. Pe urmtoarele linii se
gsesc cte dou numere x i y, separate printr-un spaiu, reprezentnd faptul c
operaia x trebuie realizat naintea operaiei y.

Date de ieire
Fiierul de ieire PAPA.OUT va conine pe o succesiune de n numere, separate prin
cte un spaiu, reprezentnd numerele de ordine ale operaiilor culinare n ordinea n
care pot fi realizate, astfel nct masa s fie bine pregtit.

Restricii i precizri
1 n 100;
Datele de intrare sunt corecte i nu exist operaii care s determine o preceden
circular de exemplu operaia 1 s fie realizat naintea operaiei 2, 2 naintea lui
3 i 3 naintea lui 1.

Exemplu
PAPA.IN
7
2 1
2 4
1 7
4 7
6 7
5 6
3 6
5 3
1 4

PAPA.OUT
2 5 1 3 4 6 7


5:Taie carnea 3: Condimenteaz carnea
6: Frige carnea
7: Spal vase
4: Amestec salata
2: Spal legume
1: Taie
legume
5: Taie carnea
6: Frige carnea 7: Spal vase 4: Amestec
salata
2: Spal legume 1: Taie legume
3: Condimenteaz
carnea
9. Alocarea dinamic 287

9.8.7. Sportivi
Mai muli sportivi de performan trebuie s se antreneze pe aparate identice ntr-o
sal de sport. n funcie de nivelul de pregtire, fiecare sportiv se antreneaz un anumit
timp t
i
, stabilit dinainte. Un sportiv se antreneaz fr ntrerupere i fr s i schimbe
aparatul la care ncepe antrenamentul. Dup ce fiecare sportiv i termin antrenamen-
tul, i numai atunci, toi sportivii au voie s mearg, mpreun, la Festivalul Toam-
nei, care tocmai a nceput n ora. tiind c sunt m sportivi i n aparate, repartizai
sportivii la aparate, astfel nct s poat pleca la festival ct mai repede.

Date de intrare
Prima linie a fiierului de intrare SPORTIVI.IN conine dou numere naturale n i m,
reprezentnd numrul aparatelor, respectiv numrul sportivilor. Urmtoarea linie con-
ine m numere ntregi, reprezentnd durata antrenamentului fiecrui sportiv. Aceste
numere vor fi separate prin cte un spaiu.

Date de ieire
Fiierul de ieire SPORTIVI.OUT va conine n + 1 linii. Pe prima linie se va scrie un
numr ntreg care reprezint timpul scurs de la nceperea antrenamentelor pn cnd
termin i ultimul exerciiile i sportivii pot pleca la festival. Urmtoarele n linii vor
conine duratele antrenamentelor pe aparate, adic pe linia i + 1 se vor scrie duratele
antrenamentelor sportivilor crora li s-a repartizat aparatul i.

Restricii i precizri
1 n, m 1000;
1 t
i
1000, unde t
i
reprezint duratele antrenamentelor, i = 1, 2, , m.

Exemplu
SPORTIV.IN
3 6
6 5 1 3 4 7
SPORTIV.OUT
9
1 7
3 6
4 5

9.8.8. Cutii
S-a mutat muzeul. Obiectele au fost mpachetate n cutii avnd forme cubice de diver-
se dimensiuni. La despachetare lucreaz multe persoane n paralel i pentru a evita
dezordinea, prin ncperile unde se lucreaz la despachetare s-a instalat o band rulan-
t pe care se aeaz cutiile goale, cu singura deschiztura orientat n sus. tefan st la
captul benzii i strnge cutiile. El a primit sarcina s mpacheteze cutiile unele n al-
tele astfel nct numrul pachetelor de cutii s fie cel mai mic posibil. Directorul mu-
zeului, vzndu-l pe tefan ncurcat datorit acestei cerine, stabilete regulile:
288 9. Alocarea dinamic

Cutiile se culeg de pe band n ordinea sosirii lor.
Cutia curent se aeaz ntr-o alt cutie, dac aceasta are dimensiunea mai mic.
Dac nu exist pachet nceput n care s ncap cutia curent, aceasta va constitui
prima cutie dintr-un pachet nou.
ntr-un pachet nceput se aeaz o singur cutie (nu se pun mai multe cutii unele
lng altele ntr-un pachet nceput, chiar dac exist loc).
O cutie aezat la un moment dat nu se mai scoate.
Un pachet nceput nu se aeaz n alt pachet, chiar dac acest lucru ar fi posibil.
Nici o cutie nu poate fi ignorat.

Scriei un program care determin numrul minim de pachete de cutii rezultate n
urma muncii lui tefan, precum i secvenele de cutii din fiecare pachet.

Date de intrare
Prima linie a fiierului de intrare CUTII.IN conine un singur numr natural n, repre-
zentnd numrul cutiilor. Urmtoarele n linii conin fiecare cte un numr natural, re-
prezentnd dimensiunile cutiilor.

Date de ieire
Pe prima linie a fiierului CUTII.OUT se va scrie numrul minim m de pachete de cu-
tii. Pe urmtoarele m linii se vor scrie dimensiunile cutiilor pe care tefan le va mpa-
cheta ntr-un singur pachet, n ordinea invers mpachetrii.

Restricii
0 n 1000;
1 dimensiune_cutie 15000.

Exemplu
CUTII.IN
10
4
1
5
10
7
9
2
8
3
2
CUTII.OUT
4
1 4
2 5
2 3 7 10
8 9


9. Alocarea dinamic 289

9.9. Soluiile problemelor propuse

9.9.1. Polinom
Deoarece se poate ntmpla ca polinoamele s fie rare, (vor avea muli coeficieni
egale cu 0), cea mai potrivit reprezentare o vom realiza cu o list alocat dinamic n
care pstrm cte un monom al polinomului. Un monom se descrie prin coeficientul
su i gradul necunoscutei x.
Se tie c adunarea a dou polinoame se realizeaz termen cu termen, dar nu putem
lua simplu un monom din primul polinom i unul din al doilea, deoarece se poate n-
tmpla ca gradele acestor monoame s difere, caz n care acestea nu se pot aduna. n
concluzie, ne vom situa n dreptul primului monom n primul polinom i vom aduna
coeficientul acestui monom cu coeficientul din cellalt doar n cazul n care gradele
celor dou monoame sunt identice. n caz contrar vom avea un termen nou n polino-
mul sum format fie din monomul respectiv al primului polinom (dac gradul acestuia
este mai mic dect gradul monomului celui de al doilea polinom) fie din al doilea. n
polinomul sum trebuie s avansm n ordinea cresctoare a gradelor, deoarece va tre-
bui s afim coeficienii polinomului sum n ordine descresctoare dup gradul ne-
cunoscutei i aceti coeficieni se pstreaz ntr-o list simplu nlnuit de tip LIFO,
n cazul crora prelucrarea inverseaz ordinea elementelor.
Declaraiile aferente celor dou polinoame date i polinomului sum sunt urmtoa-
rele:

type lista=^monom; { lista este tipul pointerilor care vor referi monoame }
monom=record
coef:Integer; { coeficientul monomului }
gradx:Byte; { gradul monomului }
urm:lista { cmpul care asigur nlnuirea monoamelor }
end;
var cap,cap1,cap2:lista; { pointerii care indic primul monom din }
... { polinomul sum, respectiv din primul i al doilea polinom }

Dup ce s-au creat listele simplu nlnuite corespunztoare celor dou polinoame
date, calculm polinomul sum. Algoritmul este de fapt un algoritm de interclasare:

procedure Suma(var cap:lista; cap1,cap2:lista);
var p,q,r:lista;
begin
p:=cap1; { cap1 indic monomul de grad minim n primul polinom }
q:=cap2; { cap2 indic monomul de grad minim n al doilea polinom }
cap:=nil;

290 9. Alocarea dinamic

while (p <> nil) and (q <> nil) do begin
{ dac avem termeni n ambele polinoame }
New(r); { crem element nou pentru polinomul sum }
if p^.gradx = q^.gradx then
with r^ do begin
coef:=p^.coef+q^.coef; { gradele sunt egale, adunm coeficienii }
if coef = 0 then
Dispose(r) { dac avem coeficient nul, nu crem element }
else begin
gradx:=p^.gradx; { crem elementul cu toate cmpurile sale }
urm:=cap;
cap:=r
end;
cap1:=cap1^.urm;
Dispose(p); { eliberm spaiul alocat elementelor prelucrate }
cap2:=cap2^.urm;
Dispose(q);
p:=cap1; { repoziionm pointerii auxiliari pe capul listelor }
q:=cap2
end else begin { dac n primul polinom, termenul este de grad mai mic }
if p^.gradx < q^.gradx then begin
Element_nou(cap,cap1,r,p) { l inserm n polinomul sum }
else { altfel inserm termenul din al doilea polinom }
Element_nou(cap,cap2,r,q)
end;
while p <> nil do begin { dac mai exist termeni n primul polinom }
New(r);
Element_nou(cap,cap1,r,p)
end;
while q <> nil do begin { dac mai exist termeni n al doilea polinom }
New(r);
Element_nou(cap,cap2,r,q)
end
end;

n aceast procedur se apeleaz subprogramul Element_Nou(cap,cap1,r,p)
n care se creeaz cmpurile elementului nou r^ a listei monoamelor polinomului su-
m (pointerul cap indic primul element al acesteia) i se elibereaz spaiul alocat mo-
nomului copiat n lista original (referit de pointerul cap1). Pointerul p se repoziio-
neaz pe primul element al listei originale.



9. Alocarea dinamic 291

procedure Element_Nou(var cap,cap1,r,p:lista);
begin
with r^ do begin
coef:=p^.coef;
gradx:=p^.gradx;
urm:=cap;
cap:=r;
end;
cap1:=cap1^.urm;
Dispose(p);
p:=cap1
end;

9.9.2. Farfurii
Din farfuriile despachetate crem o stiv alocat dinamic, apoi o parcurgem n scopul
cutrii farfuriei avnd modelul dat. n timpul cutrii, farfuriile verificate le punem
deoparte ntr-o stiv nou, totodat numrndu-le. n final, afim rspunsul la ntre-
bare n funcie de rezultatul cutrii i afim coninutul celei de-a doua stive.
Dac prima farfurie verificat (ultima despachetat) este cea cutat, stiva auxiliar
este vid. Dac farfuria s-a spart i nu o gsim n stiv, toate farfuriile despachetate se
mut n stiva auxiliar.

9.9.3. Agenie
Cele n persoane nscrise formeaz o coad de ateptare. La excursie vor participa pri-
mii k dintre ei. Dac s-au nscris mai puine persoane dect k, i vom afia pe toi care
s-au nscris.

9.9.4. Mesaje
Rezult din enun c cea mai potrivit structur de date cu care am putea reprezenta
lista prietenilor lui Vasile este o list circular. Iniial crem o coad (cu santinel)
apoi legm ultimul element de primul:

...
ultim^.urm:=santinela^.urm;
...

Urmeaz traversarea listei circulare urmnd referinele urm, efectund o numr-
toare pn la k. Fiecare al k-lea element se marcheaz. Traversarea listei se oprete n
momentul n care o numrtoare se termin pe un element marcat deja. n paralel, fie-
care al k-lea element se scrie n fiier, acestea reprezentnd numele prietenilor lui
Vasile care au primit mesajul de felicitare.
292 9. Alocarea dinamic

9.9.5. Admitere
Va trebui s citim datele i s construim o list ordonat dup medii, iar n caz de
egalitate, dup numele candidailor. n programul principal, dup citirea numrului
candidailor i a numrului de locuri n anul I, crem lista vid, n care cmpul nume
din santinel va fi string-ul vid.

Begin
Assign(f,fin); Reset(f); { deschidem fiierul de intrare }
ReadLn(f,n,k); { citire }
New(cap); { crem lista vid }
cap^.urm:=nil;
cap^.num:='';
cap^.medie:=0;
for i:=1 to n do begin
New(nou); { crem un element, fr s l legm n list }
ReadLn(f,nou^.num);
ReadLn(f,nou^.medie);
nou^.confirmat:=false; { deocamdat nu a confirmat nimeni }
Inserare(cap,nou) { inserarea elementului nou n list }
end;
Verif(cap); { completm cmpul confirmat }
Close(f);
Afiseaza(cap) { afim candidaii admii (au confirmat i media este 5) }
End.

n subprogramul Inserare(cap,nou) cutm poziia unde trebuie s inserm
elementul nou^:

procedure Inserare(var cap:lista; nou:lista);
{ inserm elementul nou n lista ordonat dup medii; cap este santinela; }
{ candidaii cu medii egale se insereaz n ordine alfabetic }
var p:lista;
begin
p:=cap; { cutm locul n funcie de medie }
while (p^.urm <> nil) and (p^.urm^.medie > nou^.medie) do
p:=p^.urm;
if p^.urm <> nil then { cutm locul n funcie de nume }
while (p^.urm^.medie=nou^.medie)and(p^.urm^.num<nou^.num) do
p:=p^.urm;
nou^.urm:=p^.urm; { inserm elementul }
p^.urm:=nou
end;
9. Alocarea dinamic 293

9.8.6. Pregtirea mesei
Problema a fost tratat n capitolul 5, unde s-a explicat metoda sortrii topologice.
Aceast metod de sortare se poate implementa mai natural cu ajutorul unui ir n care
fiecare element este un cap de list. n rest algoritmul este cel prezentat n capitolul 5
(teoria grafurilor).

9.9.7. Sportivi
Aceast problem o cunoatem deja din capitolul 3 (euristica greedy). Vom pstra
lista sportivilor care se antreneaz la acelai aparat.

9.9.8. Cutii
Problema de fapt cere ca dintr-un ir de numere date ntr-o ordine oarecare s crem
numr minim de subiruri strict descresctoare. Strategia de abordare a rezolvrii din
punct de vedere algoritmic este greedy, dar avem cteva probleme privind spaiul de
memorie necesar pentru structurile de date. De exemplu, dac avem cazul particular n
care irul este dat n ordine cresctoare, fiecare cutie constituie un pachet, deci ar
trebui s declarm attea subiruri cte numere avem n irul dat. n plus, dac avem
cazul particular n care irul este dat n ordine descresctoare, avem un singur pachet,
deoarece fiecare cutie nou ncape n cutia precedent, deci subirul ar trebui s fie
declarat avnd aceeai lungime cu irul dat. n enun s-a specificat c pot exista 8000
de cutii care ar necesita un tablou bidimensional de 64.000.000 de elemente... Evident,
nu putem declara un astfel de tablou. (i nu am luat n considerare faptul c n cazul
subirurilor ar trebui s pstrm lungimea efectiv a fiecruia etc.)
S considerm exemplul din enun n care avem 10 cutii avnd dimensiunile: 4, 1,
5, 10, 7, 9, 2, 8, 3, 2. Prima cutie constituie totodat i primul pachet.

Pachet 1
4

nc nu tim dac vom pune sau nu alt cutie n ea, dar n momentul n care vine la
rnd cutia de dimensiune 1, datorit faptului c acesta ncape n cutia de dimensiune 4,
o punem n ea.
Pachet 1
4, 1

Urmeaz cutia de dimensiune 5. Aceasta nu ncape ntr-o cutie de dimensiune 1,
deci ncepem cu ea un pachet nou:

Pachet 1 Pachet 2
4, 1 5
294 9. Alocarea dinamic

Urmeaz cutia de dimensiune 10, care nu ncape n nici una dintre cutiile aezate,
deci avem un treilea pachet:

Pachet 1 Pachet 2 Pachet 3
4, 1 5 10

Cutia urmtoare are dimensiunea 7 i ncape n ultima cutie aezat:

Pachet 1 Pachet 2 Pachet 3
4, 1 5 10, 7

Acum trebuie s gsim loc pentru cutia avnd dimensiunea 9. Aceasta nu mai poate
fi pus n cea de dimensiune 10, pentru c aceasta este ocupat i nu avem voie s-o
eliberm, deci ncepem al 4-lea pachet:

Pachet 1 Pachet 2 Pachet 3 Pachet 4
4, 1 5 10, 7 9

Urmeaz o cutie de dimensiune 2. Ea ar ncape n pachetele 2, 3 i 4, dar, pe baza
strategiei greedy o vom pune acolo unde st ct mai strmt, astfel evitnd risipa de
spaiu, adic n pachetul 2:

Pachet 1 Pachet 2 Pachet 3 Pachet 4
4, 1 5, 2 10, 7 9

Urmtoarea cutie ncape n pachetul 4:

Pachet 1 Pachet 2 Pachet 3 Pachet 4
4, 1 5, 2 10, 7 9, 8

Penultima i ultima cutie ncap, una dup alta n pachetul 3:

Pachet 1 Pachet 2 Pachet 3 Pachet 4
4, 1 5, 2 10, 7, 3, 2 9, 8

Din aceast execuie pas cu pas a algoritmului care trebuie implementat rezult
dou observaii foarte importante.
1. La fiecare pas irul format din ultimele elemente ale subirurilor este ordonat cres-
ctor;
2. Pentru a gsi locul cutiei curente este suficient s i cutm locul n acest ir.

9. Alocarea dinamic 295

Pe baza observaiilor decidem s inem evidena acestor ultime cutii n vrful a cte
unei stive, astfel mutnd majoritatea datelor n heap, pstrnd n memoria static doar
irul pointerilor care indic ultima cutie aezat n fiecare pachet. n plus, datorit fap-
tului c aceste elemente din vrfurile stivelor sunt ordonate cresctor, vom aplica algo-
ritmul cutrii binare pentru a gsi locul noii cutii.
n programul principal vom iniializa stivele vide cu nil. Apoi, vom citi cte o
dimensiune de cutie i i vom cuta locul cu subprogramul Inserare(stanga,
dreapta,dimnou) care se apeleaz cu parametri 1, k i dimnou, unde 1 reprezint
indicele din stnga al irului pachetelor, k este indicele din dreapta, iar dimnou este
valoarea de inserat. La primul apel valoarea lui k (indicele din dreapta) este 0, deoare-
ce nc nu avem nici un pachet.
Subprogramul de inserare este urmtorul:

procedure Inserare(stanga,dreapta:Byte; dimnou:Word);
var mijloc:Word;
p:pachet;
begin { nu am gsit nici un pachet corespunztor }
if stanga > dreapta then begin
Inc(dreapta);
k:=dreapta; { pachet nou }
New(p); { aezm cutia curent n acest pachet nou }
p^.dim:=dimnou;
p^.urm:=pachete[k];
pachete[k]:=p
end else begin
if pachete[stanga]^.dim > dimnou then begin
New(p); { am gsit un pachet n care ncape cutia curent }
p^.dim:=dimnou;
p^.urm:=pachete[stanga];
pachete[stanga]:=p
end else begin
mijloc:=(stanga+dreapta) div 2;
if dimnou < pachete[mijloc]^.dim then
Inserare(stanga,mijloc,dimnou)
else
Inserare(mijloc+1,dreapta,dimnou)
end
end
end;

Dac ar fi trebuit afiate irurile exact n ordinea n care s-au mpachetat cutiile, n
loc de stive, am fi lucrat cu cozi, dar astfel ar fi trebuit s declarm att irul vrfurilor
ct i irul ultimelor elemente i nu am fi putut prelucra 15000 de cutii.

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