Sunteți pe pagina 1din 29

13. Tipul referin, structuri de date dinamice ( liste nlnuite i arbori binari).

13.1. Tipul referin.

Limbajul Pascal ofer posibilitatea de a lucra att cu variabile statice ct i cu variabile dinamice. Caracteristicile variabilelor statice sunt bine definite, cunoscute i fixe. Structura, tipul i adresa de memorie nu se pot modifica n timpul execuiei. Aceste variabile sunt referite prin numele lor, fiecrui nume asociindu-i-se o adres fizic de memorie. Unei variabile statice i se poate modifica doar valoarea, nu i adresa (locul su) din memoria intern. Variabilele dinamice au un tip bine precizat nc din faza de compilare ns ele pot fi alocate dinamic (pot lua fiin n faza de execuie a programului), pot fi utilizate (referite) prin adresa lor din memoria intern i pot fi distruse (dealocate) dac nu mai sunt utile. Aceste variabile pot fi referite printr-o

variabil de referin ce conine adresa variabilei dinamice. Variabila de referin poate face referiri numai la variabile dinamice de acelai tip declarat, bine definit i fix. Aceast legatur (coresponden) ntre variabila dinamic i tipul de date referin permite cunoaterea structurii variabilei dinamice. Variabilele de tip referin vor conine n timpul execuiei adrese de memorie ale variabilelor dinamice referite. Datorit acestui fapt, declararea

tipului referin poate fi fcut naintea definirii tipului variabilei dinamice referite. Definirea unui tip referin i a variabilelor de tip referin se face astfel: Type Tip_Referin = ^ Tip_Var_Dinamic; Tip_Var_Dinamic = ... Var Var_Ref1,Var_Ref2,... : Tip_Referin; { poate referi Tip_Referin } {Vor conine adrese}
104

Alocarea dinamic (ocuparea zonei de memorie) pentru variabila dinamic se realizeaz cu procedura : New (Var_Ref) care caut n memoria intern o zon liber pentru variabila dinamic i furnizeaz adresa acestei zone n variabila de tip referin (Var_Ref). Aceast zon este alocat variabilei dinamice, fr a fi iniializat. Dimensiunea zonei alocate

variabilei dinamice este determinat de tipul variabilei dinamice, aa cum se poate vedea n exemplul urmtor : Type Adresa=^Numar; Numar = Record Numar_Cifre : Byte; Sir_Cifre : Array [1..100] of Byte End; Var n : Adresa; Begin New (n); ... End. { Se aloc 101 octeti } {Prelucrare (n^) }

Referirea unei variabile dinamice se face printr-o variabil de tip referin (legat de variabila dinamic). Numele variabilei referin urmat de caracterul ^ reprezint numele variabilei dinamice. Eliberarea zonei de memorie ocupat de o variabila dinamic se va executa cu instruciunea (apelul de procedur) : Dispose (Var_Ref) care disponibilizeaz zona de la adresa coninut n variabila Var_Ref. Dac o variabil referin nu refer nici o variabil dinamic, atunci valoarea variabilei referin este Nil .

105

Variabilele referin se pot utiliza n instruciuni de atribuire prin care o astfel de variabil primete valoarea altei variabile de acelai tip sau constanta Nil i, de asemenea, n expresii relaionale cu operaorii "=" sau "<>". Subliniem c orice referire a variabilei dinamice P^ trebuie s se fac ntre apelurile NEW (P) i DISPOSE (P).

n urmtorul program se adun dou numere raionale citite de la tastatur.

Program Rationale; Type Q = ^Rational; Rational = Record Numarator , Numitor : Integer End; Var a,b,c : Q ; Function Cmmdc (m,n:Integer) : Integer; Var Rest:Integer; Begin While n<>0 Do Begin Rest := m Mod n; m := n; n := Rest End {while}; Cmmdc := m End; {Cmmdc} Function Cmmmc (m,n:Integer) : Integer; Begin If m*n = 0 Then Writeln(nr.nul) Else Cmmmc := m * n Div Cmmdc (m,n) End; {Cmmmc}

{Programul Tipul referin}

{Gaseste c.m.m.d.c.} {al numerelor m i n}

{Calculeaza c.m.m.m.c.} {al numerelor m i n nenule}

106

Procedure Simplific (Var p:Q); {Simplifica nr.rational p } Var Divizor_comun: Integer; Begin With p^ do Begin Divizor_comun:=Cmmdc(Numarator,Numitor); If Divizor_comun > 1 Then Begin Numarator := Numarator Div Divizor_comun; Numitor := Numitor Div Divizor_comun End {If} End {With} End; {Simplific} Procedure Citeste (Var p:Q); {Citeste nr.rational in p} Begin Write ( Numarator,numitor = ); Readln (p^.Numarator, p^.Numitor); Simplific (p) End; {Citeste} Procedure Aduna (a,b:Q; Var c:Q); Var N_comun: Integer; { Numitorul comun } Begin N_comun := Cmmmc(a^.Numitor,b^.Numitor); c^.Numarator := a^.Numarator * (N_comun Div a^.Numitor) + b^.Numarator * (N_comun Div b^.Numitor); c^.Numitor :=N_comun; Simplific (c) End; {Aduna} Procedure Tipareste (p:Q); Begin With p^ do Write (Numarator,/,Numitor) End; {Tipareste nr.rational p}

Begin {Programul principal} New (a); Citeste (a); New (b); Citeste (b); New (c); Aduna (a,b,c); Tipareste(a); Write( + ); Tipareste(b); Write( = ); Tipareste (c); Dispose (a); Dispose (b); Dispose (c); Readln; End.

107

Vom rezolva n continuare aceeai problem n alt manier, utiliznd funcia Aduna pentru suma a dou numere raionale care va da ca rezultat adresa numrului raional calculat. Program Rationale; Type Q = ^Rational; Rational = Record Numarator, Numitor : Integer End; Var a, b : Q ; Function Cmmdc (m,n:Integer) : Integer; Var Rest:Integer; Begin While n<>0 do begin Rest := m Mod n; m := n; n := Rest End {while}; Cmmdc := m End; Function Cmmmc (m,n:Integer): Integer; Begin If m*n = 0 Then Writeln(nr.nul) Else Cmmmc := m * n Div Cmmdc (m,n) End; Procedure Simplific (Var p:Q); Var Divizor_comun: Integer; Begin With p^ Do Begin Divizor_comun:=Cmmdc(Numarator,Numitor); If Divizor_comun > 1 Then Numarator := Numarator Div Divizor_comun; Numitor := Numitor Div Divizor_comun End {With} End; End {If} Begin { Simplifica nr.rational p } {Calculeaza c.m.m.m.c.} {al numerelor m i n nenule} {Gaseste c.m.m.d.c.} {al numerelor m i n} {Programul 2. Tipul referin}

108

Procedure Citeste (Var p:Q); Begin With p^ do Begin

{Citeste nr.rational in p}

Write( Numarator numitor = ); Readln (Numarator, Numitor); If Numitor=0 Then Repeat Write ( Numitor = 0! dati altul); Readln (Numitor); Until Numitor <> 0; End; {With} Simplific (p) End; Function Aduna (a,b:Q) : Q; Var Numit_comun: Integer; Begin New (s); Numit_comun := Cmmmc(a^.Numitor,b^.Numitor); s^.Numarator:= a^.Numarator * (Numit_comun Div a^.Numitor)+ b^.Numarator * (Numit_comun Div b^.Numitor); s^.Numitor :=Numit_comun; Simplific (s); Aduna:=s; End; Procedure Tipareste (p:Q); Begin With p^ Do Write (Numarator,/,Numitor) End; Begin New (a); Citeste (a); Dispose(a); Dispose(b); Readln; End. New (b); Citeste (b); Tipareste(a); Write(+); Tipareste(b); Write(=); Tipareste(Aduna(a,b)); {Tipareste nr.rational p} Dispose(s) s:Q;

109

n limbajul Pascal se pot utiliza variabile nelegate de un anumit tip de baz (tipul variabilei dinamice) n scopul memorrii valorilor unor variabile de tip referin (memorare de adrese). Acest tip referin nelegat, este tipul Pointer. Operaiile ce se pot efectua cu astfel de variabile sunt de tip atribuire astfel: Var AdrP : Pointer; Adr1 : Tip_Referin1; Adr2 : Tip_Referin2; Begin ... AdrP:=Adr1; Adr2:=AdrP; ... End. n exemplul anterior funcia Aduna putea avea urmtorul antet: Function Aduna (a,b:Q) : Pointer; fr a fi necesare alte modificri n program. n urmtorul exemplu ne propunem s tiprim n hexazecimal coninutul zonei de memorie pe care se reprezint un numr real. Program Numar_Real; Const Lung = 6; Type Sir_Octeti = Array[1..Lung] of Byte; Var Pointer_Real : ^Real; Pointer_Byte : ^Sir_Octeti; Pointer_Manevra : Pointer; i : Integer; Function Hexa (Cifra:Byte) : Char; Begin If Cifra < 10 Then Hexa := Chr(Cifra+Ord(0)) Else Hexa := Chr(Cifra+Ord(A)-10) {Tipareste o cifra} {hexazecimala} {Prgramul Pointeri}

110

End; Procedure Print (Octet: Byte); Begin Write (Hexa(Octet Div 16)); Write (Hexa(Octet Mod 16), ) End; Begin New ( Pointer_Real); Write ( Dati un numar real : ); Readln ( Pointer_Real^ ); Pointer_Manevra := Pointer_Real; Pointer_Byte := Pointer_Manevra; For i:=1 to Lung do Print (Pointer_Byte^[i]); Dispose (Pointer_Real); Readln; End. {Programul principal} {Tipareste continutul unui} {octet in hexazecimal}

13.2. Liste nlnuite.

Prin list se nelege o colecie de elemente de acelai tip x1, x2, ... , xn, n care x1 este primul element, xn este ultimul element i pentru 1 < k < n elementul xk are un unic succesor (pe xk+1) i un unic predecesor (pe xk-1). Se poate stabili o coresponden biunivoc ntre elementele listei liniare i submulimea numerelor naturale {1, 2, ..., n} astfel: primului element i atam 1, urmtorului 2 i aa mai departe, iar ultimului n. Numrul natural asociat unui

111

element din list se numete poziia elementului respectiv.

Vom spune c

elementul de pe poziia i precede elementul de pe poziia j dac i < j . Rezult c pe o astfel de structur de date putem defini urmtoarele funcii: Urmtorul, Precedentul i Poziia unui element, aa cum se va vedea mai jos. Asupra listelor liniare putem defini operaii care s ne permit adugarea unui element n lista, extragerea unui element (cu stergerea lui) din lista, cautarea unui element cu o anumita proprietate i crearea unei liste vide. Specificarea acestor operaii se d n continuare:

Operaia CREARE(L) este: L := lista vid (fr nici un element);

Operaia ADUGARE (L,a) este: Dac L la intrare conine elementele : x1, x2, ... , xn,

{n coada listei}

dup efectuarea operaiei, lista L va conine elementele : x1, x2, ... , xn, xn+1, unde xn+1 = a .

Operaia CUTARE (L,a,p) este: Dac L conine elementele : x1, x2, ... , xn,

dup efectuarea operaiei, p primete o valoare ntre 1 i n astfel ca xp = a, sau valoarea n+1 n caz contrar.

Operaia EXTRAGERE (L,p,a) este: Daca L conine la intrare elementele : x1, x2, ... , xn,

dup efectuarea operaiei a primete valoarea xp iar lista va avea elementele : x1, x2, ... , xp-1, xp+1, ..., xn, deci elementul xp a fost eliminat din list.

112

Implementarea acestor operaii depinde de reprezentarea listei n memoria calculatorului. Reprezentarea unei liste liniare ntr-un program Pascal poate fi static sau dinamic. Reprezentarea static a unei liste se realizeaz printr-un vector X cu componentele x1, x2, ... , xn . Spunem c reprezentarea este static ntruct alocarea memoriei necesare vectorului X este fcut n timpul compilrii. Deci locul n memorie al

elementelor listei nu se schimb pe timpul executiei. Elementele listei se vor afla n memorie n locaii consecutive cu o lungime maxim (declarat) care nu poate fi depit i nici schimbat n timpul executiei. Spre deosebire de alocarea static, reprezentarea dinamic se realizeaz cu ajutorul tipului Pointer, alocarea memoriei fcndu-se n timpul execuiei. n locul listelor implementate static care ocup o zon fix de memorie prin locaii succesive, se poate utiliza o structur mult mai flexibil n care fiecare nod este legat de nodul urmtor al listei. Aceast structur o vom numi list

nlanuit i implementarea ei se poate realiza prin alocare dinamic a memoriei. Zona de memorie ocupat de o astfel de list crete sau scade dup cum lista conine mai multe sau mai puine elemente, prin operaii de adugare, respectiv de tergere. O list nlnuit o vom reprezenta grafic astfel :
Primul Informaie Leg Ultimul Informaie Leg Nil

Informaie Leg

Informaie Leg

Un element din list va conine pe lng informaia propriu-zis i adresa (referina) urmtorului element din list (marcat n figura de mai sus prin sgei).

113

n acest fel avnd adresa primului element, vom putea accesa (regsi) orice element din lista nlnuit prin parcurgarea secvenial a acesteia. Ultimul element va conine o informaie (cod) de "santinel" care va marca sfritul listei. Acest cod poate avea valoarea Nil ceea ce nseamn c acest element nu mai este legat de un altul (nu exist urmtorul element). O astfel de list, care conine n fiecare element adresa urmtorului element poate fi parcurs doar secvenial ncepnd de la primul element pn la ultimul, ntr-un singur sens. nlnuit . Variabilele de tip referin dintr-un program Pascal care utilizeaz o list simplu nlnuit se pot declara astfel : Type Tip_Info = ... ; Adr = ^Tip_Elem; Tip_Elem = Record Informatie : Tip_Info; Leg : Adr End; Var Primul, Element, Ultimul, Nou : Adr; O list simplu nlnuit, n functie de disciplina de generare a ei (prin adugare element cu element) poate fi: lista nlnuit nainte, lista nlnuit napoi i list nlnuit ordonat. Lista nlnuit nainte este lista n care adugarea se face n coada listei, aa cum se poate vedea n figura urmtoare:
Primul Informaie Leg Ultimul Informaie Leg Ultimul Nou Informaie Leg
Nil Nil

Aceast structur o vom numi list simplu

Informaie Leg

Operaia scris n Pascal este urmtoarea : Procedure Adaug_in_coada (Nou:Adr; Var Primul,Ultimul:Adr); Begin Nou^.Leg:=Nil; If Primul=Nil Then Primul := Nou { Prima adugare }

114

Else Ultimul^.Leg := Nou; Ultimul:=Nou End; Lista nlnuit napoi este lista n care adugarea se face n capul listei, aa cum se poate vedea n figura de mai jos:
Primul Nou Informaie Leg Primul Informaie Leg

Informaie Leg

Informaie Leg Nil

Operaia scris n Pascal este urmtoarea: Procedure Adaug_in_cap (Nou:Adr; Var Primul:Adr); Begin Nou^.Leg := Primul; Primul := Nou End; Lista nlnuit ordonat este lista n care adugarea se face astfel nct elementele listei s fie ntr-o anumit ordine (cresctor sau descresctor dup o anumit cheie) aa cum se poate vedea n urmtoarea figur :
Primul Informaie Leg Prec Informaie Leg Elem Informaie Leg

Informaie Leg Nil

Nou Informaie Leg

Dac ORDINE este o funcie boolean care precizeaz ordinea dorit, atunci procedura Pascal corespunztoare este: Procedure Adaug_ord (Nou:Adr; Var Primul:Adr); Var Elem, Prec : Adr; Begin Elem:=Primul; While (Elem<>Nil) and not ORDINE(Elem, Nou) do Begin Prec := Elem; Elem := Elem^.Leg End; Nou^.Leg := Elem;

{ Cauta locul } { de inserare }

115

If Elem=Primul Then Primul:= Nou { Adugare naintea primului element } Else Prec^.Leg := Nou; End; n exemplul urmtor vom genera o list prin adugarea elementelor n coada listei, n captul listei sau n aa fel nct lista sa fie ordonat dup un cmp numit cheie. Disciplina de adugare va fi aleas de ctre utilizator. Program Liste_simplu_inlantuite; Type Tip_Cheie = Integer; Tip_Date = String[20]; Tip_Info = Record Cheie : Tip_Cheie; Date : Tip_Date End; Adr = ^Tip_Elem; Tip_Elem = Record Inf : Tip_Info; Leg : Adr End; Var Lista : Adr; Disc : Char; Procedure Citesc (Var Element:Adr); Begin New (Element); Write ( ................... Cheie, Date : ); With Element^, Inf do Readln (Cheie,Date) End; Procedure Generare (Var Primul:Adr; Disc:Char); Var Nou,Ult : Adr; Rasp : Char; Procedure Adaug_in_coada; Begin Nou^.Leg:=Nil; If Primul=Nil Then Primul := Nou Else Ult^.Leg := Nou; Ult:=Nou End; {Programul Liste}

{ Adresa primului element } { Tipul adaugarii }

116

Procedure Adaug_in_capat; Begin Nou^.Leg := Primul; Primul := Nou End; Procedure Inserare_ord; Var Elem, Prec : Adr; Begin Elem:=Primul; While (Elem<>Nil) and (Elem^.Inf.Cheie < Nou^.Inf.Cheie) do Begin Prec := Elem; Elem := Elem^.Leg End;{While} Nou^.Leg := Elem; If Elem=Primul Then Primul := Nou Else Prec^.Leg := Nou; End; Begin {Generare} Primul:=Nil; Repeat Repeat Write (Adaugati(D,N):); Readln(Rasp); Rasp:=Upcase(Rasp); Until Rasp in [D,N]; If Rasp = D Then Begin Citesc (Nou); Case Disc of F : Adaug_in_coada; L : Adaug_in_capat; O : Inserare_ord End {Case} End {If} Until Rasp = N End; Procedure Parcurgere (Elem:Adr); Begin While Elem <> Nil Do With Elem^ Do Begin With Inf Do Writeln (Cheie:5,Date); Elem:=Leg End {With} End; Begin Repeat Writeln ( Ce disciplina doriti la adugare ? ); Writeln ( F = in coada listei (FIFO) ); Writeln ( L = in capul listei (LIFO) ); Writeln ( O = lista ordonata ); Write ( Optiune : ); Readln (Disc); Disc:=Upcase(Disc) Until Disc in [F,L,O];
117

Generare (Lista,Disc); For Disc:=A to E do Writeln; Writeln(Elementele listei sunt:); Parcurgere (Lista); Readln End.

{ Creerea listei }

{ Listarea listei }

tergerea unui element din list se realizeaz prin modificarea legturii elementului precedent (dac acesta exist) urmat de dealocarea zonei ocupate de elementul ters astfel:
Primul Informaie Leg Prec Informaie Leg Elem Informaie Leg

Informaie Leg Nil

Operaia de tergere (scris n Pascal) a elementului Elem din lista L este urmtoarea: Procedure Stergere(Var Elem,Prec,L: Adr); Begin If L = Elem Then L := Elem^.Leg Else Prec^.Leg := Elem^.Leg; Dispose (Elem); End; {Prec=precedentul } { lui Elem } { dac se sterge primul element }

n exemplul urmtor se poate urmri tergerea unor elemente precizate prin cheia coninut n informaia propriu-zis a elementelor. Program Stergere_element_din_lista; {Creeaza o lista de elemente (cheie,string),} Type Tip_Cheie = Integer; Tip_Date = String[20]; Tip_Info = Record Cheie : Tip_Cheie; Date : Tip_Date End; Adr = ^Tip_Elem;
118

{apoi sterge elemente i tipareste lista finala }

Tip_Elem = Record Inf : Tip_Info; Leg : Adr End; Var Lista : Adr; Procedure Citesc (Var Element:Adr); Begin Write ( ............ Cheie, Date : ); With Element^.Inf do Readln(Cheie,Date) End; Procedure Generare (Var Primul:Adr); Var Nou : Adr; Rasp : Char; Begin Primul:=Nil; Repeat Repeat Write ( Adaugati (D,N) : ); Readln (Rasp); Rasp:=Upcase(Rasp); Until Rasp in [D,N]; If Rasp = D Then Begin New(Nou); Citesc (Nou); Nou^.leg:=Primul; End {If} Until Rasp = N End; Procedure Stergere (Var Primul,Prec:Adr); Var Elem:adr; Begin If (Prec=Nil) Then Begin Elem:=Primul; Primul := Primul^.Leg End Else Begin Elem:=Prec^.Leg; {Sterge succesorul} { lui Prec din lista Primul} Primul:=Nou {Creaza lista} { Adresa primului element } {Citeste informatia} {din nodul Element}

119

Prec^.Leg := Elem^.Leg End; Dispose (Elem) End;

Procedure CAUTA(Primul:adr; Cod: Tip_Cheie; Var Prec:adr); Var Elem:Adr; Begin Elem:=Primul; Prec := Nil; While (Elem<>Nil) And (Elem^.Inf.Cheie<>Cod) Do Begin Prec:=Elem; End; Procedure Stergelemente (Var Primul:Adr); Var Elem,Prec : Adr; Cod : Tip_Cheie;Rasp : Char; Begin Repeat Write (Sterg element ? (D,N):); Readln(Rasp); Rasp:=Upcase(Rasp); If Rasp=D Then Begin Write ( Cheia elementului : ); Readln(Cod); Cauta(Primul,Cod,Prec); End {If} until Rasp=N End; Procedure Parcurgere (Primul:Adr); Var Elem:adr; Begin While Elem <> Nil Do Elem:=Elem^.Leg End; Begin End {While} With Elem^.inf do Writeln (Cheie:5,Date); {Parcurge lista Primul} {si-i tipareste continutul} Stergere(Primul,Prec) {Sterge la cerere} {elemente din lista Primul} Elem:=Elem^.Leg End; {While} If (Elem=Nil) Then WriteLN ( Nu exista elementul cu codul ,Cod) {Cauta in lista Primul, elementul cu cheia Cod } { i retine in Prec predecesorul acestui element }

120

Begin Generare (Lista); Stergelemente(Lista); Parcurgere (Lista); End. Readln

{Programul principal } { Creerea listei } { Stergere elemente } { Listarea listei }

Lista simplu nlnuit poate fi traversat doar ntr-un singur sens ncepnd cu primul pn la ultimul element, pentru c un element conine doar adresa urmtorului element din list. Dac dorim sa traversm lista i invers (de la coad la capt) atunci va trebui s reinem n fiecare element i adresa elementului anterior (precedent) :
Leg.St. Informaie Leg.Dr.

O list care are aceast proprietate se numete lista dublu nlnuit.


Primul
LegSt. Inf. LegDr. LegSt. Inf. LegDr. LegSt. Inf. LegDr.

Ultim
LegSt. Inf. LegDr.

Nil

Nil

Variabilele de tip referin dintr-un program Pascal care utilizeaz o list dublu nlnuit se pot declara astfel: Type Tip_Cheie = ...; Tip_Date = ...; Tip_Info = Record Cod : Tip_Cheie; Info : Tip_Date End; Adr = ^Tip_Elem; Tip_Elem = Record

121

Leg_Preced : Adr; Informatii : Tip_Info; Leg_Urmtor : Adr End; Var Primul, Ultim, Elem, Prec, Nou : Adr;

{Leg_St}

{Leg_Dr}

Operaia de adugare ntr-o list dublu nlnuit se poate realiza astfel :


Primul
LegSt. Inf. LegDr.

Prec
LegSt. Inf. LegDr.

Elem
LegSt. Inf. LegDr.

Ultim
LegSt. Inf. LegDr.

Nil Nou
LegSt. Inf. LegDr.

Nil

O procedur Pascal care realizeaz inserarea elementului Nou n faa elementului Elem este urmtoarea: Procedure Inserare(Var Nou,Elem: Adr); Begin (Elem^.leg_st)^.Leg_dr:=Nou; Nou^ .Leg_St:=Elem^.Leg_St; Nou^ .Leg_Dr:=Elem; Elem^.Leg_St:=Nou; End;

Operaia de tergere a elementului Elem dintr-o list dublu nlnuit se poate schia astfel:
Primul
LegSt. Inf. LegDr. LegSt. Inf. LegDr.

Elem
LegSt. Inf. LegDr.

Ultim
LegSt. Inf. LegDr.

Nil

Nil

O procedur Pascal corespunzatoare acestei operaii este urmtoarea:

122

Procedure Stergere(Var Elem : Adr); Begin (Elem^.Leg_St)^.Leg_Dr:=Elem^.Leg_Dr; (Elem^.Leg_Dr)^.Leg_St:=Elem^.Leg_St; Dispose (Elem); End; n exemplul urmtor se poate urmri generarea unei liste dublu nlnuite urmate de parcurgerea acesteia n ambele sensuri: Program Liste_dublu_inlantuite; Type Tip_Cheie = Integer; Tip_Date = String[20]; Tip_Info = Record Cheie : Tip_Cheie; Date : Tip_Date End; Adr = ^Tip_Elem; Tip_Elem = Record Inf : Tip_Info; Leg_St, Leg_Dr : Adr End; Directie = (Inainte,Inapoi); Var Prim : Adr; Ultim : Adr; Disc : Char; Procedure Citesc (Var Element:Adr); Begin New (Element); Write ( ............... Cheie, Date : ); With Element^, Inf Do Readln (Cheie,Date) End; Procedure Generare (Disc:Char; Var Primul,Ultim:Adr); {Creaza o lista dublu} { nlnuit } Var Nou : Adr; Rasp : Char; Procedure Adaug_in_coada; { Adresa primului element } { Adresa ultimului element } { Tipul adaugarii } {Citeste informatia} {dintr-un nod}

123

Begin Nou^.Leg_Dr:=Nil; Nou^.Leg_St:=Ultim; If Primul = Nil Then Primul := Nou Else Ultim^.Leg_Dr := Nou; Ultim:=Nou End; Procedure Adaug_in_cap; Begin Nou^.Leg_Dr := Prim; Prim := Nou End; Procedure Inserare_ord; Begin Elem:=Primul; While (Elem<>Nil) and (Elem^.Inf.Cheie < Nou^.Inf.Cheie) Do Elem := Elem^.Leg_dr; If Elem=Nil Then Adaug_in_coada Else If Elem=Primul Then Adaug_in_cap Else Begin Prec := Elem^.Leg_St; Nou^.Leg_St := Prec; Nou^.Leg_Dr := Elem; Elem^.Leg_St:= Nou; Prec^.Leg_Dr:= Nou End {If} End {inserare}; Begin Primul:=Nil; Ultim:=Nil; Repeat Repeat Write ( Adaugati (D,N) : ); Readln (Rasp); Rasp:=Upcase(Rasp); until Rasp In [D,N]; If Rasp = D Then Begin New(Nou); Citesc (Nou); {Generare} Var Prec,Elem : Adr; Nou^.Leg_St := Nil; If Prim = Nil Then Ultim := Nou Else Prim^.Leg_St := Nou;

124

Case Disc of F : Adaug_in_coada; L : Adaug_in_cap; O : Inserare_ord End {Case} End {If} until Rasp = N End; Procedure Parcurgere (Elem:Adr; Sens:Directie); Begin While Elem <> Nil Do Begin With Elem^.Inf do Writeln (Cheie:5,Date); If Sens=Inainte Then Elem:=Elem^.Leg_Dr Else Elem:=Elem^.Leg_St End {While} End; Begin Repeat Writeln ( Ce disciplina doriti la adugare ? ); Writeln ( Writeln ( Writeln ( Readln (Disc); F = in coada listei (FIFO) ); L = in capul listei (LIFO) ); O = lista ordonata Disc:=Upcase(Disc) );

Write ( Optiune : ); Until Disc In [F,L,O]; Generare (Disc, Prim, Ultim ); Writeln(Lista inainte); Parcurgere (Prim,Inainte); Writeln(Lista inapoi); Parcurgere (Ultim ,Inapoi ); Readln End. { Traversare inapoi } { Traversare inainte } { Creerea listei }

125

13.3. Arbori binari.


O colecie de elemente are o structur de tip arborescent dac elementele componente sunt n relaie unu la mai_multe, adic un element este n relaie cu mai multe elemente. Elementele unei astfel de structuri se numesc noduri sau vrfuri. Ele au un unic predecesor numit printe dar mai muli succesori numii fii. Un arbore este format din mulimea nodurilor i legturile dintre acestea. Un nod al unui arbore poate fi rdcin (dac nu are predecesor) sau poate fi nod intern (dac are un singur predecesor i mai muli succesori) sau poate fi nod terminal sau frunz (dac nu are nici un succesor). Un arbore particular este arborele binar pentru care relaia dintre elemente este de tip unu la dou, adic un element poate avea maxim doi succesori. Un arbore binar poate fi definit recursiv ca fiind o mulime (colecie) de noduri vid sau format din nodul Rdcin, Subarbore_stng i

Subarbore_drept.
Rd.

Subarbore stng

Subarbore drept

La reprezentarea n memorie a unui arbore binar, pe lnga informaia propriu-zis se vor memora n fiecare nod i adresele de legatur spre nodurile succesoare n felul urmtor:
0 1 Nil 11 111 Nil 112 211 21 Nil 212 222 Nil 22 2

126

Deci o variabil de tip arbore va avea tipul Arbore definit astfel: Type Arbore = ^Tip_Elem; Info End; Aici Tip_Info este tipul informaiei pastrat ntr-un nod, putnd fi orice tip Pascal definit anterior. Prin traversarea unui arbore binar vom nelege parcurgerea tuturor vrfurilor arborelui, trecnd o singur dat prin fiecare nod. n funcie de ordinea (disciplina) de vizitare a nodurilor unui arbore binar, traversarea poate fi n preordine, n inordine sau n postordine. Traversarea n preordine este aceea n care se parcurge mai nti nodul rdcin, apoi subarborele stng i dup aceea subarborele drept. Deci se parcurge arborele n ordinea (Rdcin, Subarbore_stng, Subarbore_drept). Evident, definiia este recursiv, parcurgerea unui subarbore fiind facut dupa aceai regul, deci ncepnd cu rdcina. O procedur Pascal corespunzatoare se d n continuare. Procedure Preordine (R:Arbore); Begin If R<>Nil Then With R^ do Begin Prelucrare (Info); Preordine (As); Preordine (Ad) End; Procedura Prelucrare (Nod) poate reprezenta orice subalgoritm de prelucrare a informaiei din nodul specificat (de exemplu tiprirea informailor). End {if} : Tip_Info;

Tip_Elem = Record As, Ad : Adr

127

Traversarea n inordine este aceea n care se parcurge mai nti subarborele stng, apoi nodul rdcin i dup aceea subarborele drept. Deci se parcurge arborele n ordinea (Subarbore_stng, Rdcin, Subarbore_drept). n continuare este prezentat o procedur Pascal de traversarea n inordine : Procedure Inordine (R:Arbore); Begin If R<>Nil Then With R^ do Begin Inordine (As); End {if} Prelucrare (Info); Inordine (Ad) End; Traversarea n postordine este aceea n care se parcurge mai nti subarborele stng, apoi subarborele drept i dup aceea nodul rdcin. Deci se parcurge arborele n ordinea (Subarbore_stng, Subarbore_drept, Rdcin). Procedura Pascal corespunztoare este urmtoarea : Procedure Postordine (R:Arbore); Begin If R<>Nil Then With R^ Do Begin Postordine (As); Postordine (Ad); Prelucrare (R) End; Pentru arborele alturat, ordinea nodurilor corespunztoare cele trei tipuri de traversri este urmtoarea :
16 7 5 2 9 10 13 19 21 20 25 29

End {if}

a) n preordine : 16,7,5,2,10,9,13,20,19,25,21,29; b) n inordine : 2,5,7,9,10,13,16,19,20,21,25,29; c) n postordine :2,5,9,13,10,7,19,21,29,25,20,16.

128

n exemplul urmtor vom genera un arbore binar prin adugri succesive de noduri dup umtoarea regul: elementele cu cheie mai mare dect cheia informaiei din nodul rdcin, se adaug n subarborele stng iar cele cu cheie mai mic n subarborele drept. Prin traversarea n inordine a arborelui astfel construit vom obine lista nodurilor n ordine descrescatoare a cheilor. Informaia pastrat ntr-un nod se refer la un student i conine media acelui student (aceasta fiind cheia dup care se face ordonarea) i numele studentului.

Program Arbore_Binar_Ordonat; Type Tip_Info = Record Medie : real ; nume : string[20] End; Adr = ^Tip_Elem; Tip_Elem = Record Info : Tip_Info; As, Ad : Adr End; Var Arb:Adr; Procedure Citesc (Var InfNod:Tip_Info); Begin Write ( Medie(>0), Nume_Student : ); With InfNod Do Readln (Medie,nume) End; Procedure Print (InfNod:Tip_Info); Begin With InfNod do Writeln ( Medie:6:2, - ,nume) End;

{ Prgramul arbore_Binar }

129

Procedure Creare (Var Arb:Adr); Var InfNod: Tip_Info; Procedure Adaug (Var Sarb:Adr); Begin If Sarb<>Nil Then With Sarb^ Do If InfNod.medie>Info.medie Then Adaug(As) Else Adaug(Ad) Else New (Sarb); End; Begin Arb:=Nil; Repeat Citesc (InfNod); If InfNod.medie > 10 Then Writeln(medie>10?) Else If InfNod.medie > 0 Then Adaug(Arb) Until InfNod.medie <= 0 End; Procedure Traversare_Inordine (Sarb:Adr); Begin If Sarb <> Nil Then With Sarb^ Do Begin Traversare_Inordine (As); Print (Info); Traversare_Inordine (Ad) End; Begin Writeln(Se citesc medii i nume pana media=0); Creare (Arb); Writeln(Elementele arborelui (lista studentilor dupa medie) : ); Traversare_Inordine (Arb); Readln End. End Sarb^.Info:=InfNod; End {if} Sarb^.As:=Nil; Sarb^.Ad:=Nil Begin

130

Urmtorul program va construi un arbore genealogic ascendent (pentru fiecare nod se rein informaii despre o persoan i legturile de tip mam i tat spre nodurile respective) iar apoi se vor depune aceste informaii ntr-o list ordonat dup anul naterii. La parcurgerea acestei liste, persoanele vor fi tiparite n ordinea vrstei. Program Arbore_Genealogic; Type Tip_Info = Record An_Nastere : Integer; Nume : String[20]; End; Adra= ^Tip_Elem_A; Tip_Elem_A = Record Info : Tip_Info; As, Ad : Adra End; Adrl= ^Tip_Elem_L; Tip_Elem_L = Record Info : Tip_Info; Leg : Adrl End; Var Arb : Adra; Lista : Adrl; Procedure Citesc (Var Pers:Tip_Info); Begin Write ( Anul_Nasterii, Nume_Prenume : ); With Pers do Readln (An_Nastere,nume) End; Procedure Adaug_Arbore (Var Sarb:Adra); Var Pers : Tip_Info; Begin Citesc (Pers); If Pers.An_nastere > 0 Then New (Sarb); Sarb^ .Info:=Pers; Write ( Mama ,Pers.nume, : ); Adaug_Arbore (Sarb^ .As); Write ( Tata ,Pers.nume, : ); Else Sarb:=Nil End; Adaug_Arbore (Sarb^ .Ad) End {Then} Begin {Depune la adresa Sarb, } { inf. pt.o persoana } {si decide, dac e nod terminal sau continua } { Adresa rdcinii arbvorelui } { Adresa primului element } {Citeste in Pers} {informatiile despre o persoana} {Program pentru strmoii unei persoane }

131

Procedure Print (Pers:Tip_Info); Begin Writeln (Pers.An_nastere:6, Pers.nume) End;

{Tipareste informatiile} {despre persoana Pers}

Procedure Inserare_Lista_Ordonata(Pers:Tip_Info; Var Lista:Adrl); {Insereaza } Var E, Prec, Nou : Adrl; Begin E:=Lista; Prec := E; E := E^.Leg End;{While} New (Nou); Nou^.Info:= Pers; Nou^.Leg := E; If E = Lista Then Lista := Nou Else Prec^.Leg := Nou End; Procedure Arbore_Lista (Sarb:Adra: Var L: Adrl); Begin If Sarb<>Nil Then With Sarb^ do Begin Inserare_Lista_Ordonata (Info, L); Arbore_Lista (As); Arbore_Lista (Ad) End; Procedure Parcurgere (Elem:Adrl); Begin While Elem <> Nil Do Print(Elem^.Info); Elem:=Elem^.Leg End; Begin Write ( Persoana: ); Adaug_Arbore (Arb); Lista:=Nil; Parcurgere (Lista); End. Arbore_Lista(Arb,Lista); Readln; { Tiparirea listei } Writeln(Persoanele in ordine cronologica:); Writeln; {Programul principal} Begin End {While} End {If} {Creaza lista L prin } { Traversare in Inordine} {Se creaza noul element } { i se depune in lista } { un nou element } {pt. Pers, in Lista ordonata } {Se cauta locul inserarii}

While (E<>Nil) and (E^.Info.An_nastere < Pers.An_nastere) do Begin

132