Sunteți pe pagina 1din 348

Structuri de date i algoritmi

Specializarea Calculatoare ingineri zi Semestrul: Nr. Ore Curs: Nr. Ore Laborator: Nr.credite: Forma de verificare: Structura nota: 4 35 (2,5/Saptamana) 28 (2/Saptamana) 5 examen scris 33% Activitate de laborator (obligatorie) 66% Proba scrisa

Bibliografie
[AHU85] A.V. Aho, J.E. Hopcroft, J.D. Ullman, "Data Structures and Algorithms", AddisonWesley Publishing Company, 1985 [CLR92] T.H. Cormen, C.E. Leiserson, R.L. Rivest, "Introduction to Algorithms", MIT Press, McGraw-Hill Book Company, 1992 [CLR00] T.H. Cormen, C.E. Leiserson, R.L. Rivest, "Introducere n Algoritmi", Editura Agora, 2000 [Cr87] V. Creu, "Structuri de date i tehnici de programare", Litografia Institutului Politehnic "Traian Vuia" Timioara, 1987 [Cr92] V. Creu, "Structuri de date i tehnici de programare avansate", Litografia Universitii Tehnice Timioara, 1992 [Cr00] V.Creu, "Structuri de date i algoritmi. Structuri de date fundamentale. Vol.1", Editura "Orizonturi Universitare", 2000 [HS78] E. Horowitz, S. Sahni "Fundamental of Computer Algorithms", Computer Science Press, Inc., 1978 [Se98] R. Sedgewick, "Algorithms in C", Addison-Wesley Publishing Company, 1998 [Wi76] N. Wirth, "Algorithms + Data Structures = Programs", Prentince-Hall, Inc., Englewood Cliffs, New Jersey, 1976 [Wi86] N. Wirth, "Algorithms&Data Structures", Prentince-Hall, Inc., Englewood Cliffs, New Jersey, 1986

Cuprins
Prefa 1. Structuri de date fundamentale 1.1. Introducere 1.2. Tipuri de date. Tipuri de date abstracte 1.3. Tipuri nestructurate 1.4. Tipuri structurate 1.4.1. Structura tablou. Tipul de date abstract tablou 1.4.2. Tehnici de cutare n tablouri 1.4.3. Structura articol. Tipul de date abstract articol 1.4.4. Structura mulime. Tipul de date abstract mulime 1.4.5. Structura secven. Tipul de date abstract secven 1.5. Conceptul de obiect 2. Noiunea de algoritm. Analiza algoritmilor 2.1. Noiunea de algoritm 2.2. Analiza algoritmilor 2.3. Notaii asimptotice 2.4. Aprecierea timpului de execuie 2.5. Profilarea unui algoritm 3. Sortri 3.1. Conceptul de sortare 3.2. Sortarea tablourilor 3.3. Sortarea secvenelor. Sortarea extern 4. iruri de caractere 4.1. Tipul de date abstract ir 4.2. Implementarea tipului de date abstract 4.3. Tehnici de cutare n iruri 4.3.1. Cutarea tabelar (Table search) 4.3.2. Cutarea de iruri direct 4.3.3. Cutarea de iruri Knuth-Moriss-Pratt 4.3.4. Cutarea de iruri Boyer-Moore 5. Recursivitate 5.1. Introducere 5.2 Utilizarea recursivitii 5.3. Exemple de algoritmi 5.4. Algoritmi cu revenire (backtracking) 5.4.1. Turneul calului 5.4.2. Probleme (n,m). Determinarea unei soluii 5.4.3. Problema celor 8 regine 5.4.4. Determinarea tuturor soluiilor unei probleme (n,m). Generalizarea problemei celor 8 regine 5.4.5. Problema relaiilor stabile 5.4.6. Problema seleciei optime 5.5. Structuri de date recursive 6. Liste 6.1. Structura de date list 6.2. TDA List 6.3. Tehnici de implementare a listelor 6.4. Aplicaii ale listelor nlnuite 6.5. Structuri de date derivate din structura list 6.5.1. Liste circulare

6.5.2. Liste dublu nlnuite 6.5.3. Stive 6.5.4. Cozi 6.5.5. TDA Coad bazat pe prioritate 6.6. Structura de date multilist 6.7. Liste generalizate 6.8. Tipul de date abstract asociere 7. Tabele 7.1. TDA Tabel 7.2. Tehnici de implementare a TDA Tabel 7.3. Implementarea tabelelor prin tehnica dispersiei 7.3.1. Consideraii generale 7.3.2. Alegerea funciei de dispersie 7.3.3. Tratarea situaiei de coliziune 7.3.4. Analiza performanei tehnicii dispersiei nchise

1 Structuri de date fundamentale


1.1 Introducere Sistemele de calcul moderne sunt dispozitive care au fost concepute cu scopul de a
facilita i accelera calcule complicate, mari consumatoare de timp.

Din acest motiv, n majoritatea aplicaiilor, viteza, frecvena de lucru i


capacitatea lor de a memora i de a avea acces la cantiti mari de informaii au un rol hotrtor i sunt considerate caracteristici primordiale ale calculatoarelor direct.

Abilitatea de calcul, n cele mai multe cazuri este irelevant n manier Cantitatea mare de informaii pe care o prelucreaz un sistem de calcul reprezint de
regul, o abstractizare a lumii nconjurtoare, respectiv a unei pri a lumii reale. selectate referitoare la lumea real,

Informaia furnizat sistemului de calcul const dintr-o mulime de date Datele selectate se refer la mulimea de date care este considerat cea mai
reprezentativ pentru problema tratat i despre care se presupune c din ea pot fi obinute (deduse) rezultatele dorite.

n acest context, datele reprezint o abstractizare a realitii n sensul c anumite proprieti i caracteristici ale obiectelor reale pe care
acestea le reprezint, sunt ignorate ntruct sunt considerate periferice i nerelevante pentru problema particular tratat.

O abstractizare este de fapt o simplificare a faptelor. n rezolvarea unei probleme cu ajutorul unui sistem de calcul un rol esenial revine
alegerii unei abstractizri convenabile a realitii care modeleaz (reprezint) situaia real.

Acest lucru se poate realiza definind spre exemplu un set reprezentativ de date Alegerea abstractizrii este determinat de regul de natura problemei de
rezolvat.

Urmeaz n continuare stabilirea reprezentrii abstractizrii. Aceasta activitate este determinat de uneltele care vor fi utilizate n
rezolvarea problemei, spre exemplu de facilitile oferite de un anume sistem de calcul.

De cele mai multe ori cei doi pai se ntreptrund. Alegerea reprezentrii este de multe ori o activitate deosebit de dificil ntruct nu
este determinat n mod unic de facilitile disponibile.

Se cunoate c reprezentarea intern a informaiei ntr-un sistem de calcul se


realizeaz cu ajutorul cifrelor binare (bii).

Aceast reprezentare care este foarte potrivit pentru circuitele electronice, nu


este potrivit pentru fiina uman din cauza numrului prea mare de cifre implicate.

Din acest motiv, alegerea reprezentrii datelor se poate realiza la diferite niveluri de
abstractizare, funcie de obiectivul urmrit i de limbajul de programare utilizat.

n acest context, un limbaj de programare reprezint: Un calculator abstract, Capabil s neleag fraze construite cu ajutorul termenilor definii n acest
limbaj,

Termeni care la rndul lor,

pot s ncorporeze un anumit nivel de abstractizare al entitilor efectiv utilizate (definite) de ctre maina real.

Utiliznd un astfel de limbaj, programatorul va fi eliberat spre exemplu de chestiuni


legate de reprezentarea numerelor dac acestea constituie entiti elementare n descrierea limbajului. valabil pentru marea majoritate a problemelor de prelucrare a datelor, se reflect n special n domeniul de reliabilitate al programelor care rezult.

Utilizriea unui limbaj de programare care ofer un set fundamental de abstractizri,

Este mult mai uor s se conceap un program bazat pe obiecte familiare

(numere, mulimi, secvene) dect unul bazat pe structuri de bii, cuvinte i salturi. masiv de informaii binare.

Desigur, oricare sistem de calcul reprezint n ultim instan datele ca i un Acest fapt este ns transparent pentru programator, El poate opera cu noiuni abstracte, mai uor de manipulat ntruct dispune de
compilatoare specializate care preiau sarcina transformrii noiunilor abstracte n termeni specifici sistemului de calcul int.

Cu ct nivelul de abstractizare este mai strns legat de un anumit sistem de calcul, Cu att este mai simplu pentru inginer sau pentru implementatorul de aplicaii
s aleag o reprezentare mai eficient a datelor, o clas mai larg de aplicaii convenabile.

ns cu att mai redus va fi posibilitatea ca reprezentarea aleas s satisfac Acest fapt precizeaz limitele nivelului de abstractizare abordat pentru un
sistem de calcul real respectiv pentru un limbaj de programare.

n acest sens se remarc limbajele Pascal, C, respectiv C++ Acoper n mod fericit o plaj larg situat ntre Limbajele orientate spre main sau limbaje dependente de main care
las deschise problemele reprezentrilor i automat problemele de reprezentare.

Limbajele cu un nalt nivel de abstractizare bazate pe obiecte care rezolv n acest mod utilizatorul poate aborda nivelul de abstractizare convenabil din
punctul de vedere al realizrii unei anumite aplicaii.

1.2. Tipuri de date. Tipuri de date abstracte


1.2.1. Conceptul de tip de date

n procesul de prelucrare a datelor se face o distincie clar ntre: Numerele reale, Numerele complexe, Valori logice Variabilele care reprezint valori individuale, mulimi de valori, mulimi de
mulimi sau ntre funcii, mulimi de funcii, etc.

n acest sens se statueaz principiul conform cruia fiecare constant, variabil,


expresie sau funcie este de un anumit tip.

Un tip este n mod esenial caracterizat prin: (1) Mulimea valorilor creia i aparine o constant a tipului n cauz,
respectiv mulimea valorilor pe care le poate asuma o variabil, o expresie sau care pot fi generate de o funcie ncadrat n acel tip

(2) Un anumit grad de structurare (organizare) a informaiei; (3) Un set de operatori specifici. n textele matematice tipul variabilelor este n general deductibil din maniera lor de
prezentare, respectiv din forma tipografic a caracterelor utilizate, fr a lua n considerare contextul.

Aceast modalitate este ns dificil de utilizat n textele surs ale programelor pentru

calculatoare unde n general se utilizeaz o gam relativ redus de caractere tipografice.

Din acest motiv, n practica curent, tipul asociat unei variabile este precizat printr-o

declaraie explicit de constant, variabil sau funcie, declaraie care precede textual utilizarea respectivei constante, variabile sau funcii.

Aceast metod simplific activitatea compilatorului permind evitarea alocrii


dinamice a memoriei.

Caracteristicile conceptului de tip sunt urmtoarele: (1) Un tip de date determin mulimea valorilor creia i aparine o constant,
sau pe care le poate asuma o variabil sau o expresie, sau care pot fi generate de un operator sau o funcie. dedus din forma sau din declaraia sa, fr a fi necesar execuia unor procese de calcul.

(2) Tipul unei valori precizate de o constant, variabil sau expresie poate fi

(3) Fiecare operator sau funcie accept argumente de un tip precizat i


conduce la un rezultat de un tip precizat.

Dac un operator admite argumente de diferite tipuri (exemplu -

adunarea numerelor ntregi cu numere reale), atunci tipul rezultatului poate fi determinat din regulile specifice limbajului.

(4) Un tip presupune un anumit nivel de structurare (organizare) a informaiei. Drept urmare, respectnd aceste reguli, un compilator poate verifica
compatibilitatea i legalitatea anumitor construcii de limbaj, n faza de compilare, fr a fi necesar execuia efectiv a programului.

Acest tip de redundan a textului programului este extrem de folositor n dezvoltarea


de programe i este considerat ca un mare avantaj al limbajelor de nivel superior asupra limbajelor de asamblare. fr vreo structur aparent.

Din punctul de vedere al sistemului de calcul, memoria este o mas omogen de bii Ori tocmai structurile abstracte sunt acelea care permit recunoaterea,

interpretarea i prelucrarea configuraiilor de cifre binare prezente n memoria sistemului de calcul

1.2.2. Tipuri de date abstracte

Noiunea de tip de date abstract (TDA). Un TDA poate fi conceput ca un model


matematic cruia i se asociaz o colecie de operatori specifici.

Vom realiza o paralel cu conceptul de procedur. (1) Procedura generalizeaz noiunea de operator. n loc de a fi limitat la utilizarea exclusiv a operatorilor definii n
cadrul limbajului de programare ("built-in" operators), folosind procedurile, programatorul este liber s-i defineasc proprii si

operatori, pe care ulterior s-i aplice asupra unor operanzi care nu e necesar s aparin tipurilor de baz (primitive) ale limbajului utilizat.

Un exemplu de procedur utilizat n aceast manier este spre


exemplu, rutina de nmulire a dou matrici.

(2) Procedurile ncapsuleaz anumite pri ale unui algoritm prin "localizare" Aceasta nseamn plasarea ntr-o singur seciune a programului a
tuturor instruciunilor relevante.

ntr-o manier similar, un TDA (1) Un TDA generalizeaz tipurile de date primitive (ntreg, real, etc.), dup
cum procedurile sunt generalizri ale operaiilor primitive (+, -, etc.).

(2) Un TDA ncapsuleaz conceptual un tip de date n sensul c din punct de


vedere logic i fizic, definirea tipului i toate operaiile referitoare la el sunt localizate ntr-o singur seciune a programului.

Dac apare necesitatea modificrii implementrii TDA-ului, (1) Programatorul tie cu certitudine locul n care trebuiesc efectuate schimbrile (2) Poate fi sigur c revizuirea seciunii respective nu are nici o repercursiune
asupra restului programului.

Mai mult chiar, n afara seciunii n care sunt definii operatorii, TDA-ul n cauz poate
fi tratat ca un tip primitiv.

n acest caz un utilizator trateaz un TDA astfel definit, n termenii operatorilor


asociai nefiind n nici un fel preocupat de implementarea acestora.

O problem care poate s apar este cea legat de faptul c anumite operaii
pot s se refere la mai multe TDA-uri, caz n care accesarea acestor operaii trebuie realizat n mod specific n seciunea fiecrui TDA.

Dezavantajul major al folosirii TDA-urilor rezid n necesitatea respectrii riguroase


a disciplinei de utilizare.

Cu alte cuvinte toate referirile la datele ncapsulate ntr-un TDA trebuiesc


realizate prin operatorii specifici.

Aceast cerin nu este verificabil la nivelul compilatorului i trebuie acceptat


ca i o constrngere a disciplinei de programare. ---------------------------------------------------------------------------------------------------------------Exemplul 1.2.2. Se consider un tip de date abstract List. Nodurile listei aparin unui tip precizat TipNod. Fie L o instan a TDA-ului (L: Lista), fie v o variabil nod (v: TipNod) i fie c o referin la un nod al listei (c: TipReferinta). Un set posibil de operatori pentru TDA List este urmtorul:

1. ListVid(L:Lista);- operator care iniializeaz pe L ca list vid; 2. c:= Primul(L:Lista); - operator care returneaz valoarea indicatorului la primul nod al listei, sau indicatorul vid n cazul unei liste goale; 3. c:= Urmtor(L:Lista); - operator care returneaz valoarea indicatorului urmtorului nod al listei, respectiv indicatorul vid dac un astfel de nod nu exist; 4. Insereaz(v:TipNod, L:Lista); - operator care insereaz nodul v n lista L dup nodul curent. 5. Furnizeaz(v:TipNod, L:Lista); - operator care furnizeaz coninutul nodului curent al listei. --------------------------------------------------------------------------------------------------------------- Observaii referitoare la avantajele definirii unui astfel de TDA. a) Odat definit i implementat TDA List, toate prelucrrile de liste se pot realiza n termenii operatorilor definii asupra acestuia, declarnd un numr corespunztor de instane ale TDA-ului b) Indiferent de maniera concret de implementare a TDA List (tablouri, pointeri, cursori), din punctul de vedere al utilizatorului, forma operatorilor rmne cea definit iniial.

Chiar n accepiunea modificrii pe parcurs a implementrii spre

exemplu din motive de eficien, programele utilizator rmn nemodificate, singurele modificri fiind suportate de seciunea care include TDA-ul.

c) Nu exist nici o limitare referitoare la numrul de operaii care pot fi aplicate instanelor unui model matematic precizat.

Se face ns sublinierea c fiecare set de operatori definete i se refer


la un TDA distinct. d) Eficiena, sigurana i corectitudinea utilizrii TDA-ului Lista este garantat numai n situaia n care utilizatorul realizeaz accesele la listele definite numai prin operatorii asociai tipului respectiv. 1.2.2.1. Definirea unui tip de date abstract (TDA)

Dup cum s-a precizat, un tip de date abstract (TDA) este conceput ca i un model
matematic cruia i se asociaz o colecie de operatori definii pe acest model.

n consecin, definirea unui tip de date presupune:


(1) Precizarea modelului matematic (2) Definirea operatorilor asociai.

Limbajele de programare includ mai multe metode de definire a tipurilor de date. n toate situaiile se pornete de la un set de tipuri denumite Tipuri primitive Tipurile primitive joac rolul de crmizi ale dezvoltrii.

n cadrul acestor tipuri primitive se disting: (1) tipurile standard sau predefinite (2)tipurile primitive dezvoltate de utilizator. Tipurile standard sau predefinite includ de regul numerele i valorile logice. Ele se bazeaz pe modele matematice consacrate (mulimile de numere
ntregi, reale, logice, etc.) i implementeaz operaiile specifice definite pe aceste categorii de numere. tipul se numete ordonat sau scalar.

Dac ntre valorile individuale ale tipului exist o relaie de ordonare, atunci Tipuri primitive definite de utilizator. O metod larg utilizat de construcie a unor astfel de tipuri este aceea a
enumerrii valorilor constitutive ale tipului.

Spre exemplu, ntr-un program care prelucreaz figuri geometrice, poate fi


introdus un tip primitiv numit TipFigur ale crui valori pot fi precizate de identificatorii dreptunghi, ptrat, elips, cerc.

n multe cazuri, noile tipuri se definesc n termenii unor tipuri anterior definite. Valorile unor astfel de tipuri, sunt de regul conglomerate de valori
componente ale unuia sau mai multor tipuri constitutive definite anterior motiv pentru care se numesc tipuri structurate.
Tipuri structurate. Sunt de regul conglomerate de valori de componente. Dac

exist un singur tip constitutiv, acesta se numete tip de baz. Numrul valorilor distincte aparintoare unui tip T se numete cardinalitatea lui T. Poate fi construit o ntreag ierarhie de astfel de structuri,

Pentru definirea tipurilor structurate, se utilizeaz metode de structurare. Acestea pot fi: Statice: tabloul, articolul, mulimea i secventa (fiierul). Dinamice: listele, arborii, grafurile

---------------------------------------------------------------------------------------------------------------Standard (predefinite): intreg, real, boolean, Tipuri primitive (nestructurate) Definite de utilizator: enumerare

TIPURI Statice: tablou, articol, multime Tipuri structurate Dinamice: liste, arbori, grafuri -----------------------------------------------------------------------------------------------------------

Pentru a utiliza un tip de date definit, trebuiesc declarate variabile ncadrate n tipul n cauz, vom numi acest proces instaniere Prelucrarea acestor variabile se realizeaz cu ajutorul operatorilor asociai tipului. o Tipuri de operatori: omogeni, micti, interni, externi o Categorii de operatori: Primitivi, sau operatori atomici, (de regul implementai prin hardware) Definii prin limbaj (de regul cei de structurare sau de acces) Definii i implementai de utilizator o Operatori de baz sunt comparaia i atribuirea, operatorii de transfer, operatorii constructori, operatorii selectori. Concluzie: o Un tip este de fapt o manier de structurare a informaiei. o Pentru tipurile nestructurate este mai relevant mulimea constantelor tipului i mai puin gradul de structurare. o Pe msur ce tipul devine mai complex, gradul de structurare devine tot mai relevant.

1.2.2.2. Implementarea unui TDA

Implementarea unui TDA presupune o translatare, adic o exprimare a sa n termenii


unui limbaj de programare concret.

Aceast translatare presupune (1) Precizarea structurii propriu-zise (modelul matematic) n termenii unui
limbaj de programare

(2) Definirea procedurilor care implementeaz operatorii specifici. Desigur, activitatea de implementare a unui tip se realizeaz numai n cazul tipurilor
de date definite de utilizator, n restul cazurilor elementele specifice de implementare fiind de regul incluse n limbajul de programare utilizat.

Dup cum s-a precizat, un tip structurat se construiete pornind de la tipurile de date
de baz care sunt definite n cadrul limbajului, utiliznd facilitile de structurare disponibile. de maniera de implementare aleas pentru materializarea TDA-ului.

n continuare definirea procedurilor care implementeaz operatorii este intim legat

Exist posibilitatea abordrii ierarhice a implementrii TDA-urilor, respectiv


implementarea unui TDA n termenii altor TDA-uri deja existente

La modul ideal, programatorii ar dori s-i redacteze programele n limbaje ale cror
tipuri primitive de date s fie ct mai apropiate modelele i operaiile TDA-urilor proprii.

Din acest punct de vedere, n multe sensuri limbajele obinuite de programare


nu sunt suficient de nzestrate n ceea ce privete facilitile de a crea diverse TDA-uri i nici de definire a operatorilor specifici.

Un pas nainte n acest sens l realizeaz limbajul ADA.


1.2.3. Tip de date abstract. Tip de date. Structur de date

Se pune problema: care este semnificaia termenilor de mai jos, adesea confundai: Tip de date abstract (TDA), Tip de date (TD) sau simplu tip i Structur de date (SD) Astfel, un tip de date abstract este un model matematic, mpreun cu totalitatea
operaiilor definite pe acest model.

La nivel conceptual, algoritmii pot fi proiectai n termenii unor tipuri de date

abstracte (TDA), dar pentru a implementa un astfel de algoritm ntr-un anumit limbaj de programare, este absolut necesar s se realizeze o reprezentare a acestor TDA-uri n termenii tipurilor de date i ai operatorilor definii n limbajul de programare respectiv. ntr-un limbaj de programare.

In aceste condiii un tip de date nu este altceva dect o implementare a unui TDA Un tip de date (TD) nu poate fi utilizat ca atare. n consecin n cadrul programului se declar variabile ncadrate n tipul respectiv,
variabile care pot fi efectiv prelucrate i care iau valori n mulimea valorilor tipului.

Acest proces se numete instaniere i el de fapt conduce n cazul tipurilor


nestructurate la generarea unei date elementare (DE) iar n cazul tipurilor structurate la generarea unei structuri de date (SD). de date este o instan a unui tip de date structurat.

Astfel o dat elementar este o instan a unui tip de date nestructurat iar o structur Ca atare legtura dintre aceste noiuni este materializat de urmtoarea formul:
------------------------------------------------------------------------

TDA

(implementare)

TD

(instaniere)

DE (Dat elementar) SD (Structur de date)

-------------------------------------------------------------------------

TD

(instaniere)

DE

------------------------------------------------------------------------

Se face precizarea c formula este valabil pentru tipurile primitive definite de


utilizator i pentru cele structurate. variabile asociate tipului.

Prin implementare se nelege definirea tipului iar prin instaniere, declararea unei In cazul tipurilor predefinite faza de implementare lipsete ea fiind realizat de ctre
limbajul de programare aa cum se vede din ultima formul.

1.3. Tipuri nestructurate


1.3.1. Tipul enumerare

n marea majoritate a programelor se utilizeaz numerele ntregi, chiar atunci cnd nu


sunt implicate valori numerice i cnd ntregii reprezint de fapt abstractizri ale unor entiti reale. primitiv nestructurat precizat prin enumerarea tuturor valorilor sale posibile

Pentru a evita aceast situaie limbajele de programare introduc un nou tip de date Un astfel de tip se numete Tip enumerare i el este definit prin enumerarea tuturor

valorilor sale posibile: c1,c2,....,cn . -----------------------------------------------------------TYPE TipEnumerare=(c1,c2,c3,...,cn); [1.3.1.a] enum TipEnumerare {c1,c2,c3,...,cn} variabila; typedef enum {c1,c2,c3,...,cn} nume_tip; [1.3.1.b] ----------------------------------------------------------- Este un tip ordonat, valorile sale considerndu-se ordonate cresctor n ordinea n care au fost definite n baza conveniei: (ci < cj) (i < j) Cardinalitatea tipului este card(TipEnumerare) = n . Tipul enumerare este un tip nestructurat definit de utilizator, n consecin utilizarea sa presupune att faza de implementare (declarare a tipului) ct i faza de instaniere (declarare de variabile aparintoare tipului).

-----------------------------------------------------------{Faza de implementare a tipului enumerare Varianta Pascal} TYPE TipFigura=(dreptunghi,patrat,cerc,elipsa); TipCuloare=(alb,rosu,portocaliu,galben,verde, albastru,maro,negru); TipBoolean=(adevarat,fals); [1.3.1.c] TipZiSaptamina=(luni,marti,miercuri,joi, vineri,sambata,duminica);

TipGrad=(soldat,caporal,sergent,sublocotenent, locotenent,capitan,maior,colonel,general); TipObiect=(constanta,tip,variabila,procedura, functie); TipStructura=(secventa,tablou,articol,multime); -----------------------------------------------------------// Faza de implementare a tipului enumerare Varianta C
/*1.3.1.c*/

typedef enum {dreptunghi,patrat,cerc,elipsa} TipFigura; typedef enum {alb, rosu, portocaliu, galben, verde, albastru, maro, negru} TipCuloare; typedef enum {adevarat,fals} TipBoolean; typedef enum {luni,marti,miercuri,joi,vineri,sambata,duminica} TipZiSaptamina; typedef enum{soldat,caporal, sergent, sublocotenent, locotenent, capitan, maior, colonel,general} TipGrad; typedef enum {constanta,tip,variabila,procedura,functie} TipObiect; typedef enum {secventa,tablou,articol,multime} TipStructura;
------------------------------------------------------------------------

// Faza de implementare a tipului enumerare Varianta C enum boolean {false,true} a; typedef enum {false,true} boolean; [1.3.1.d] ------------------------------------------------------------

La definirea unui astfel de tip se introduce nu numai un nou identificator de tip, dar

se definesc i identificatorii care precizeaz valorile noului tip (de fapt domeniul valorilor tipului) acest fel considerabil gradul de nelegere al textului surs.

Aceti identificatori pot fi utilizai ca i constante n cadrul programului, sporind n


-----------------------------------------------------------{Faza de instaniere a tipului enumerare Varianta Pascal} VAR c: z: g: b: TipCuloare; TipZiSaptamina; TipGrad; TipBoolean;

[1.3.1.e] c:= alb; {c:= 0;} z:= miercuri; {z:= 2;} g:= capitan; {g:= 5;} b:= fals; {b:= 1;} -----------------------------------------------------------// Faza de instaniere a tipului enumerare - Varianta C

/*1.3.1.e */

TipCuloare c; TipZiSaptamina z; TipGrad g; TipBoolean b; c z g b = = = = alb; miercuri; capitan; fals; /*c:= /*z:= /*g:= /*b:= 0;*/ 2;*/ 5;*/ 1;*/

----------------------------------------------------------- Astfel, referitor la secvena [1.3.1.c] se pot introduce spre exemplu variabilele c,z,g,b [1.3.1.e].

n mod evident, aceast reprezentare la nivel de program este mult mai


intuitiv dect spre exemplu: c:= 0; z:= 2; g:= 5 ; b:= 1,

Aceasta reprezentare se bazeaz pe presupunerea c c,z,g,b sunt de tip

ntreg i c n calitate de constante le sunt asociate numere ntregi n ordinea enumerrii lor. nespecifici pentru astfel de tipuri nenumerice, ca de exemplu: c:= c+1 care va fi semnalat ca eroare.

Drept urmare un compilator poate verifica uor utilizarea unor operatori Deoarece, tipul enumerare este un tip ordonat, au fost introdui operatori care
genereaz succesorul respectiv predecesorul unui argument precizat.

Acetia sunt operatori : SUCC(x) i PRED(x) Operator de transfer (type casting) care permite conversia simpl a unei valori
ntregi reprezentnd o poziie ordinal n cadrul tipului, n valoarea corespunztoare. Spre exemplu construcia TipCuloare(1) reprezint valoarea rosu a tipului TipCuloare

1.3.2. Tipuri standard predefinite Tipurile standard predefinite sunt acele tipuri care sunt disponibile n marea majoritate a sistemelor de calcul ca i caracteristici hardware Tipurile standard predefinite includ numerele ntregi, valorile logice, caracterele i numerele fracionare,: INTEGER, BOOLEAN, CHAR, REAL. Tipul ntreg implementeaz o submulime a numerelor ntregi,

Dimensiunea ntregilor (numrul maxim de cifre) variaz de la un sistem de


calcul la altul i depinde de caracteristicile hardware ale sistemului. valori exacte i se conformeaz regulilor obinuite ale aritmeticii.

Se presupune ns c toate operaiile efectuate asupra acestui tip conduc la

Procesul de calcul se ntrerupe n cazul obinerii unui rezultat n afara setului


reprezentabil (depirea capacitii registrelor - DCR).

Pe mulimea numerelor ntregi n afara operatorilor clasici de comparare i


atribuire, pe tipul ntreg se definesc i operatori standard (DIV) i modulo (MOD)

Operatori standard: adunare (+), scdere (-), nmulire (*), mprire ntreag
m - n < (m DIV n)* n <= m (m DIV n)* n + (m MOD n) = m

-----------------------------------------------------------TDA ntreg Modelul matematic: elemente scalare cu valori n mulimea numerelor ntregi: {..., -3, -2, -1, 0, 1, 2, 3,....}. Notaii: i,j,k - ntregi; [1.3.2.a] inz - ntreg nonzero; inn - ntreg nonnegativ; e - valoare ntreag (constant sau variabil); b - valoare boolean. Operaii: AtribuireIntregi(i,e) - memoreaz valoarea lui e n variabila i (procedur); k:= Adunarentregi(i,j) (funcie); k:= Scderentregi(i,j) (funcie); k:= nmulirentregi(i,j) (funcie); k:= mprirentregi(i,inz) (funcie); inn:= Modulo(i,inz) - returneaz ntregul pozitiv care reprezint restul mpririi lui i cu inz (funcie); b:= EgalZero(i) (funcie); b:= MaiMareCaZero(i) (funcie). ----------------------------------------------------------- Tipul real implementeaz o submulime reprezentabil a numerelor reale. o n timp ce aritmetica numerelor ntregi conduce la rezultate exacte, aritmetica valorilor de tip real este aproximativ n limitele erorilor de rotunjire cauzate de efectuarea calculelor cu un numr finit de cifre zecimale. Tipului Boolean are dou valori care sunt precizate de ctre identificatorii TRUE (adevrat) i FALSE (fals). o n limbajul C aceste valori lipsesc fiind substituite de valori ntregi: 1 sau <> 0 semnific adevrat, respectiv 0 semnific fals. o Operatorii specifici definii pentru acest tip sunt operatorii logici: conjuncie, reuniune i negaie P TRUE TRUE FALSE FALSE Q TRUE FALSE TRUE FALSE P AND Q TRUE FALSE FALSE FALSE P OR Q TRUE TRUE TRUE FALSE NOT P FALSE FALSE TRUE TRUE

Tabelul 1.3.2.a. Operatori logici Tipul standard caracter cuprinde o mulime de caractere afiabile. o Codificarea ASCII (American Standard Code for Information Interchage)

-----------------------------------------------------------Litere mari: A B C ... X Y Z hexazecimal: x41' x'42' x'43' x'58' x'59' x'5A' zecimal: 65 66 67 88 89 90 Cifre: hexazecimal: zecimal: Litere mici: hexazecimal: zecimal: 0 x'30' 48 a x'61' 97 1 x'31' 49 b x'62' 98 2 ... ' ' x 32 50 c ... x'63' 99 9 x'39' 57 z x'7A' 122

Caracterul blanc: hexazecimal: x20 zecimal: 32 ----------------------------------------------------------- Limbajul Pascal definete tipul primitiv char i Limbajul C tipurile char i int sunt echivalente. Stabilirea naturii unui caracter: ('A' <= x) AND (x <= 'Z') ('a' <= x) AND (x <= 'z') ('0' <= x) AND (x <= '9') - x este liter mare; - x este liter mic; - x este o cifr.

Limbajul Pascal pune la dispoziie dou funcii standard de transfer ntre tipurile char i integer , o ORD(c:char):integer; care returneaz numrul de ordine al caracterului c n mulimea char (cu alte cuvinte valoarea ntreag corespunztoare codului caracterului); o CHAR(i: integer):char; preciznd cel de-al i - lea caracter al mulimii char (adic caracterul care are codul i). o CHAR i ORD sunt funcii inverse. Spre exemplu, f('5')=5, g(8)='8'. f i g sunt funcii inverse: f(g(i))=i (0 < i < 9) g(f(c))=c ('0' < c < '9').

-----------------------------------------------------------{Varianta Pascal} VAR i:integer; c:char; ORD(CHR(i))=i; CHR(ORD(c))=c; [1.3.2.b] f(c)=ORD(c)-ORD('0'); {f:C I} g(i)=CHR(i+ORD('0')); {g:I C} -----------------------------------------------------------//Varianta C

char c; int n; n=c-'0' //f(c) [1.3.2.c] c=n+'0'; //g(i) -----------------------------------------------------------1.3.3. Tipul subdomeniu Este destul de frecvent cazul n care o variabil a unui anumit tip poate s ia valori doar ntr-un anumit interval limitat. Acest lucru poate fi exprimat definind variabila ca aparinnd unui tip subdomeniu al tipului respectiv conform relaiei: -----------------------------------------------------------TYPE TipSubdomeniu = min..max; [1.3.3.a] ----------------------------------------------------------- unde min i max sunt limitele intervalului. Se precizeaz faptul c tipul TipSubdomeniu se refer la un tip deja definit (predefinit) care se numete tip scalar asociat i c min i max sunt valori ale acestui tip.

n secvena [1.3.3.b] se prezint cteva exemple de definire a unor astfel de tipuri: -----------------------------------------------------------TYPE TipAn = 1900..1999; TipLitera = 'a'..'z'; [1.3.3.b] TipCifra = '0'..'9'; TipOfiter = sublocotenent..general; ----------------------------------------------------------- n acest context fiind date variabilele y: TipAn i c: TipLitera , atunci atribuirile y:= 1973 i c:= 'W' sunt permise, iar y:= 1291 sau c:= '9' nu. dect n cazul n care valoarea de atribuit este precizat printr-o constant sau printr-o variabil de acelai tip.

Legalitatea unor asemenea atribuiri nu poate fi verificat de ctre compilator

Atribuiri de forma y:= i i c:= a unde i este de tip INTEGER iar a de tip

CHAR pot fi verificate numai n timpul execuiei programului element care complic i lungete codul generat de ctre compilator.

Practica confirm ns valoarea deosebit a unor astfel de verificri n dezvoltarea


de programe.

1.4. Tipuri structurate


1.4.1. Structura tablou. Tipul de date abstract tablou. Un tablou este o structur omogen el constnd dintr-o mulime de componente de acelai tip numit tip de baz. Tabloul este o structur de date cu acces direct (random-acces), deoarece oricare dintre elementele sale sunt direct i n mod egal accesibile. Pentru a preciza o component individual, numelui ntregii structuri i se asociaz un indice care selecteaz poziional componenta dorit.

La definirea unui tablou se precizeaz: o (1) Metoda de structurare, care este implementat direct de limbaj (ARRAY) , o (2) Numele tipului de date rezultat (TipTablou) , o (3) Tipul de baz al tabloului (TipElement) o (4) Tipul indice (TipIndice).

-----------------------------------------------------------{Definirea unui tip tablou Varianta Pascal} TYPE TipTablou = ARRAY[TipIndice] OF TipElement; VAR tablou: TipTablou; [1.4.1.a]

TYPE TipMatrice = ARRAY[TipIndice1] OF TipTablou; VAR matr: TipMatrice; ----------------------------------------------------------- TipElement nu este supus nici unei restricii, adic el poate fi oricare alt tip (inclusiv un tip structurat) TipIndice este n mod obligatoriu un tip ordinal (nu este admis tipul ntreg, n schimb este permis un tip subdomeniu al acestuia) n limbajul C lucrurile se petrec n mod asemntor cu deosebirea c tipul indice este n mod obligatoriu tipul ntreg (int) i nu se precizeaz explicit metoda de structurare.
/*1.4.1.a*/
typedef TipElement TipTablou[IndiceTipOrdinal]; TipTablou tablou; typedef TipTablou TipMatrice[IndiceTipOrdinal1]; /*echivalent cu: typedef TipElement TipMatrice[IndiceTipOrdinal][IndiceTipOrdinal1]; */ TipMatrice matr;

-----------------------------------------------------------------

// Definirea unui tip tablou - Varianta C

-----------------------------------------------------------------

//TipElement numeTablou[nrElemente]; float tablou[10]; [1.4.1.b] char sir[10]; ----------------------------------------------------------- Tabloul este o structur de date static pentru care rezervarea de memorie se realizeaz n faza de compilare a programului. O valoare structurat (o instan) a unui tip de date TipTablou avnd componentele c1, c2..., cn poate fi iniializat printr-un constructor de tablou i o operaie de atribuire: tablou = TipTablou(c1, c2,...,cn). -----------------------------------------------------------{Varianta Pascal} TYPE x = (a,b,c); Tablou = ARRAY[x] OF integer; CONST vector: tablou=(3,1,7); [1.4.1.c] -----------------------------------------------------------

//Varianta C int vect[5]={0,1,33,4,8}; int mat[2][3]={{11,12,13},{1,2,3}}; char str[9]="Turbo C++"; ---------------------------------------------------------- Operatorul invers al unui constructor este selectorul. Acesta selecteaz o component individual a unui tablou. Fiind dat o variabil tablou vector , un selector de tablou se precizeaz adugnd numelui tabloului, indexul i al componentei selectate: vector[i]. n locul unui indice constant poate fi utilizat o expresie index (de indice). Evaluarea acestei expresii n timpul execuiei programului, conduce la determinarea componentei selectate. Dac tipul de baz al unui tablou este de asemenea ordonat, atunci n cadrul tipului tablou poate fi definit o relaie de ordonare natural.

Ordonarea natural a dou tablouri de acelai tip este determinat de ctre acele componente corespunztoare care sunt diferite i care au cel mai mic indice. (2,3,5,8,9) >(2,3,5,7,11) 'CAPAC' < 'COPAC' -----------------------------------------------------------TYPE TipTablou=ARRAY[1..n] OF TipElement; VAR x,y:TipTablou; [1.4.1.d] (x<y) <=> Ek:0<k<=n:((Ai:0<i<k:x[i]=y[i])&(x[k]<y[k])) ---------------------------------------------------------- Un exemplu clasic n acest sens l constituie ordonarea lexicografic a tablourilor de caractere. Deoarece toate componentele unui tip tablou TipTablou aparin aceluiai tip de baz TipElement avem: Card (TipTablou) = Card (TipElement) Card (TipIndice) . Tablouri de o singur dimensiune sau tablouri liniare. Accesul la oricare element al unui astfel de tablou se realizeaz utiliznd un singur indice n mecanismul de selecie. TipElement precizat la definirea unui tip de date tablou poate fi la rndul su un tip tablou. Prin acest mecanism se definesc tablourile de mai multe dimensiuni. Astfel de tablouri se numesc de obicei matrice Accesul la un element al matricei se realizeaz utiliznd un numr de indici egal cu numrul de dimensiuni ale matricei mat[i,j,k, ,m,n]. Tipul de date abstract tablou poate fi prezentat ca i n secvena [1.4.1.e]. Exemplu de implementare Pascal a unui astfel de tip de date abstract [1.4.1.d].

-----------------------------------------------------------TDA Tablou Model matematic: Secven de elemente de acelai tip. Indicele asociat aparine unui tip ordinal finit. Exist

o coresponden biunivoc ntre indici i elementele tabloului. Notaii: TipElement -tipul elementelor tabloului; a - tablou unidimensional; i - index; [1.4.1.e] e - obiect de tip TipElement. Operaii: DepuneInTablou(a,i,e) - procedur care depune valoarea lui e n cel de-al i-lea element al tablolui a; e:= FurnizeazDinTablou(a,i) - funcie care returneaz valoarea celui de-al i-lea element al tabloului a. -----------------------------------------------------------{Exemplu de implementare Varianta Pascal} CONST NumrMaxElem = 10; TYPE TipElement = ......; TipIndex = 1.. NumrMaxElem; TipTablou = ARRAY [Tip Index] OF TipElement; VAR i: TipIndex; a: TipTablou; [1.4.1.f] e: TipElement; {DepuneInTablou(a,i,e)} a[i]:=e; {FurnizeazDinTablou(a,i)} e:=a[i]; -----------------------------------------------------------//Exemplu de implementare - Varianta C /*1.4.1.f*/ #define NumarMaxElem valoareIntreaga typedef ... TipElement; /*se defineste tipul elementelor ex. typedef float TipElement dar poate fi si un tip definit de programator, sau un tip incomplet definit*/ /*unde TipOrdinal este un tip ordinal (fundamental sau definit anterior)*/

typedef TipOrdinal TipIndex;

typedef TipElement TipTablou[NumarMaxElem]; TipIndex i; TipTablou a; TipElement e; a[i]=e; /*DepuneInTablou(a,i,e)*/ e=a[i]; /*FurnizeazaDinTablou(a,i)*/ ------------------------------------------------------------------------

1.4.2. Tehnici de cutare n tablouri Formularea problemei: se presupune c este dat o colecie de date, n care se cere s se localizeze (fixeze) prin cutare un anumit element. Se consider c mulimea celor n elemente este organizat n forma unui un tablou liniar: a: ARRAY[1..n] OF Tip Element; De regul TipElement are o structur de articol cu un cmp specific pe post de cheie.

Sarcina cutrii este aceea de a gsi un element al tabloul a, a crui cheie este identic cu o cheie cutat x . Indexul i care rezult din procesul de cutare satisface relaia a[i].cheie=x, i el permite accesul i la alte cmpuri ale elementului localizat. Vom presupune n continuare c TipElement const numai din cmpul "cheie", adic el este chiar cheia. Cu alte cuvinte se va opera de fapt cu un tablou de chei.

1.4.2.1. Cutarea liniar

Atunci cnd nu exist nici un fel de informaii referitoare la colecia de date n care se face cutarea, tehnica evident este aceea de a parcurge n mod secvenial colecia prin incrementarea indexului de cutare pas cu pas. Aceast metod se numete cutare liniar i este ilustrat n secvena [1.4.2.1.a].

-----------------------------------------------------------{Cautare liniar (while) - Varianta Pascal} VAR a:ARRAY[1..n] OF TipElement; x:TipElement; [1.4.2.1.a] i:=1; WHILE (i<n)AND(a[i]<>x) DO i:=i+1; IF a[i]<>x THEN {n tablou nu exist elementul cutat} ELSE {avem o coinciden la indicele i} ----------------------------------------------------------/*cautare liniara (while) - varianta C*/ TipElement a[NumarMaxElem]; TipElement x; int i=0; while ((i<n-1) && (a[i]!=x)) i++; if (a[i]!=x ){ /*n tablou nu exista elementul cautat*/ } else{ /*avem o coincidenta la indicele i*/ } ----------------------------------------------------------------/*1.4.2.1.a*/

Exist dou condiii care finalizeaz cutarea:


Elementul a fost gsit adic a[i] = x ; Elementul nu a fost gsit dup parcurgerea integral a tabloului.

Invariantul buclei, care de fapt este condiia care trebuie ndeplinit naintea fiecrei incrementri a indexului i , este: (1<= i < n)&(Ak:1 <= k < i:a[k]<> x) i el exprim faptul c pentru valori ale lui k < i , nu exist coinciden.

Condiia de terminare va fi:

((i = n)OR(a[i]= x))&(Ak:1 <= k< i:a[k]<> x)


Aceast condiie poate fi utilizat ntr-un algoritm de cutare bazat pe ciclul REPEAT

[1.4.2.1.b], -----------------------------------------------------------{Cautare liniar (repeat) - Varianta Pascal} VAR a: ARRAY[1..n] OF TipElement; {n>0} x: TipElement; i:=0; REPEAT [1.4.2.1.b] i:=i+1 UNTIL (a[i]=x)OR(i=n)); IF a[i]<>x THEN {n tablou nu exist elementul cutat} ELSE {avem o coinciden la indicele i}

------------------------------------------------------------------------

/*cautare liniara (do) - varianta C*/ /*1.4.2.1.b*/ TipElement a[NumarMaxElem]; TipElement x;

int i=-1; do{ i++; }while ((i<n-1) && (a[i]!=x)) if (a[i]!=x ){ /*n tablou nu exista elementul cautat*/ } else{ /*avem o coincidenta la indicele i*/ } ----------------------------------------------------------------

n legtur cu acest algoritm se precizeaz urmtoarele:


La prsirea buclei mai trebuie realizat o comparaie a lui a[i] cu x n vederea

stabilirii existenei sau nonexistenei elementului cutat; multe elemente identice).

Dac elementul este gsit, el este elementul cu cel mai mic indice (dac exist mai

Dup cum se observ fiecare pas al algoritmului necesit evaluarea expresiei booleene (care este format din doi factori) i incrementarea indexului.

Acest proces poate fi simplificat i n consecin cutarea poate fi accelerat,


prin simplificarea expresiei booleene.

O soluie de a simplifica expresia este aceea de a gsi un singur factor care s-i implice pe cei doi existeni. Acest lucru este posibil dac se garanteaz faptul c va fi gsit cel puin o potrivire. n acest scop se completeaz tabloul a cu un element adiional a[n+1] cruia i se atribuie iniial valoarea lui x (elementul cutat). Tabloul devine:

a: ARRAY[1..n+1] OF TipElement;

Elementul x poziionat pe ultima poziie a tabloului se numete fanion, iar tehnica de cutare, tehnica fanionului.

Evident, dac la prsirea buclei de cutare i > n , rezult c elementul cutat nu se afl n tablou.

Algoritmul de cutare astfel modificat apare n [1.4.2.1.c].

-----------------------------------------------------------{Cautare liniara tehnica fanionului (while)-Varianta Pascal} VAR a: ARRAY[1..n+1] OF TipElement; x: TipElement; a[n+1]:=x; i:=1; [1.4.2.1.c] WHILE (a[i]<>x) DO i:=i+1; IF i=n+1 THEN {n tablou nu exist elementul cutat} ELSE {avem o coinciden la indicele i}

------------------------------------------------------------------------

/*cautare liniara metoda fanionului (while) - varianta C*/ /*1.4.2.1.c*/ TipElement a[NumarMaxElem+1]; TipElement x; int i=0; a[NumarMaxElem]=x; while (a[i]!=x) i++; if (i == NumarMaxElem){ /*n tablou nu exista elementul cautat*/ } else{ /*avem o coincidenta la indicele i*/ } ----------------------------------------------------------------

Condiia de terminare dedus din acelai invariant al ciclului este: (a[i]= x)&(Ak:1 <= k < i:a[k]<> x)

n secvena [1.4.2.1.d] apare acelai algoritm implementat cu ajutorul ciclului REPEAT (do).

-----------------------------------------------------------{Cautare liniara tehnica fanionului(repeat)-Varianta Pascal} VAR a: ARRAY[1..n+1] OF TipElement;{n > 0} x: TipElement; i:=0; a[n+1]:=x REPEAT i:=i+1 UNTIL a[i]=x;

[1.4.2.1.d]

IF i=n+1 THEN {n tablou nu exist elementul cutat} ELSE {avem o coinciden la indicele i} -----------------------------------------------------------/*cautare liniara metoda fanionului (do) - varianta C*/ /*1.4.2.1.d*/ TipElement a[NumarMaxElem+1]; TipElement x; int i=-1; a[NumarMaxElem]=x; do{ i++; }while (a[i]!=x) if (i == NumarMaxElem){ /*n tablou nu exista elementul cautat*/ } else{ /*avem o coincidenta la indicele i*/ } ----------------------------------------------------------------

Performana cutrii liniare este O(n/2), cu alte cuvinte, n medie un element este gsit dup parcurgerea a jumtate din elementele tabloului. n secvena [1.4.2.1.e] apare un alt exemplu de implementare n limbajul C a tehnicilor de cutare discutate anterior.

-----------------------------------------------------------//tehnici de cautare - implementare C //cautare liniara //returneaza n pentru negasit, respectiv i<n pentru gasit // pe pozitia i int cautare _liniara(int x,int a[],int n {int i; for(i=0;i<n && a[i]-x;i++); return i; }

[1.4.2.1.e]

//cautare liniara metoda fanionului //returneaza n pentru negasit, respectiv i<n pentru gasit // pe pozitia i int cautare_fanion(int x,int a[],int n) {int i; a[n]=x; for(i=0;a[i]-x;i++); return i; }
------------------------------------------------------------------------

1.4.2.2. Cutarea binar

Procesul de cutare poate fi mult accelerat dac se dispune de informaii suplimentare


referitoare la datele cutate.

Este bine cunoscut faptul c o cutare se realizeaz mult mai rapid ntr-un masiv de
date ordonate (spre exemplu ntr-o carte de telefoane sau ntr-un dicionar).

n continuare se prezint un algoritm de cutare ntr-o structur de date ordonat,


adic care satisface condiia: -----------------------------------------------------------VAR a:ARRAY[1..n] OF TipElement;{n>0} Ak:1 < k <= n:a[k-1]<= a[k] [1.4.2.2.a] -----------------------------------------------------------

Ideea de baz:

Se inspecteaz un element aleator a[m] i se compar cu elementul cutat x. Dac este egal cu x cutarea se termin;

Dac este mai mic dect x , se restrnge intervalul de cutare la elementele care

au indici mai mari ca m;


Dac este mai mare dect x , se restrnge intervalul la elementele care au indicii

mai mici ca m.

n consecin rezult algoritmul denumit cutare binar.

Variabilele s i d sunt de tip indice i ele precizeaz limitele stnga respectiv dreapta ale intervalului n care elementul ar mai putea fi gsit: -----------------------------------------------------------{Cutare binar - Varianta Pascal} VAR a:ARRAY[1..n] OF TipElement;{n>0} x:TipElement; s,d,m:TipIndice; gasit:boolean; s:=1; d:=n; gasit:=false; WHILE (s<=d) AND (NOT gasit) DO [1.4.2.2.b] BEGIN m:={orice valoare cuprinsa intre s si d}; IF a[m]=x THEN gasit:=true ELSE IF a[m]<x THEN s:=m+1 ELSE d:=m-1 END; IF gasit THEN {avem o coinciden la indicele i}

----------------------------------------------------------------/*cautare binara - varianta C*/ /*1.4.2.2.b*/ TipElement a[n]; /*n>0*/ TipElement x; TipIndice s,d,m; int gasit;

s=0; d=n-1; gasit=0;

while((s<=d)&&(!gasit)) { m=(s+d)/2; /*sau orice valoare cuprinsa intre s si d*/ if(a[m]==x) gasit=1; else if(a[m]<x) s=m+1; else d=m-1; } if(gasit) /*avem o coincidenta la indicele i*/ ----------------------------------------------------------------

Invariantul buclei, adic condiia ce trebuie ndeplinit naintea fiecrui pas este (s <= d)&(Ak:1 <= k < s:a[k]< x)&(Ak:d < k <= n:a[k] > x) Condiia de terminare este: gasit OR ((s>d)&(Ak:1<=k<s:a[k]<x)&(Ak:d<k<=n:a[k]>x)) Alegerea lui m este arbitrar n sensul c ea nu influeneaz corectitudinea execuiei algoritmului, n schimb influeneaz eficiena sa. Soluia optim n acest sens este alegerea elementului din mijlocul intervalului, ntruct ea elimin la fiecare pas jumtate din intervalul n care se face cutarea. n consecin rezult c numrul maxim de pai de cutare va fi O(log 2 n),

Aceast performan reprezint o mbuntire remarcabil fa de cutarea liniar unde numrul mediu de cutri este n / 2 .

Eficiena cutrii binare poate fi mbuntit dac se interschimb instruciunile IF ntre ele. O mbuntire de fond ns a eficienei se poate obine - ca i n cazul cutrii liniare - prin simplificarea condiiei de terminare.

Acest lucru se poate realiza dac se renun la ideea de a termina algoritmul de ndat ce s-a stabilit coincidena. La prima vedere acest lucru nu pare prea nelept, ns la o examinare mai atent, se poate observa c ctigul n eficien la fiecare pas este mai substanial dect pierderea provocat de cteva comparaii suplimentare de elemente. Se reamintete faptul c numrul de pai este log 2 n .

Soluia cea mai rapid are la baz urmtorul invariant: (Ak:1<=k<s:a[k]<x)&(Ak:d<k<=n:a[k]>x) Procesul de cutare continu pn cnd intervalul de cutare ajunge de dimensiune banal (0 sau 1 element). Aceast tehnic este ilustrat n [1.4.2.2.c].

-----------------------------------------------------------{Cutare binar ameliorat - varianta Pascal} VAR a:ARRAY[1..n] OF TipElement;{n>0} x:TipElement; s,d,m:TipIndice;

{cautare binara performant} s:=1; d:=n+1; WHILE s<d DO BEGIN m:=(s+d) DIV 2; IF a[m]<x THEN [1.4.2.2.c] s:=m+1 ELSE d:=m END; IF d>n THEN {nu exista elementul cautat}; IF d<=n THEN IF a[d]=x THEN {elementul exista} ELSE {elementul nu exista}; -----------------------------------------------------------/*cautare binar ameliorat - varianta C */ /*1.4.2.2.c*/ TipElement a[n]; /*n>0*/ TipElement x; TipIndice s,d,m; s=0; d=n; while(s<d){ m=(s+d)/2; if(a[m]<x) s=m+1; else d=m; } if(d>n) /*nu exista elementul cautat*/; if(d<=n) if(a[d]==x) /*elementul exista*/; else /*elementul nu exista*/; -----------------------------------------------------------

Condiia de terminare n acest caz este s > d . Ciclul se termin cnd s = d .


Cu toate acestea egalitatea s = d nu indic automat gsirea elementului cutat. Dac d > n nu exist nici o coinciden. Dac d<=n se observ c elementul a[d] corespunztor ultimului indice d nu a fost comparat cu cheia, n consecin este necesar efectuarea n afara ciclului a unui test a[d]=x care de fapt stabilete existena coincidenei.

Acest algoritm asemenea cutrii liniare, gsete elementul cu cel mai mic indice, lucru care nu este valabil pentru cutarea binar normal. Alte variante de implementare a celor dou cutri binare n limbajul C apar n secvena [1.4.2.2.d].

-----------------------------------------------------------//cautari binare - varianta C int cautare_binara(int x,int a[],int n){ int s=0,d=n-1,m; do{ (x>a[m=(s+d)/2])?(s=m+1):(d=m-1); }while(a[m]-x && s<=d); return(a[m]-x)?n:m; }

[1.4.2.2.d]

int cautare_binara_ameliorata(int x,int a[],int n){ int s=0,d=n,m; do{ (x>a[m=(s+d)/2])?(s=m+1):(d=m); }while(s<d); return ((d>=n)||(d<n && a[d]-x))?n:d; } -----------------------------------------------------------1.4.2.3. Tehnica cutrii prin interpolare

O alt mbuntire posibil a metodei de cutare binar, const n a determina cu mai mult precizie locul n care se face cutarea n intervalul curent, n loc de a selecta ntotdeauna elementul de la mijloc. Astfel cnd se caut ntr-o structur ordonat, spre exemplu cartea de telefon, un nume ncepnd cu B este cutat la nceputul crii, n schimb un nume care ncepe cu V este cutat spre sfritul acesteia. Aceast metod, numit cutare prin interpolare, necesit o simpl modificare n raport cu cutarea binar. Astfel, n cutarea binar, noul loc n care se face accesul este determinat cu ajutorul relaiei, unde s i d sunt limitele intervalului de cutare:
1 m = s + *(d s) 2

Urmtoarea evaluare a lui m , numit cutare prin interpolare se consider a fi mai performant:
m=s+

x a[ s ] *(d s) a[ d ] a[ s ]

Ea ine cont de valorile efective ale cheilor n stabilirea indicelui la care se face urmtoarea cutare Aceast estimare ns presupune o distribuie uniform a valorilor cheilor pe domeniul vizat.

------------------------------------------------------------------/* cautare prin interpolare - varianta C int cautare_interpolare(element a[],int ultim,int x){ long m,s,d;

s=0; d=ultim-1; [1.4.2.1.m] do{ m=s+((x-a[s].cheie)*(d-s))/(a[d].cheie-a[s].cheie); if (x>a[m].cheie) s=m+1; else d=m-1; } while ((s<=d) && (a[m].cheie!=x) && (a[d].cheie!=a[s].cheie) && (x>=a[s].cheie) && (x<=a[d].cheie)); if (a[m].cheie==x) return m; else return ultim; } ------------------------------------------------------------------

Proprietate. Cutarea prin interpolare utilizeaz att n caz de reuit ct i n caz de nereuit mai puin de lg (lg (n) +1) comparaii [Se 88]. Funcia lg (lg n) este o funcie care crete extrem de ncet,

Astfel spre exemplu, pentru valori ale lui n de ordinul 109, lg(lg n)< 5.

1.4.3. Structura articol. Tipul de date abstract articol

Metoda cea mai general de a obine a tipuri structurate este aceea de a reuni elemente ale mai multor tipuri, unele dintre ele fiind la rndul lor structurate, ntr-un tip compus. Exemple n acest sens sunt: o Numerele complexe din matematic care sunt compuse din dou numere reale o Punctele de coordonate compuse din dou sau mai multe numere reale n funcie de numrul de dimensiuni ale spaiului la care se refer.

n matematic un astfel de tip compus se numete produsul cartezian al tipurilor constitutive. Setul de valori al tipului compus const din toate combinaiile posibile ale valorilor tipurilor componente, selectnd cte o singur valoare din fiecare.

O astfel de combinaie se numete n-uplu;

Numrul total de combinaii este egal cu produsul numerelor de elemente ale fiecrui tip constitutiv, adic cardinalitatea tipului compus este egal cu produsul cardinalitilor tipurilor constitutive. Termenul care descrie o dat compus de aceast natur este cuvntul record (articol) respectiv struct

n secvenele [1.4.3.a.] i [1.4.3.b.] se prezint modul generic de definire a unui tip articol n limbajele Pascal respectiv C. -----------------------------------------------------------{Definire Tip articol - Varianta Pascal}

TYPE TipArticol = RECORD NumeCmp1: Tip1;

NumeCmp2: Tip2; [1.4.3.a] ... NumeCmpn: Tipn END; {RECORD} -----------------------------------------------------------//Definire Tip articol - Varianta C typedef struct { tip1 numeCmp1; tip2 numeCmp2; [1.4.3.b] ... tipn numeCmpn; } numeTip; ------------------------------------------------------- Exemple de definire a unor tipuri de date articol. -------------------------------------------------------{Exemple de definire a tipului articol} TYPE complex = RECORD re: real; im: real END;{RECORD complex}

data = RECORD zi: 1..31; luna: 1..12; an: 1..2010 END;{RECORD data}

[1.4.3.c]

persoana = RECORD nume: string[12]; prenume: string[12]; dataNastere: data; sitSociala: (necasatorit, casatorit, divortat); sex: (barbatesc,femeiesc) END;{RECORD persoana) ------------------------------------------------------- Identificatorii NumeCmp1 , NumeCmp2 ,.... NumeCmpn introdui la definirea tipului, sunt nume conferite componentelor individuale ale acestuia.

Ei sunt utilizai n selectorii de articol care permit accesul la cmpurile unei variabile structurate de tip articol. Pentru o variabil x:TipArticol , cea de-a i-a component, adic cel de-al i-lea cmp poate fi precizat prin notaia "dot" sau "punct": x.NumeCmpi O atribuire a respectivei componente poate fi precizata prin construcia sintactic: x.NumeCmpi:= xi unde xi este o valoare (expresie) de tip Tipi .

In secvena [1.4.3.d] apar cteva exemple de instaniere a tipurilor articol anterior definite. -----------------------------------------------------------{Exemple de instaniere a structurilor articol}

VAR

z:complex; d:data; p:persoana;

z.im (de tip real) [1.4.3.d] d.luna (de tip subdomeniu 1..12) p.nume (de tip string) p.dataNastere (de tip data) p.dataNastere.zi (de tip subdomeniu 1..31) ------------------------------------------------------- Exemplul care se refer la tipul persoana, pune n eviden faptul c constituenii unui tip articol pot fi la rndul lor structurai, aceast situaie conducnd la concatenarea selectorilor.

n mod evident, tipuri structurate diferite pot fi utilizate ntr-o manier ncuibat.

Astfel cea de-a i-a component a unui tablou a , care la rndul su este o component a unei variabile structurate x se precizeaz prin construcia sintactic: x.a[i] Componenta avnd numele de selector NumeCmp aparinnd celei de-a i-a componente de tip articol a unui tablou a este precizat prin construcia: a[i].NumeCmp.

Din punct de vedere formal tipul de date abstract articol este prezentat n secvena [1.4.3.e.]. -----------------------------------------------------------TDA Articol [1.4.3.e] Modelul matematic: O colecie finit de elemente numite cmpuri, care pot aparine unor tipuri diferite. Exista o coresponden biunivoc ntre lista identificatorilor de cmpuri i colecia de elemente. Notatii: a - obiect de tip articol; id - identificator nume de cmp; e - obiect de acelai tip cu cmpul id din articolul a. Operatori: DepuneArticol(a,id,e) - procedur care memoreaz valoarea lui e n cmpul id al lui a; e:= FurnizeazArticol(a,id) - funcie care returneaz valoarea cmpului id din a. -----------------------------------------------------------

Secvenele [1.4.3.f.] i [1.4.3.g.] prezint cte un exemplu de implementare n Pascal respectiv C a tipului de date abstract articol. -----------------------------------------------------------

{Exemplu de implementare Pascal} TYPE tip1 = ... ; {tipuri de cmpuri} tip2 = ... ; .... tipn = ... ; TipArticol = RECORD

c1: tip1; c2: tip2; ... cn: tipn END;{RECORD TipArticol}

[1.4.3.f]

VAR a: TipArticol; {DepuneArticol(a,ci,e)} a.ci:=e; {FurnizeazaArticol(a,ci)} e:=a.ci; -----------------------------------------------------------//Exemplu de implementare C struct punct { int x; int y; } struct dreptunghi { struct punct varf1; struct punct varf2; } struct punct p1; double dist; struct dreptunghi fereastra;

[1.4.3.g]

dist=sqrt((double)(p1.x*p1.x)+(double)(p1.y*p1.y)); fereastra.varf1.x=188; -------------------------------------------------------------1.4.3.1. Articole cu variante

De multe ori, n practica programrii, este mai convenabil ca dou tipuri simple s fie considerate variante ale aceluiai tip. Spre exemplu, tipul coordonate despre care s-a amintit n seciunea precedent, poate fi privind ca o reuniune a coordonatelor carteziene i polare ale cror constitueni sunt respectiv (a) dou lungimi i (b) o lungime i un unghi.

Pentru a identifica varianta actual a unei astfel de variabile s-a introdus o a treia component numit discriminator de tip sau cmp selector. -----------------------------------------------------------{Exemplu 1 de articol cu variante}

TYPE tip = (cartezian,polar); [1.4.3.1.a] coordonate = RECORD CASE fel: tip OF cartezian: (x,y: real); polar: (r: real; fi: unghi) END;{RECORD coordonate} VAR p: coordonate; {daca p.fel = cartezian exista cmpurile p.x si p.y} {daca p.fel = polar exista cmpurile p.r si p.fi} ----------------------------------------------------------- Dac se consider variabila p:coordonate , atunci numele cmpului discriminator este p.fel iar numele coordonatelor sunt fie p.x i p.y n cazul unei valori carteziene (p.fel = cartezian), fie p.r i p.fi n cazul unei valori polare (p.fel = polar).

Foarte adesea, dou sau mai multe tipuri pot fi doar parial reunite. Aceast situaie conduce la structura de articol cu variante.

Un exemplu n acest sens este tipul persoana1 din secvena [1.4.3.1.b]: -----------------------------------------------------------{Exemplu 2 de articol cu variante}

TYPE sex: (barbatesc,femeiesc); persoana1 = RECORD nume,prenume: alfa; [1.4.3.1.b] dataNastere: data; situatieSociala: (necasatorit,casatorit, divortat, vaduv); CASE sexul: sex OF barbatesc: (greutate: real; musta: boolean); femeiesc : (masuri:ARRAY[1..3] OF integer) END;{RECORD persoana1} ------------------------------------------------------- Forma general generic a unui tip articol cu variante este: -------------------------------------------------------{Definirea generica a unui articol cu variante} TYPE TipArticolVariante = RECORD s1: t1; s2: t2;...sn-1: tn-1; CASE sn: tn OF c1: (s1,1: t1,1;...s1,n1: t1,n1); ...... ck: (sk,1: tk,1;...sk,nk: tk,nk) [1.4.3.1.c] ...... cm:(sm,1: tm,1;...sm,nm: tm,nm) END;{RECORD TipArticolVariante} ------------------------------------------------------ si i sij sunt numele selectorilor componentelor tipurilor constitutive ti respectiv ti,j ,

sn este numele cmpului discriminator avnd tipul tn . Constantele c1,.......,cm sunt valori ale tipului (scalar) tn . O variabil x a tipului TipArticolVariante const din componentele x.s1, x.s2,...,x.sn, x.sk,1,...,x.sk,nk dac i numai dac valoarea curent a cmpului discriminator x.sn = ck .

Componentele x.s1,..., x.sn reprezint partea comun a celor m variante.

Deoarece utilizarea selectorului de component x.sk,h (1 < h < nk) n condiiile n care x.sn <> ck reprezint o grav eroare de programare, se recomand gruparea operaiilor de prelucrare corespunztoare variantelor individuale, n instrucii selective (CASE) a cror structur reflect ntocmai structura tipului articol cu variante prelucrat [1.4.3.1.d]. -----------------------------------------------------------{Exemplu de utilizare a unui articol cu variante} VAR x: TipArticolVariante;

BEGIN {secvena de prelucrare} . . . CASE x.sn OF c1: S1 ; c2: S2 ; ... cm: Sm END;{CASE} [1.4.3.1.d] .... END; {secvena de prelucrare} -----------------------------------------------------------

Sk reprezint o secven de program corespunztoare cazului n care x trebuie prelucrat n varianta k, adic este selectat spre execuie numai cnd cmpul indicator x.sn are valoarea ck . n aceast situaie singura grij a programatorului este aceea ca fiecare secven Sk s conin selectori referitori numai la varianta k: x.sk,1, x.sk,2,..., x.sk,nk Echivalentul mai simplu al unui articol cu variante din Pascal este union n limbajul C. n secvena [1.4.3.1.e] se prezint un exemplu de astfel de structur.

-----------------------------------------------------------// Articol cu variante - implementare C struct { char *nume; int fanioane; int tip_uniune; //cmp discriminator union { int ival; [1.4.3.1.e] float rval; char strval; } u; } tab_simb[NRSIMB] -----------------------------------------------------------Exemplul 1.4.3.1. Prelucrarea articolelor cu variante. Considernd tipul de date coordonate definit n secvena [1.4.3.1.a], se cere s se calculeze distana dintre punctele a i b,ambele de tip coordonate, indiferent de maniera n care sunt exprimate. -----------------------------------------------------------{Exemplu de prelucrare a unui articol cu variante - Pascal} {VAR a,b: coordonate; CASE a.fel OF [1.4.3.1.f] cartezian: CASE b.fel OF cartezian: d:= Sqrt(Power((a.x-b.x),2.0) +Power((a.y-b.y),2.0)); polar: d:= Sqrt(Power((a.x-b.r*Cos(b.fi)),2.0) +Power((a.y-b.r*Sin(b.fi)),2.0)) END; polar: CASE b.fel OF cartezian: d:= Sqrt(Power((a.r*Cos(a.fi)-b.x),2.0) +Power((a.r*Sin(a.fi)-b.y),2.0)); polar: d:=Sqrt(Power((a.r),2.0)+Power((b.r),2.0) +2*a.r*b.r*Cos(a.fi-b.fi))

END END; ----------------------------------------------------------1.4.4. Structura mulime. Tipul de date abstract mulime Cea de-a treia structur fundamental alturi de tablou i articol este structura mulime, care se definete astfel: -----------------------------------------------------------TYPE TipMultime = SET OF TipDeBaza; [1.4.4.a] ----------------------------------------------------------- Valorile posibile ale unei variabile x a tipului TipMultime, sunt mulimi de elemente ale lui TipDeBaza.

Se numete mulime de baz mulimea tuturor elementelor lui TipDeBaza. Mulimea tuturor submulimilor de elemente ale lui TipDeBaza formeaz puterea mulimii de baz. Tipul TipMultime are ca domeniu de valori, puterea mulimii de baz asociat lui TipDeBaza. Cu alte cuvinte fiind dat mulimea de baz, prin mulime vom nelege orice submulime a acesteia, inclusiv mulimea vid, respectiv orice element al puterii mulimii de baz corespunztoare

Spre exemplu dac se alege drept mulime de baz {a,b,c}, atunci se pot utiliza urmtoarele opt submulimi drept constante ale tipului mulime asociat tipului de baz [1.4.4.b]. -----------------------------------------------------------TYPE TipMultime = SET OF (a,b,c); [1.4.4.b] []; [a]; [b]; [c]; [a,b]; [a,c]; [b,c]; [a,b,c]; -----------------------------------------------------------n secvena [1.4.4.c] se prezint cteva exemple de definire a unor tipuri de date mulime, de instaniere i de utilizare a unor structuri de date asociate. -----------------------------------------------------------TYPE TipCuloare = (alb,rosu,portocaliu,galben,verde, albastru,maro,negru); multimeLitere = SET OF char; multimeCifre = SET OF '0'..'9'; setOperatori = SET OF('+','-','*','/'); culori = SET OF TipCuloare;

VAR l1,l2: multimeLitere; c1,c2: multimeCifre; [1.4.4.c] o1: setOperatori; cul1,cul2: culori; paleta: ARRAY[1..10] OF culori; {tablou de multimi} BEGIN {exemplu de utilizare} ... l1:= ['a','b','d']; l2:= ['a','b','c','d','e','f'] c1:= []; c2:= ['0','4','5','7'] o1:= ['+','*']; cul1:= [rosu,maro,negru]; cul2:= [alb]; paleta[3]:= [galben,verde,rosu,portocaliu];

... END; {exemplu de utilizare} ------------------------------------------------------- Cardinalitatea unui tip mulime este: Card(TipMultime)=2Card(TipDeBaza)

Aceast formul poate fi dedus simplu din faptul c fiecare dintre elementele lui TipDeBaza (al cror numr este egal cu cardinalitatea lui TipDeBaza), poate fi reprezentat printr-una din valorile "absent" sau "prezent" i c toate elementele sunt independente unele fa de altele. n cadrul tipului mulime se definesc urmtoarele legi de compoziie intern, valabile pentru dou mulimi de acelai tip:

asignare(:=), reuniune (OR sau +), scdere (-) , intersecie (AND sau *), negare (NOT).

De asemenea se mai definesc i operatorii relaionali care conduc la valori booleene:


egalitate(=), inegalitate (<>), incluziune (<=), incluziune invers (>=).

Dac e este o expresie aparinnd lui TipDeBaza i m este o variabil de tip mulime avnd asociat acelai tip de baz, atunci este definit i relaia de apartenen, notat cu e IN m .

Din punct de vedere formal tipul de date abstract mulime se poate defini ca i n secvena [1.4.4.d] iar n secvna [1.4.4.e] apare un exemplu de implementare n limbajul Pascal. -----------------------------------------------------------TDA Multime Modelul matematic: elementele aparin unui tip ordinal finit i sunt membre ale unei mulimi definite n sens matematic. Notaii: [1.4.4.d] TipElement - tipul de baza; S,T,V - mulimi cu elemente de TipElement; e - valoare (obiect) de TipElement; b - valoare boolean. Operatori: CopiazMulime(S,T) - procedur care copiaz mulimea T n mulimea S; b:= EgalitateMultime(S,T) - funcie care returneaz true dac S este egal cu T; b:= AparineMulime(S,e) - funcie care returneaz true dac e este membru al lui S; b:= Submulime(S,T) - funcie care returneaz true dac S este submulime a lui T; V:= Reuniune(S,T) - funcie care returneaz mulimea V ca reuniune a mulimilor S i T; V:= Intersecie(S,T) - funcie care returneaz

mulimea V ca i intersecie a lui S i T; V:= Diferena(S,T) - funcie care returneaz mulimea V ca i diferen a lui S i T; MulimeVid(S) - procedur care-l face pe S identic cu mulimea vid; S:= CreazMulime(e) - funcie care creeaz mulimea S care conine doar elementul e. -----------------------------------------------------------{Implementare a tipului mulime - varianta Pascal} TYPE TipElement = ... ; TipMulime = SET OF TipElement; VAR S,T,V: TipMultime; e: TipElement; b: boolean; [1.4.4.e]

{CopiazMulime(S,T)} S:= T; {EgalitateMulime(S,T)} b:= (S=T); {AparineMulime(S,e)} b:= (e IN S); {Submulime(S,T)} b:= (S<=T); {Reuniune(S,T)} V:= S+T; {Intersecie(S,T)} V:= S*T; {Diferen(S,T)} V:= S-T; {MulimeVid(S)} S:= []; {CreazMulime(e)} S:= [e]; ---------------------------------------------------------- Exemplu 1.4.4.a. Construcia unei mulimi elementare. O valoare a unei variabile de tip mulime poate fi construit n dou moduri: o Static prin asignarea variabilei cu o constant a tipului respectiv [1.4.4.f]; o Dinamic prin asignarea variabilei cu o expresie de calcul avnd drept operanzi, mulimi ncadrate n acelai tip de baz. n acest caz trebuie s se porneasc ns de la o mulime dat prin elementele sale, numit mulime elementar [1.4.4.g]. -----------------------------------------------------------{Construcia static a unei mulimi} VAR litera: multimeLitere; cifra: multimeCifre; BEGIN ... [1.4.4.f] litera:=['a'..'z']; cifra:=['0'..'9']; ... END; -----------------------------------------------------------{Construcia dinamic a unei mulimi} VAR litera: multimeLitere; cifra: multimeCifre; ch: char; BEGIN litera:= []; {multimi elementare} cifra:= []; [1.4.4.g] FOR ch:= 'a' TO 'z' DO litera:= (litera + [ch]); FOR ch:= '0' TO '9' DO cifra:= (cifra + [ch]); ...

END; -----------------------------------------------------------1.4.5. Structura secven. Tipul de date abstract secven

Caracteristica comun a structurilor de date prezentate pn n prezent (tablouri, articole, mulimi) este aceea c toate au cardinalitatea finit, respectiv cardinalitatea tipurilor lor componente este finit.

Din acest motiv, implementarea lor nu ridic probleme deosebite.

Cele mai multe dintre aa numitele structuri avansate: secvenele, listele, arborii, grafurile, etc, sunt caracterizate prin cardinalitate infinit.

Aceast diferen fa de structurile clasice este de profund importan avnd consecine practice semnificative.

Spre exemplu structura secven avnd tipul de baz T0 se definete dup cum urmeaz:

-----------------------------------------------------------S0=< > (secvena vid) Si=<Si-1,si> unde 0 < i i si T0 ----------------------------------------------------------- Cu alte cuvinte, o secven cu tipul de baz T0 , este: o Fie o secven vid o Fie o concatenare a unei secvene (cu tipul de baz T0) cu o valoare (si) a tipului T0 .
Definirea recursiv a unui tip secven conduce la o cardinalitate infinit.

o Fiecare valoare a tipului secven conine n realitate un numr finit de componente de tip T0 , o Acest numr este nemrginit deoarece, pornind de la orice secven se poate construi o secven mai lung.

O consecin important a acestui fapt este:

Volumul de memorie necesar reprezentrii unei structuri avansate, nu poate fi cunoscut n momentul compilrii, Ca atare, este necesar aplicarea unor scheme de alocare dinamic a memoriei, n care memoria este alocat structurilor care "cresc" i este eliberat de ctre structurile care "descresc".

Pentru a implementa aceast cerin, este necesar ca limbajul superior utilizat n implementare s fie prevzut cu acces la funcii sistem care permit:

Alocarea / eliberarea dinamic a memoriei Legarea / referirea dinamic a componentelor

n aceste condiii cu ajutorul instruciilor limbajului pot fi descrise i utilizate structuri avansate de date.

Tehnicile de generare i de manipulare a unor astfel de structuri avansate sunt prezentate n capitolele urmtoare ale cursului Structura secven este din acest punct de vedere o structur intermediar;

Ea este o structur avansat din punctul de vedere al cardinalitii care este infinit Este ns att de curent utilizat nct includerea sa n mulimea structurilor fundamentale este consacrat. Aceast situaie este influenat i de faptul c alegerea unui set potrivit de operatori referitori la structura secven, permite implementatorilor s adopte reprezentri potrivite i eficiente ale acestei structuri. Drept consecin, mecanismele alocrii dinamice a memoriei devin suficient de simple pentru a permite o implementare eficient, neafectat de detalii, la nivel de limbaj superior.

1.4.5.1. Tipul de date abstract secven

Includerea structurii secven n rndul structurilor fundamentale, presupune restrngerea setului de operatori de o asemenea manier nct se permite numai accesul secvenial la componentele structurii. Aceast structur este cunoscut i sub denumirea de fiier secvenial sau pe scurt fiier. Structura secven este supus unicei restricii de a avea componente de acelai tip, cu alte cuvinte este o structur omogen. Numrul componentelor, denumit i lungime a secvenei, se presupune a fi necunoscut att n faza de compilare, ct i n faza de execuie a codului.

Mai mult chiar, acest numr nu se presupune constant el putndu-se modifica n timpul execuiei.

Cardinalitatea tipului secven este n consecin infinit. n dependen de maniera de implementare a structurilor de tip secven, acestea se nregistreaz de regul pe suporturi de memorie extern reutilizabile cum ar fi benzile magnetice sau discurile n alocare secvenial. Acest lucru face posibil tratarea relativ simpl a structurilor de tip secven i permite ncadrarea lor n rndul structurilor fundamentale, dei de drept ele aparin structurilor dinamice. Secvena este o structur ordonat. Ordonarea elementelor structurii este stabilit de ordinea n timp a crerii componentelor individuale. Datorit mecanismului de acces secvenial preconizat, selectarea prin indici a componentelor individuale devine improprie. n consecin la definirea unui tip de secven se precizeaz numai tipul de baz. TYPE TipSecven = FILE OF TipDeBaz;

n cadrul unei secvene definite ca mai sus n orice moment este accesibil o singur component, denumit component curent, care este precizat printr-un pointer (indicator) asociat secvenei.

Acest indicator avanseaz secvenial, practic dup execuia oricrei operaii asupra secvenei.

n plus oricrei secvene i se asociaz un operator boolean standard sfrit de fiier notat cu Eof(f:TipSecventa) care permite sesizarea sfritului secvenei. Modul concret de acces la componente, precum i posibilitile de prelucrare efectiv, rezult din semantica operatorilor definii pentru tipul de date abstract secven este prezentat sintetic n [1.4.5.1.a] . Implementrile recente ale limbajelor Pascal i C introduc n setul de instruciuni accesibile programatorului i operatori pentru prelucrarea secvenelor implementate ca i fiiere secveniale. n secvena [1.4.5.1.b] apare o astfel de implementare definit n limbajul Pascal. Variante asemntoare stau la dispoziie i n limbajul C.

-----------------------------------------------------------TDA Secven Modelul matematic: secven de elemente de acelai tip. Un indicator la secven indic elementul urmtor la care se poate realiza accesul. Accesul la elemente este strict secvenial. Notaii: TipElement - tipul unui element al secvenei. Nu poate fi de tip secven; f - variabil secven; e - variabil de TipElement; [1.4.5.1.a] b - valoare boolean; numeFisierDisc - ir de caractere. Operatori: Atribuie(f,numeFisierDisc) - atribuie variabilei secven f numele unui fiier disc precizat; Rescrie(f) - procedur care mut indicatorul secvenei la nceputul lui f i deschide fiierul f n regim de scriere. Dac fiierul f nu exist el este creat. Daca f exist, vechea sa variant se pierde i se creeaz un nou fiier f vid; ResetSecven(f) - procedur care mut indicatorul la nceputul secvenei f i deschide secvena n regim de consultare. Dac f nu exist se semnaleaz eroare de execuie; DeschideSecven(f) - procedur care n anumite implementri joac rol de rescrie sau reset de secven; b:= Eof(f) - funcie care returneaz valoarea true dac indicatorul secvenei indic marcherul de sfrit de fiier al lui f; FurnizeazSecven(f,e) - procedur care acioneaz n regim de consultare. Atta vreme ct Eof(f) este fals, furnizeaz n e urmtorul element al secvenei f i avanseaz indicatorul acesteia;

DepuneSecven(f,e) - pentru secvena f deschis n regim de scriere, procedura copiaz valoarea lui e n elementul urmtor al secvenei f i avanseaz indicatorul acesteia; Adaug(f) - procedur care deschide secvena f n regim de scriere, poziionnd indicatorul la sfritul acesteia, cu posibilitatea de a aduga elemente noi numai la sfritul secvenei. InchideSecvena(f) - nchide secvena. -----------------------------------------------------------{Implementarea tipului secven - Varianta Pascal} TYPE TipElement = ... ; TipSecven = TEXT; f: TipSecven; [1.4.5.1.b] e: TipElement; numeFisierDisc: string; {Atribuie(f,numeFisierDisc)} assign(f,numeFisierDisc) {Rescrie(f)} rewrite(f) {DepuneSecven(f,e)} write(f,e) ; writeln(...) {ResetSecven(f)} reset(f) {Eof(f)} eof(f) {FurnizeazSecven(f,e)} read(f,e); readln(...) {Adaug(f)} append(f) {InchideSecven(f)} close(f) -----------------------------------------------------------VAR
1.4.5.2. Tipul de date abstract fiier cu acces direct

Principalul dezavantaj al structurii secven const n faptul c accesul la componentele sale se realizeaz n manier strict secvenial prin intermediul unui pointer a crui poziionare este inaccesibil programatorului n mod direct. Din acest motiv, pornind de la tipul de date abstract secven, a fost dezvoltat o structur mai flexibil numit fiier cu acces direct. Astfel, unui fiier cu acces direct i se asociaz un indicator prin intermediul cruia pot fi accesate oricare din componentele fiierului. n acest scop fiecare component a fiierului este identificat printr-un indice care i precizeaz poziia n fiier exact ca i la structura tablou.

Numerotarea componentelor ncepe cu 0,1,2,... i continu pn la DimensiuneFiier-1.

Crearea i modificarea structurii fiier cu acces direct se realizeaz ns tot n manier secvenial.

Cu alte cuvinte, se pot aduga sau modifica nregistrri dar nu se pot insera altele noi ntre cele existente.

Tipul de date abstract asociat acestei structuri apare n secvena [1.4.5.2.a] iar n secvena [1.4.5.2.b] un exemplu de implementare Pascal. -----------------------------------------------------------TDA Fiier cu acces direct Modelul matematic: secven de elemente de acelai tip. Un indicator asociat fiierului indic elementul la care se

poate realiza accesul. Acesta poate fi oricare element al structurii. Fiecare component a structurii este identificat printr-un indice. Numartoarea ncepe cu 0,1,2,... i continu pn la DimensiuneFisier-1. Notaii: TipElement - tipul unui element al fiierului. Nu poate fi de tip fiier; f - fiier cu acces direct; e - variabil de TipElement; [1.4.5.2.a] i - indice n fiier. Operatori: Atribuie(f,numeFisierDisc) - atribuie variabilei fiier f numele unui fiier disc precizat; Rescrie(f) - procedur care mut indicatorul fiierului la nceputul acestuia. Daca fiierul f nu exist el este creat. Daca f exist, vechea sa variant se pierde i se creeaz un nou fiier f vid. Indicatorul asociat fiierului ia valoarea 0; Reset(f) - procedur care mut indicatorul la nceputul fiierului f. Indicatorul ia valoarea 0. Daca f nu exist se semnaleaz eroare; DeschideFisier(f) - procedur care n anumite implementri joac rol de rescrie sau reset de fiier; i:= DimensiuneFisier(f) - funcie care returneaz numrul curent de componente al fiierului f (numrul de componente memorate n f); i:= PoziieFiier(f) - funcie care returneaz numrul nregistrrii curente (cea precizat de indicatorul fiierului). La deschiderea unui fiier PoziieFiier ia valoarea 0; Caut(f,i) - selecteaz o nregistrare precizat printrun numr poziionnd indicatorul fiierului pe nregistrarea respectiv; FurnizeazFiier(f,e) - procedur care furnizeaz n e elementul curent al fiierului f i avanseaz indicatorul acestuia; Depune(f,e) - procedur care depune valoarea lui e n fiierul f n poziia precizat de indicatorul fiierului i avanseaz indicatorul la elementul urmtor; nchideFisier(f) - nchide fiierul f. -----------------------------------------------------------{Implementarea tipului secven cu acces direct- Varianta Pascal} TYPE TipElement = ... ; TipFisierAccesDirect = FILE OF TipElement; TipIndice = integer; VAR f: TipFisierAccesDirect; e: TipElement; i: TipIndice;

[1.4.5.2.b] rewrite(f) reset(f) i:= filesize(f)

{Rescrie(f)} {Reset(f)} {i:= DimensiuneFisier(f)}

{i:= PozieFiier(f)} i:= filepos(f) {Caut(f,i)} seek(f,i) {DepuneFiier(f,e)} write(f,e); writeln(...); {FurnizeazFiier(f,e)} read(f,e); readln(...); {nchideFiier(f)} close(f) -----------------------------------------------------------

Variante similare de implementare ale TDA secven exist i pentru limbajele C respectiv C++. Dac la nivel principial conceptele prezentate rmn valabile, la nivel de implementare pot s apar diferenieri importante.

2. Noiunea de algoritm. Analiza algoritmilor


2.1. Noiunea de algoritm
Termenul algoritm i are sorgintea n numele autorului persan Abu Ja'far Mohamed Ibn Musa Al Khowarismi care n anul 825 .e.n. a redactat un tratat de matematic n care prezenta pentru prima dat metode de rezolvare generice pentru anumite categorii de probleme. Dicionarul explicativ al limbii romne [DEX75] ofer urmtoarea definiie: "Ansamblu de simboluri i de operatori, folosii n matematic i logic, permind gsirea n mod mecanic (prin calcul) a unor rezultate". Alte dicionare ofer alte definiii, spre exemplu: "Orice metod de rezolvare a unei anumite probleme". n activitatea de programare vom conveni ca prin algoritm s nelegem o "modalitate de rezolvare a unei probleme utiliznd un sistem de calcul". Un algoritm este de fapt o metod sau o reet pentru a obine un rezultat dorit. Un algoritm const din: o Un set de date iniiale care abstractizeaz contextul problemei de rezolvat o Un set de relaii de transformare care sunt operate pe baza unor reguli al cror coninut i a cror succesiune reprezint nsi substana algoritmului o Un set de rezultate preconizate sau informaii finale, care se obin trecnd de regul printr-un ir de informaii (rezultate) intermediare. Un algoritm se bucur de urmtoarele proprieti: o Generalitate - un algoritm nu rezolv doar o anume problem ci o clas generic de probleme de acelai tip; o Finitudine - informaia final se obine din cea iniial trecnd printr-un numr finit de transformri; o Unicitate - transformrile i ordinea n care ele se aplic sunt univoc determinate de regulile algoritmului. Ori de cte ori se aplic acelai algoritm asupra aceluiai set de date iniiale se obin aceleai rezultate. Scopul capitolului: analiza performanei algoritmilor.

2.2. Analiza algoritmilor


La ce servete analiza algoritmilor ? o Permite precizarea predictiv a comportamentului algoritmului; o Prin analiz pot fi comparai diferii algoritmi i poate fi astfel ierarhizat experiena i ndemnarea diverilor productori [HS78].

Analiza algoritmilor se bazeaz de regul pe ipoteze: o Sistemele de calcul sunt considerate convenionale adic ele execut cte o singur instrucie la un moment dat o Timpul total de execuie al algoritmului rezult din nsumarea timpilor instruciilor individuale componente. o Desigur aceast ipotez nu este valabil la sistemele de calcul paralelele i distribuite. n astfel de cazuri aprecierea performanelor se realizeaz de regul prin msurtori experimentale i metode statistice.

n general, analiza unui algoritm se desfoar n dou etape: o (1) Analiza aprioric Const n aprecierea din punct de vedere temporal a operaiilor care se utilizeaz i a costului lor relativ. Conduce de regul la stabilirea expresiei unei funcii care mrginete timpul de execuie al algoritmului

o (2) Testul ulterior Const n stabilirea unui numr suficient de seturi de date iniiale care s acopere practic toate posibilitile de comportament ale algoritmului Testarea comportamentul algoritmului pentru fiecare set n parte Se finalizeaz prin culegerea unor date n baza crora pot fi elaborate o serie de statistici referitoare la consumul de timp specific execuiei algoritmului n cauz (profilul algoritmului).

Concluzie: o Analiza aprioric are drept scop principal determinarea teoretic a ordinului de mrime al timpului de execuie al unui algoritm
o

Testul ulterior are ca scop principal determinarea efectiv a acestui ordin prin stabilirea profilului algoritmului

2.3. Notaii asimptotice


Ordinul de mrime al timpului de execuie al unui algoritm: o Reprezint o msur a eficienei algoritmului o Permite compararea relativ a variantelor de algoritmi. Studiul eficienei asimptotice a unui algoritm, se realizeaz utiliznd mrimi de intrare cu dimensiuni suficient de mari pentru a face relevant numai ordinul de mrime al timpului de execuie al algoritmului.

Intereseaz cu precdere limita la care tinde timpul de execuie al algoritmului odat cu creterea nelimitat a dimensiunii intrrii. De regul, un algoritm care este asimptotic mai eficient dect alii, va constitui cea mai bun alegere i pentru intrri de dimensiuni mici i foarte mici.

2.3.1. Notaia (teta) Pentru aprecierea limitei ordinului de mrime al timpului de execuie al unui algoritm se utilizeaz notaia .

Definiie. Fiind dat o funcie g(n), prin (g(n)) se desemneaz o mulime de funcii definit astfel: -----------------------------------------------------------(g(n)) = { f(n): exist constantele pozitive c1 , c2 i n0 astfel nct 0 c1 g(n) f(n) c2 g(n) pentru n n0 } [2.3.1.a] ----------------------------------------------------------- Se spune c o funcie f(n) aparine mulimii (g(n)), dac exist constantele pozitive c1 si c2 , astfel nct ea poate fi cuprins (ca un "sandwich") ntre c1 g(n) i c2 g(n) pentru un n suficient de mare.
c2 g(n)

f(n)
c1 g(n)

n0

Fig.2.3.a. Reprezentarea lui f(n) = (g(n)) Dei (g(n)) reprezint o mulime de funcii, se utilizeaz uzual notaia f(n) = (g(n)), cu semnificaia f(n) este (g(n)) , indicnd astfel faptul c f(n) este membru al lui (g(n) sau c f(n) (g(n)) [2.3.1.b]. -----------------------------------------------------------f(n) = (g(n)) [2.3.1.b] ----------------------------------------------------------- Cu alte cuvinte pentru orice n > n0 , f(n) este egal cu g(n) n interiorul unui factor constant. Se spune ca g(n) este o margine asimptotic strns ("asymptotically tight bound") a lui f(n). Definiia lui necesit ca fiecare membru a lui (g(n)) s fie asimptotic pozitiv, deci f(n) s fie pozitiv pentru valori suficient de mari ale lui n. n practic, determinarea lui n cazul unei expresii, se realizeaz de regul lund n considerare termenii de ordinul cel mai mare i neglijnd restul termenilor.

Aceast afirmaie poate fi demonstrat intuitiv, utiliznd definiia formal a lui n a arta c
1 2 n 3 n = (n 2) 2 o Pentru aceasta, constantele c1 , c2 i n0 trebuiesc determinate astfel nct, pentru orice n n0 s fie valabil relaia: 1 c1 n 2 n 2 3 n c2 n 2 2

o Se mpart membrii inegalitii cu n2 i se obine 1 3 c1 c2 2 n o Inegalitatea din dreapta este valabil pentru orice n 1 dac l alegem pe c2 1/2. o Inegalitatea din stnga este valabil pentru orice valoare a lui n 7 dac se alege c1 1/14 . o Astfel, alegnd c1 = 1/14 , c2 = 1/2 i n0 = 7 se poate verifica simplu c 1 2 n 3 n = (n2) 2 ----------------------------------------------------------- Exemplul 2.3.1. Stabilirea funciei g(n) pentru o funcie polinomial. Pornind de la observaia ca termenii de ordin inferior ai unei funcii asimptotice pozitive pot fi neglijai Dac se alege pentru c1 o valoare care este cu puin inferioar coeficientului termenului celui mai semnificativ Dac se alege pentru c2 o valoare uor superioar aceluiai coeficient Inegalitile impuse de notaia sunt satisfcute. Coeficientul termenului celui mai semnificativ poate fi n continuare omis, ntruct el modific pe c1 i pe c2 doar cu un factor constant egal cu coeficientul. Fie spre exemplu funcia polinomial:

f (n)= an +bn + c

a>0

o Neglijnd termenii de ordin inferior lui 2, se ajunge la concluzia ca f(n) = (n 2). o Acest lucru poate fi demonstrat i formal alegnd pentru coeficieni urmtoarele valori: ----------------------------------------------------------- b c 7a a , c1 = , c2 = i n0 = 2 max [2.3.1.c] 4 4 a a ------------------------------------------------------------

n baza acelorai considerente, pentru orice funcie polinomial p(n) de ordinul n unde ai este constant i a d > 0, este valabil afirmaia p(n) = (n d) ( [2.3.1.d]).

-----------------------------------------------------------Dac

p(n) =

atunci p(n) = (n d ) [2.3.1.d] ----------------------------------------------------------- Deoarece o funcie polinominal de grad zero este o constant, despre orice funcie constant se poate spune ca este (n 0) sau (1). Dei acesta este un abuz de interpretare (deoarece n nu tinde la infinit), prin convenie (1) desemneaz fie o constant fie o funcie constant n raport cu o variabil. -----------------------------------------------------------

i=0

ai ni

unde

ai= constant

i a d > 0

2.3.2. Notaia O (O mare) Notaia O desemneaz marginea asimptotic superioar a unei funcii. Pentru o funcie dat g(n), se definete O(g(n)) ca i mulimea de funcii: -----------------------------------------------------------O(g(n)) = { f(n): exist constantele pozitive c i n0 astfel nct 0 f(n) c g(n) pentru n n0 } [2.3.2.a] ----------------------------------------------------------- Notaia O se utilizeaz pentru a desemna o margine superioar a unei funcii n interiorul unui factor constant. Pentru toate valorile n superioare lui n0 , valoarea funciei f(n) este pe sau dedesubtul lui g(n) (fig.2.3.b ).
c g(n)

f(n)

n0

Fig.2.3.b. Reprezentarea lui f(n) = O(g(n)) Pentru a preciza c o funcie f(n) este membr a lui O(g(n)) se utilizeaz notaia f(n) = O(g(n)). Faptul c f(n) este (g(n)) implic c f(n) = O(g(n)) deoarece notaia este mai puternic dect notaia O. Formal acest lucru se precizeaz prin relaia [2.3.2.b].

-----------------------------------------------------------(g(n)) O(g(n)) [2.3.2.b] ----------------------------------------------------------- n consecin, deoarece s-a demonstrat faptul c orice funcie ptratic a n 2 + b n + c , a > 0 este (n 2), n baza relaiei [2.3.2.b] rezult ca aceasta funcie este implicit i O(n 2). Este surprinztor faptul c din aceleai considerente, funcia liniara a n + b este de asemenea O(n 2), lucru uor de verificat dac se alege c = a + | b | i n0 = 1. Notaia O este de obicei cea mai utilizat n aprecierea timpului de execuie al algoritmilor respectiv a performanei acestora. Ea poate fi uneori apreciat direct din inspectarea structurii algoritmului, spre exemplu existena unei bucle duble conduce imediat la o margine de ordinul O(n 2) Deoarece notaia O descrie o margine superioar, cnd este utilizat pentru a mrgini cazul cel mai defavorabil de execuie al unui algoritm, prin implicaie ea mrginete superior comportamentul algoritmului n aceeai msur pentru orice intrare. In cazul notaiei lucrurile difer. o Spre exemplu, daca (n2) mrginete superior cel mai defavorabil timp de execuie al unui algoritm de sortare prin inserie, aceasta nu nseamn c (n2) mrginete orice intrare. o Astfel, dac intrarea este gata sortat, algoritmul dureaz un timp proporional cu (n). Tehnic vorbind, afirmaia c timpul de execuie al unui algoritm de sortare este O(n2) constituie un abuz de limbaj. Corect: indiferent de configuraia intrrii de dimensiune n, pentru orice valoare a lui n timpul de execuie al algoritmului pentru setul corespunztor de intrri este O(n2).

Cele mai obinuite ordine de mrime ale notaiei O se afl n relaiile de ordine prezentate n [2.3.2.c], iar reprezentarea lor grafic la scar logaritmic apare n fig.2.3.c. -----------------------------------------------------------O(1) < O(log n) < O(n) < O(n log n) < O(n2) < O(n3) < < O(2n) < O(10 n) < O( n ! ) < O(n n) [2.3.2.c]

-----------------------------------------------------------2 216
n

n2

n3

212 28 24 log 2 (n) 2


1

n log 2 (n) n

20

21

22

23

24

25

26

Fig. 2.3.c. Ordine de mrime ale notaiei O n acelai context n [2.3.2.d] se prezint cteva sume ntregi utile care sunt frecvent utilizate n calculul complexitii algoritmilor. -----------------------------------------------------------n n (n + 1) i = 2 = O( n2 ) i =1

i2
i
i =1
n i =1 n

=
=
=

i =1 n

n (n + 1) (2 n + 1) 3 = O( n ) 6
n 2 (n 2 + 1) 4 = O( n ) 4
n n ) = O (n k +1 ) + = O( k +1 k +1
n 1 i =1
k +1 k +1

[2.3.2.d]

ik
i= 2

(i 1) = i

-----------------------------------------------------------------

2.3.3. Aritmetica ordinal a notaiei O n vederea aprecierii complexitii algoritmilor n raport cu notaia O, a fost dezvoltat o aritmetic ordinal specific care se bazeaz pe o serie de reguli formale [De89]. Aceste reguli sunt prezentate n continuare. o Regula 1. O(k) < O(n) pentru k . o Regula 2. Ignorarea constantelor: k f(n) = O(f(n)) pentru k i f sau O( k f ) = O( f )

o Regula 3. Tranzitivitate: dac f(n) O(g(n)) i g(n) O(h(n)) atunci f(n) O(h(n)) -----------------------------------------------------------Demonstraie: f(n) = O(g(n)) c1 , n1 f(n) c1 g(n) pentru n > n1 g(n) = O(h(n)) c2 , n2 g(n) c2 h(n) pentru n > n2 Se alege n3 = max {n1 , n2 }. n relaia f(n) c1 g(n) se nlocuiete g(n) cu expresia de mai sus. Se obine relaia: f(n) c1 ( c2 h(n)) Alegnd c3 = c1 c2 se obine f(n) c3 h(n) pentru n > n3 deci f(n) = O(h(n)). -----------------------------------------------------------o Regula 4. f(n) + g(n) = O( max { f(n), g(n) } o Regula 5. Dac f1(n) = O( g 1(n)) i f 2 (n) = O( g 2(n)) atunci f1(n) f 2 (n) = O( g 1(n) g 2(n)) Utiliznd aceste reguli se poate calcula o estimare a lui O pentru o expresie aritmetic dat, fr a avea valori explicite pentru k i n. ----------------------------------------------------------- Exemplul 2.3.3. Se consider expresia 8 n log(n) + 4 n 3 / 2 i se cere o estimare a sa n termenii notaiei O. Se procedeaz dup cum urmeaz: o log (n) = O(n1/ 2) deoarece logaritmul unui numr este mai mic dect orice putere pozitiv a numrului n baza urmtoarei teoreme: o Fie a > 0 i a 1 ; o Pentru exponent d > 0 exist un numr N astfel nct pentru x > N avem log a x < x d. o n log(n) = O(n n 1/2) = O(n 3/2) o 8 n log(n) = O(n 3/2) o 4 n 3/2 = O(n 3/2) o 8 n log(n) + 4 n 3/2 = O(max { 8 n log(n), 4 n 3/2}) = =O(max { n 3/2, n 3/2} ) = O(n 3/2) (Regula 5). (Regula 2). (Regula 2).

(Regula 4).

Rezult n urma estimrii c expresia 8 n log(n) + 4 n 3/2 este O(n 3/2).

------------------------------------------------------------------------

2.3.4. Notaia (Omega mare) Notaia (omega mare) precizeaz o margine asimptotic inferioar. Pentru o funcie dat g(n), prin (g(n)) se precizeaz mulimea funciilor

-----------------------------------------------------------(g(n)) = {f(n) : exist constantele pozitive c si n 0 astfel nct pentru n n0 } [2.3.4.a] 0 c g(n) f(n) -----------------------------------------------------------f (n)

c g(n)

n0

Fig. 2.3.d. Reprezentarea lui f(n)= (g)n)) ----------------------------------------------------------- Teorem: Pentru oricare dou funcii f(n) i g(n) , f(n) = (g(n)) dac i numai dac f(n) = O(g(n)) i f(n) = (g(n)) . [2.3.4.b] ----------------------------------------------------------- Spre exemplu s-a demonstrat c a n 2 + b n + c = (n 2) pentru orice valori ale constantelor a, b, c cu a > 0 . De aici rezult imediat c a n 2 + b n + c = (n 2) . Deoarece notaia descrie o limit inferioar, atunci cnd este utilizat pentru a mrgini cazul cel mai favorabil de execuie al unui algoritm, prin implicaie ea mrginete inferior orice intrare arbitrar a algoritmului.

2.3.5. Utilizarea notaiilor asimptotice , O i Exemplul a). Se consider formula 2 n 2 + 3 n + 1 = 2 n 2 + ( n ) interpreteaz aceast formul ? . Cum se

o n general, cnd ntr-o formul apare o notaie asimptotic se consider c ea ine locul unei funcii care nu se nominalizeaz explicit. o Astfel n exemplul de mai sus avem: , unde 2 n 2 + 3 n + 1 = 2 n 2 + f( n ) o Se observ faptul c n acest caz (n).

f( n ) ( n ) .

f( n ) = 3 n + 1 , care este ntr-adevr

Exemplul b). Utilizarea notaiei asimptotice poate fi de folos n eliminarea detaliilor neeseniale ale unei ecuaii. o Spre exemplu fie relaia recursiv: T ( n ) = 2 T ( n / 2 ) + ( n )

o Dac prezint interes doar comportamentul asimptotic al lui T(n), nu e necesar s fie specificai toi termenii de ordin inferior. o Ei sunt inclui ntr-o funcie anonim, precizat prin (n). Exemplul c). Ecuaia 2 n 2 + ( n ) = ( n 2 ) se interpreteaz astfel: indiferent de maniera n care se alege funcia anonim din partea stng a ecuaiei, exist o modalitate de a alege funcia anonim din partea dreapt, astfel nct semnul "=" s indice o ecuaie adevrat. o n cazul de fa, pentru orice funcie g( n ) ( n 2 ) , astfel nct 2 n 2 + f( n ) = g( n )
f( n ) ( n ) , exist o funcie pentru orice n.

o De fapt membrul drept al ecuaiei evideniaz un grad mai redus de detaliere dect cel stng. Exemplul d). Relaiile de acest tip pot fi nlnuite, spre exemplu:

2 n 2 + 3 n + 1 = 2 n 2 + ( n ) = ( n 2 )
o Prima ecuaie precizeaz faptul c exist o funcie f( n ) ( n ) pentru toi n. 2 n 2 + 3 n + 1 = 2 n 2 + f( n ) astfel nct

o A doua ecuaie precizeaz c pentru orice g( n ) ( n ) exist o funcie h( n ) ( n 2 ) astfel nct 2 n 2 + g( n ) = h( n) pentru toi n. o Aceast interpretare implic 2 n 2 + 3 n + 1 = ( n 2 ) sugereaz intuitiv lanul de egaliti. 2.3.6. Notaia o (o mic) Marginea asimptotic superioar desemnat prin notaia O, poate fi din punct de vedere asimptotic strns sau lejer (lax). Pentru desemnarea unei margini asimptotice lejere se utilizeaz notaia o (o mic). ceea ce de fapt

----------------------------------------------------------o(g(n)) = { f(n) : pentru orice constant pozitiv c > 0 exist o constant n 0 > 0 astfel nct 0 f(n) < c g(n) pentru n n0 } [2.3.6.a] ---------------------------------------------------------- Principala diferen dntre notaiile O i o rezid n faptul c n cazul f(n) = O(g(n)), marginea 0 f(n) c g(n) este valabil pentru anumite constante c > 0, n timp ce f(n) =o(g(n)), marginea 0 g(n) < c g(n) este valabil pentru orice constant c > 0 . Intuitiv, n notaia o, funcia f(n) devine nesemnificativ n raport cu g(n) cnd n tinde la infinit [2.3.6.b]. ----------------------------------------------------------- f(n) = o(g(n)) implic

f(n) =0 n g(n) lim

[2.3.6.b]

-----------------------------------------------------------2.3.7. Notaia (omega) Prin analogie, notaia este pentru notaia ceea ce este o pentru O. Cu alte cuvinte notaia precizeaz o margine asimptotic inferioar lejer.

-----------------------------------------------------------(g(n)) = { f(n) : pentru orice constant pozitiv c > 0 exist o constant n 0 > 0 astfel nct 0 c g(n) < f(n) pentru n n0 } [2.3.7.a] ------------------------------------------------------------

Proprietate:

Relaia f(n) = (g(n)) implic [2.3.7.b], adic f(n) devine n mod arbitrar semnificativ n raport cu g(n) atunci cnd n tinde la infinit.

------------------------------------------------------------

f(n) [2.3.7.b] = n g(n) -----------------------------------------------------------f(n) = (g(n)) implic

lim

2.3.8. Proprieti ale notaiilor asimptotice Presupunnd c f(n) i g(n) sunt asimptotic pozitive, multe din proprietile relaionale ale numerelor reale se aplic similar comparaiilor asimptotice. -----------------------------------------------------------a) Tranzitivitate: f(n) = (g(n)) i g(n) = (h(n)) implic f(n) = (h(n)) f(n) = O(g(n)) i g(n) = O(h(n)) implic f(n) = O(h(n)) f(n) = (g(n)) i g(n) = (h(n)) implic f(n) = (h(n)) f(n) = o(g(n)) i g(n) = o(h(n)) implic f(n) = o(h(n)) f(n) = (g(n)) i g(n) = (h(n)) implic f(n) = (h(n)) b) Reflexivitate: f(n) = (f(n)) ; f(n) = O(f(n)) ; [2.3.8.a] f(n) = (f(n)) . c) Simetrie: f(n) = (g(n)) dac i numai dac g(n) = (f(n)) d) Simetrie transpus: f(n) = O(g(n)) dac i numai dac g(n) = (f(n)) f(n) = o(g(n)) dac i numai dac g(n)= (f(n)) ----------------------------------------------------------- Analogia dintre comparaia asimptotic a dou funcii f i g i compararea a dou numere reale a i n este prezentat n [2.3.8.b]. ----------------------------------------------------------- f(n)= O(g(n)) a b f(n)= (g(n)) a b f(n)= (g(n)) a=b [2.3.8.b] f(n)= o(g(n)) a<b f(n)= (g(n)) a>b ----------------------------------------------------------- O proprietate specific numerelor reale, care nu se aplic comparaiei asimptotice este trihotomia ("trichotomy"). o Conform acestei proprieti ntre dou numere reale oarecare exist exact una din urmtoarele relaii: a < b ; a = b ; a > b. o Dac oricare dou numere reale pot fi comparate conform relaiei de mai sus, pentru dou funcii f(n) i g(n) este ns posibil ca s nu fie valabil nici f(n) = O(g(n)), nici f(n) = (g(n)).

2.4. Aprecierea timpului de execuie


Cu ajutorul notaiei O mare se poate aprecia timpul de execuie al unui algoritm. Se face sublinierea c se poate aprecia timpul de execuie al unui algoritm abstract i nu al unui program ntruct acesta din urm depinde de mai muli factori: o Dimensiunea i natura datelor de intrare, o Caracteristicile sistemului de calcul pe care se ruleaz programul, o Eficiena codului produs de compilator. Notaia O mare permite eliminarea factorilor care nu pot fi controlai (spre exemplu viteza sistemului de calcul) concentrndu-se asupra comportrii algoritmului independent de program. n general un algoritm a crui complexitate temporal este O(n 2) va rula ca i program n O(n 2) uniti de timp indiferent de limbajul sau sistemul de calcul utilizat. n aprecierea timpului de execuie se pornete de la ipoteza simplificatoare deja enunat, c fiecare instrucie exceptnd eventual apelurile de proceduri sau funcii utilizeaz n medie aceeai cantitate de timp. Singurele instruciuni care nu pot fi ncadrate n aceast medie de timp sunt instruciunea IF, secvenele repetitive (buclele) i apelurile de proceduri i funcii. Presupunnd pentru moment c apelurile de proceduri i funcii se ignor, se adopt prin convenie urmtoarele simplificri: o (1) Se presupune c o instruciune IF va consuma ntotdeauna timpul necesar execuiei ramurii celei mai lungi, dac nu exist raiuni contrare justificate; o (2) Se presupune c ntotdeauna instruciunile din interiorul unei bucle se vor executa de numrul maxim de ori permis de condiia de control. ----------------------------------------------------------- Exemplul 2.4.a. Se consider urmtoarea procedur care caut ntr-un tablou a cu n elemente, un element egal cu cheie i returneaz n variabila unde ultima locaie n care este gsit cheia sau zero n caz contrar [2.4.a]. -----------------------------------------------------------PROCEDURE CautareLiniara(n,cheie: integer, a: TablouNumeric; VAR unde: integer); VAR indice: integer; BEGIN unde: = 0 FOR indice:= 1 TO n DO IF a[indice]= cheie THEN [2.4.a] unde:= indice END; {CautareLiniara} ------------------------------------------------------------

Intrarea n procedur i iniializarea variabilei unde Bucla FOR (n reluri) Instruciunea IF i ramura sa Revenirea din procedur O(1)

O(1) O(1+n+1) = O(n) O(n*1) = O(n)

O(1)

Fig. 2.4.a. Schema temporal a algoritmului [2.4.a] n figura 2.4.a apare schema temporal a algoritmului mpreun cu estimarea O mare corespunztoare. n analiz au fost introduse i operaiile de intrare i revenire din procedur care nu apar ca pri explicite ale rutinei. Pe viitor acestea vor fi omise din analiza temporal deoarece ele contribuie cu un timp constant la execuie i nu influeneaz estimarea O mare

Exemplul 2.4.b. Se consider urmtoarea poriune de cod [2.4.b]. -------------------------------------------------------FUNCTION SumProd(n: integer): integer; VAR rezultat,k,i: integer; BEGIN rezultat:= 0; FOR k:= 1 TO n [2.4.b] FOR i:= 1 TO k rezultat:= rezultat+k*i; SumProd:= rezultat END;{SumProd} -------------------------------------------------------iniializare rezultat bucla FOR (n reluri) bucla FOR (n reluri) interior O(1) O(n*1) = O(n) O(n*n) = O(n2) O(1+n2) = O(n2) O(1)

Fig. 2.4.b. Schema temporal a algoritmului din secvena [2.4.b]

La analiza complexitii temporale se face presupunerea c ambele bucle se reiau de numrul maxim de ori posibil, dei acest lucru nu este adevrat deoarece bucla interioar se execut cu limita k n ( i nu cu limita n) Se va demonstra c prin aceast simplificare estimarea temporal final nu este afectat. La o analiz mai aprofundat se ine cont de faptul c bucla FOR interioar se execut prima oar o dat, a doua oar de 2 ori .a.m.d, a k-oar de k ori.

-----------------------------------------------------------n( n + 1) 1 + 2 + 3 + ... + n = [2.4.c] = O( n 2 ) 2 ----------------------------------------------------------- n mod analog n cadrul secvenei [2.4.d] se ajunge la estimarea: O(n n n) = O(n3). -----------------------------------------------------------FOR i:= 1 TO n DO FOR j:= 1 TO i DO FOR k:= 1 TO i DO [2.4.d] Secven {O(1)}; ----------------------------------------------------------- Calculul exact al numrului de reluri conduce la valoarea precizat de relaia [2.4.e], innd cont de faptul c cele dou bucle interioare se execut de i2 ori la fiecare reluare a buclei exterioare FOR. -----------------------------------------------------------12 + 22 + 32 + ... + n 2 =
n (n + 1) (2 n + 1) 6

= O(n 3)

[2.4.e]

-----------------------------------------------------------Exemplul 2.4.c. se refer tot la o bucl multiplicativ. -----------------------------------------------------------m:= 1 FOR i:= 1 TO n DO BEGIN m:= m *2; FOR j:= 1 TO m DO [2.4.f] Secventa {O(1)} END; {FOR} ----------------------------------------------------------- n situaia n care bucla exterioar se execut pentru valorile 1, 2, 3, ... ale lui i, bucla interioar itereaz de 2, 4, 8, ... ori conducnd la un timp de execuie de ordinul O(2 + 4 + 8 + ... + 2n). -----------------------------------------------------------2 ( 2n 1 ) = O( 2 n +1 ) 2 + 4 + 8 + ... + 2n = [2.4.g] 2 1 ----------------------------------------------------------- Aceast estimare este complet diferit de estimarea O(n2) pentru care exist tentaia de a fi avansat i care este valabil pentru buclele nemultiplicative.

2.5. Profilarea unui algoritm


Presupunnd c un algoritm a fost conceput, implementat, testat i depanat pe un sistem de calcul int Ne intereseaz de regul profilul performanei sale, adic timpii precii de execuie ai algoritmului pentru diferite seturi de date, eventual pe diferite sisteme int. Pentru aceasta sistemul de calcul int trebuie s fie dotat cu un ceas intern i cu funcii sistem de acces la acest ceas. Dup cum s-a mai precizat, determinarea profilului performanei face parte din testul ulterior al unui algoritm i are drept scop determinarea precis a ordinul de mrime al timpului de execuie al algoritmului.

Informaiile rezultate sunt utilizate de regul pentru a valida sau invalida, respectiv pentru a nuana rezultatele estimrii apriorice. Se presupune un algoritm implementat n forma unui program numit Algoritm(X: Intrare,Y: Iesire)unde X este intrarea iar Y ieirea. Pentru a construi profilul algoritmului este necesar s fie concepute: o Seturile de date de intrare a cror dimensiune crete ntre anumite limite, pentru a studia comportamentul algoritmului n raport cu dimensiunea intrrii o Seturile de date care n principiu se refer la cazurile extreme de comportament.

o O procedur cu ajutorul creia poate fi construit profilul algoritmului n baza seturilor de date anterior amintite. -----------------------------------------------------------procedure Profil; {procedura construiete profilul procedurii Algoritm(X: Intrare,Y: Ieire)} begin *initializeaz procedura Algoritm; *afiseaza("Testul lui Algoritm. Timpii n milisecunde"); repeat *citeste(SetDeDate); [2.5.a] *afiseaza("Un nou set de date:", SetDeDate); *apel TIME(t); {atribuie lui t valoarea curent a ceasului sistem} *apel Algoritm(SetDeDate, Rezultate); *apel TIME(t1); *afiseaza("TimpExecuie=", t - t1); until sfrit(SetDeDate) end {Profil} ----------------------------------------------------------- Procedura Profil poate fi utilizat n mai multe scopuri funcie de obiectivele urmrite. o (1) Evidenierea performanei intrinseci a unui algoritm precizat. Pentru aceasta se aleg, aa cum s-a mai precizat, seturi de date cu dimensiuni din ce n ce mai mari. Rezultatul va fi profilul algoritmului. Pentru deplina conturare a profilului se testeaz de asemenea i cazurile de comportament extrem ale algoritmului (cel mai favorabil i cel mai defavorabil).

o (2) Evidenierea performanei relative a doi sau mai muli algoritmi diferii care ndeplinesc aceeai sarcin.

n acest scop se execut procedura Profil pentru fiecare din algoritmi n parte, cu aceleai seturi de date iniale, pe un acelai sistem de calcul. Compararea profilelor rezultate permite ierarhizarea performanelor algoritmilor analizai.

o (3) Evidenierea performanei relative a dou sau mai multe sisteme de calcul. n acest scop se ruleaz procedura Profil pentru un acelai algoritm, cu aceleai date iniiale pe sistemele de calcul int supuse analizei. Compararea profilelor rezultate permite ierarhizarea performanelor sistemelor de calcul analizate. De regul, pe piaa sistemelor de calcul se utilizeaz n acest scop algoritmi consacrai, n anumite cazuri standardizai, cunoscui sub denumirea de "bench marks" n baza crora sunt evideniate performanele diferitelor arhitecturi de sisteme de calcul.

Trebuie ns acordat o atenie special procedurii TIME a cror rezultate n anumite circumstane pot fi nerevelevante. o Astfel, n cazul sistemelor time-sharing, al sistemelor complexe (care lucreaz cu ntreruperi) sau al sistemelor multi-procesor, timpul furnizat de aceast funcie poate fi complet needificator. o n astfel de cazuri, una din soluii este aceea de a executa programul de un numr suficient de ori i de a apela la metode statistice pentru evidenierea performanelor algoritmului testat.

3. Sortri
3.1. Conceptul de sortare
Scopul fundamental al acestui capitol este: De a furniza un set extins de exemple referitoare la utilizarea structurilor de date introduse n capitolul 1; De a sublinia influena profund pe care adoptarea unei anumite structuri o are asupra algoritmului care o utilizeaz i asupra tehnicilor de programare care implementeaz algoritmul respectiv.

Sortarea este domeniul ideal al studiului: Construciei algoritmilor, Al performanelor algoritmilor, Al avantajelor i dezavantajelor unor algoritmi fa de alii n accepiunea unei aplicaii concrete Al tehnicilor de programare aferente difertiior algoritmi

Prin sortare se nelege n general ordonarea unei mulimi de elemente, cu scopul de a facilita cutarea ulterioar a unui element dat. Sortarea este o activitate fundamental cu caracter universal. Spre exemplu n cartea de telefoane, n dicionare, n depozitele de mrfuri i n general n orice situaie n care trebuiesc cutate i regsite obiecte, sortarea este prezent.

n cadrul acestui capitol se presupune c sortarea se refer la anumite elemente care au o structur articol definit dup cum urmeaz [3.1.a]: -----------------------------------------------------------TYPE TipElement = RECORD cheie: integer; [3.1.a] {Alte cmpuri definite} END; ----------------------------------------------------------- Cmpul cheie precizat, poate fi neesenial din punctul de vedere al informaiei nregistrate n articol, partea esenial a informaiei fiind coninut n celelalte cmpuri. Din punctul de vedere al sortrii ns, cheie este cel mai important cmp ntruct este valabil urmtoarea definiie a sortrii. o Fiind dat un ir de elemente aparinnd tipul mai sus definit a1, a2,....,an

o Prin sortare se nelege permutarea elementelor irului ntr-o anumit ordine: ak1, ak2,.....,akn o Astfel nct irul cheilor s devin monoton cresctor, cu alte cuvinte s avem ak1.cheie ak2.cheie ... akn.cheie Tipul cmpului cheie se presupune a fi ntreg pentru o nelegere mai facil, n realitate el poate fi ns orice tip scalar. O metod de sortare se spune c este stabil dac dup sortare, ordinea relativ a elementelor cu chei egale coincide cu cea iniial, element esenial n special n cazul n care se execut sortarea dup mai multe chei. n cazul sortrii, dependena dintre algoritmul care realizeaz sortarea i structura de date prelucrat este profund. Din acest motiv metodele de sortare sunt clasificate n dou mari categorii dup cum elementele de sortat: o Sunt nregistrate ca i tablouri n memoria central a sistemului de calcul ceea ce conduce la sortarea tablourilor numit sortare intern o Sunt nregistrate ntr-o memorie extern: ceea ce conduce la sortarea fiierelor (secvenelor) numit i sortare extern.

3.2. Sortarea tablourilor


Cerina fundamental care se formuleaz fa de metodele de sortare a tablourilor se refer la utilizarea ct mai economic a zonei de memorie disponibile. Din acest motive pentru nceput, prezint interes numai algoritmii care, dat fiind tabloul a, realizeaz sortarea "in situ", adic chiar n zona de memorie alocat tabloului. Pornind de la aceast restricie, n continuare algoritmii vor fi clasificai n funcie de eficiena lor, respectiv n funcie de timpul de execuie pe care l necesit. Aprecierea cantitativ a eficienei unui algoritm de sortare se realizeaz prin intermediul unor indicatori specifici. Un astfel de indicator este numrul comparaiilor de chei notat cu C, pe care le execut algoritmul. Un alt indicator este numrul de atribuiri de elemente, respectiv numrul de micri de elemente executate de algoritm notat cu M. Ambii indicatori depind de numrul total n al elementelor care trebuiesc sortate.

n cazul unor algoritmi de sortare simpli bazai pe aa-zisele metode directe de sortare att C ct i M sunt proporionali cu n2 adic sunt O(n2). Exist ns i metode avansate de sortare, care au o complexitate mult mai mare i n cazul crora indicatorii C i M sunt de ordinul lui n log2 n ( O(n log2 n) ).

Raportul n2/(n log2 n), care ilustreaz ctigul de eficien realizat de aceti algoritmi, este aproximativ egal cu 10 pentru n = 64, respectiv 100 pentru n = 1000. Cu toate c ameliorarea este substanial, metodele de sortare directe prezint interes din urmtoarele motive: Sunt foarte potrivite pentru explicitarea principiilor majore ale sortrii. Procedurile care le implementeaz sunt scurte i relativ uor de neles. Dei metodele avansate necesit mai puine operaii, aceste operaii sunt mult mai complexe n detaliile lor, respectiv metodele directe se dovedesc a fi superioare celor avansate pentru valori mici ale lui n. Reprezint punctul de pornire pentru metodele de sortare avansate.

Metodele de sortare care realizeaz sortarea "in situ" se pot clasifica n trei mai categorii: Sortarea prin inserie; Sortarea prin selecie; Sprin interschimbare.

n prezentarea acestor metode se va lucra cu tipul element definit anterior, precum i cu urmtoarele notaii [3.2.a]. -----------------------------------------------------------TYPE TipIndice = 0..n; TipTablou = ARRAY [TipIndice] OF TipElement; VAR a: TipTablou; temp: TipElement; [3.2.a] -----------------------------------------------------------3.2.1. Sortarea prin inserie Aceast metod este larg utilizat de juctorii de cri. Elementele (crile) sunt n mod conceptual divizate ntr-o secven destinaie a1...ai-1 i ntr-o secven surs ai....an. n fiecare pas ncepnd cu i = 2 , elementul i al tabloului (care este de fapt primul element al secvenei surs), este luat i transferat n secvena destinaie prin inserarea sa la locul potrivit. Se incrementeaz i i se reia ciclul.

Astfel la nceput se sorteaz primele dou elemente, apoi primele trei elemente i aa mai departe. Se face precizarea c n pasul i, primele i-l elemente sunt deja sortate, astfel nct sortarea const numai n a insera elementul a[i] la locul potrivit ntr-o secven deja sortat.

n termeni formali, acest algoritm este precizat n secvena [3.2.1.a]. ----------------------------------------------------------{Tehnica sortrii prin inserie}

FOR i := 2 TO n DO BEGIN [3.2.1.a] temp:=a[i]; *insereaz x la locul potrivit n a[1]...a[i]} END;{FOR} ----------------------------------------------------------- Selectarea locului n care trebuie inserat a[i] se face parcurgnd secvena destinaie deja sortat a[1],...,a[i-1] de la dreapta la stnga, Oprirea se realizeaz pe primul element a[j] care are cheia mai mic sau egal cu a[i], Dac un astfel de element a[j] nu exist, oprirea se realizeaz pe a[1] adic pe prima poziie.

Simultan cu parcurgerea, se realizeaz i deplasarea spre dreapta cu o poziie a fiecrui element testat pn n momentul ndeplinirii condiiei de oprire. Acest caz tipic de repetiie cu dou condiii de terminare readuce n atenie metoda fanionului (&1.4.2.1). Pentru aplicarea ei se introduce elementul auxiliar a[0] care se asigneaz iniial cu a[i]. n felul acesta, cel mai trziu pentru j = 0, condiia de a avea cheia lui a[j] "mai mic sau egal" cu cheia lui a[i] se gsete ndeplinit. Inseria propriu-zis se realizeaz pe poziia j+1.

Algoritmul care implementeaz sortarea prin inserie apare n [3.2.1.b] iar profilul su temporal n figura 3.2.1.a.

-----------------------------------------------------------{Sortare prin insertie - Varianta Pascal} PROCEDURE SortarePrinInsertie; VAR i,j: TipIndice; temp: TipElement; BEGIN FOR i:= 2 TO n DO BEGIN temp:= a[i]; a[0]:= temp; j:= i-1; WHILE a[j].cheie>temp.cheie DO BEGIN a[j+1]:= a[j]; j:= j-1 [3.2.1.b] END; {WHILE} a[j+1]:= temp END END; {SortarePrinInsertie} ------------------------------------------------------------

SortarePrinInserie FOR (n -1 iteraii) 2 atribuiri WHILE (n -1 iteraii) 1 comparaie 1 atribuire 1 atribuire O(n-1) = O(n) O(1) O(1) O((n-1) n) = O(n2) O(n2)

Fig.3.2.1.a. Profilul temporal al algoritmului de sortare prin inserie Dup cum se observ, algoritmul de sortare conine un ciclu exterior dup i care se reia de n-1 ori (bucla FOR). n cadrul fiecrui ciclu exterior se execut un ciclu interior de lungime variabil dup j, pn la ndeplinirea condiiei (bucla WHILE). n pasul i al ciclului exterior FOR , numrul minim de reluri ale ciclului interior este 0 iar numrul maxim de reluri este i-1 .

Analiza sortrii prin inserie n cadrul celui de-al i-lea ciclu FOR, numrul Ci al comparaiilor de chei executate n bucla WHILE, depinde de ordinea iniial a cheilor, fiind: Cel puin 1 (secvena ordonat), Cel mult i-l (secvena ordonat invers) n medie i/2, presupunnd c toate permutrile celor n chei sunt egal posibile

ntruct avem n-1 reluri ale lui FOR pentru i:= 2..n , parametrul C are valorile precizate n [3.2.1.c]. -----------------------------------------------------------C min = 1 = n 1
C max = (i 1) = i =
i =2 i =1

i =2 n

n 1

(n 1) n 2

[3.2.1.c]

C + C max n 2 + n 2 = C med = min 2 4 ----------------------------------------------------------- Numrul maxim de atribuiri de elemente n cadrul unui ciclu FOR este C i + 3 i corespunde numrului maxim de comparaii

Explicaia: la numrul C i de atribuiri executate n cadrul ciclului interior WHILE de tip a[j+1]:= a[j]) se mai adaug 3 atribuiri (temp:= a[i], a[0]:= temp i a[i+1]:= temp).

Chiar pentru numrul minim de comparaii de chei (C i egal cu 1) cele trei atribuiri rmn valabile.

n consecin, parametrul M ia urmtoarele valori [3.2.1.d]. -----------------------------------------------------------M min = 3 (n 1)


M max = (Ci + 3) = (i + 2) =
i =2 i =2 n n n+ 2 i =1

i (1 + 2 + 3) =
[3.2.1.d]

+ 5n 6 (n + 2) (n + 3) 6= 2 2 2 M + M max n + 11 n 12 M med = min = 2 4 = n2

----------------------------------------------------------- Se observ c att C ct i M sunt de ordinul lui n2 (O(n2)). Valorile minime ale indicatorilor rezult cnd a este ordonat, iar valorile maxime, cnd a este ordonat invers. Sortarea prin inserie este o sortare stabil.

n secvena [3.2.1.e] se prezint o variant C a acestei metode de sortare cu fanionul poziionat pe poziia n. -----------------------------------------------------------// Sortarea prin inserie - varianta C insertie(int a[],int n) { //fanionul pe poziia a[n] for(int i=n-2;i>=0;i--) { a[n]=a[i]; int j=i+1; while(a[j]<a[n]) { a[j-1]=a[j]; j++; [3.2.1.e] } a[j-1]=a[n]; } } ----------------------------------------------------------- Algoritmul de sortare prin inserie poate fi mbuntit pornind de la observaia c secvena destinaie este deja sortat. n acest caz cutarea locului de inserie se poate face mult mai rapid utiliznd tehnica cutrii binare, prin mpriri succesive n pri egale a intervalului de cutare.

Algoritmul modificat se numete inserie binar [3.2.1.f]. -----------------------------------------------------------{Sortare prin inserie binar - Varianta Pascal} PROCEDURE SortarePrinInserieBinar; VAR i,j,stanga,dreapta,m: TipIndice; temp: TipElement; a: TipTablou; BEGIN FOR i:= 2 TO n DO BEGIN temp:= a[i]; stanga:= 1; dreapta:= i-1; WHILE stanga<=dreapta DO BEGIN [3.2.1.f]

m:= (stanga+dreapta)DIV 2; IF a[m].cheie>temp.cheie THEN dreapta:= m-1 ELSE stanga:= m+1 END;{WHILE} FOR j:= i-1 DOWNTO stanga DO a[j+1]:= a[j]; a[stanga]:= temp END {FOR} END; {SortarePrinInserieBinar} ----------------------------------------------------------- Analiza inseriei binare Poziia de inserat este gsit dac a[j].cheie x.cheie a[j+1].cheie , adic intervalul de cutare ajunge de dimensiune 1. Dac intervalul iniial este de lungime i sunt necesari log2(i) pai pentru determinarea locului inseriei.

ntruct procesul de sortare presupune parcurgerea prin metoda njumtirii binare a unor secvene de lungime i (care conin i chei), pentru i=1,2,...,n , numrul total de comparaii C efectuate n bucla WHILE este cel evideniat de expresia prezentat n [3.2.1.g]. -----------------------------------------------------------C = log 2i
i =1 n

[3.2.1.g]

-----------------------------------------------------------Aceast sum se poate aproxima prin integrala: -----------------------------------------------------------n = n (log n c) + c C = log 2 x dx = x (log 2 x c) 1 2 1 n

[3.2.1.h]

c = log 2 e = 1 / ln 2 = 1.44269

----------------------------------------------------------- Numrul comparaiilor de chei este n principiu independent de ordinea iniial a cheilor. De fapt numrul minim de comparaii, este necesar pentru un ir ordonat invers, iar numrul maxim pentru un ir ordonat. Acesta este un caz de comportament anormal al unui algoritm de sortare.

Din nefericire beneficiile cutrii binare se rsfrng numai asupra numrului de comparaii i nu asupra numrului de micri. De regul, mutarea cheilor i a informaiilor asociate necesit mai mult timp dect compararea a dou chei, Astfel ntruct M rmne de ordinul lui n2, performanele acestei metode de sortare nu cresc n msura n care ar fi de ateptat. De fapt, resortarea unui tablou gata sortat, utiliznd inseria binar, consum mai mult timp dect inseria cu cutare secvenial.

n ultim instan, sortarea prin inserie nu este o metod potrivit de sortare cu ajutorul calculatorului, deoarece inseria unui element presupune deplasarea cu o poziie n tablou a unui ir de elemente, deplasare care este neeconomic. Acest dezavantaj conduce la ideea dezvoltrii unor algoritmi n care micrile s afecteze un numr mai redus de elemente i s se realizeze pe distane mai mari. Sortarea prin selecie rezolv aceste probleme.

3.2.2. Sortarea prin selecie Sortarea prin selecie folosete procedeul de a selecta elementul cu cheia minim i de a schimba ntre ele poziia acestui element cu cea a primului element. Se repet acest procedeu cu cele n -1 elemente rmase, apoi cu cele n -2, etc., terminnd cu ultimele dou elemente. Aceast metod este oarecum opus sortrii prin inserie care presupune la fiecare pas un singur element al secvenei surs i toate elementele secvenei destinaie n care se caut de fapt locul de inserie.

Selecia n schimb presupune toate elementele secvenei surs dintre care selecteaz pe cel cu cheia cea mai mic i l depoziteaz ca element urmtor al secvenei destinaie. -----------------------------------------------------------{Tehnica sortarii prin selectie} FOR i:= 1 TO n-1 DO [3.2.2.a] BEGIN *gseste cel mai mic element al lui a[i]...a[n] i asigneaz pe min cu indicele lui; *interschimb pe a[i] cu a[min] END; ----------------------------------------------------------- n urma procesului de detaliere rezult algoritmul prezentat n [3.2.2.b] al crui profil temporal apare n figura 3.2.2.a. -----------------------------------------------------------{Sortare prin selacie - Varianta Pascal} PROCEDURE SortarePrinSelectie; VAR i,j,min: TipIndice; temp: TipElement; a: TipTablou; BEGIN FOR i:= 1 TO n-1 DO BEGIN min:= i; temp:= a[i]; FOR j:= i+1 TO n DO IF a[j].cheie<temp.cheie THEN [3.2.2.b] BEGIN min:= j; temp:= a[j] END;{FOR} a[min]:= a[i]; a[i]:= temp END {FOR} END; {SortarePrinSelectie} ------------------------------------------------------------

SortarePrinSelectie FOR (n -1 iteraii) 1 atribuire FOR (i -1 iteraii) 1 comparaie 1 atribuire Hm -1 atribuiri

2 atribuiri

Fig.3.2.2.a. Profilul temporal al sortrii prin selecie Analiza sortrii prin selecie

Numrul comparaiilor de chei C este independent de ordinea iniial a cheilor. -----------------------------------------------------------C = (i 1) =


i =1 n 1 n2 i =1

i=

n2 3 n + 2 2

[3.2.2.c]

----------------------------------------------------------- Numrul atribuirilor este de cel puin 3 pentru fiecare valoare a lui i, (temp:= a[i],a[min]:= a[i],a[i]:= temp), de unde rezult: -----------------------------------------------------------[3.2.2.d] M min = 3 (n 1) ----------------------------------------------------------- Acest minim poate fi atins efectiv, dac iniial cheile sunt deja sortate. Pe de alt parte dac cheile sunt iniial n ordine invers Mmax se determin cu ajutorul formulei empirice [3.2.2.e] [Wi76].
n2 M max = + 3 (n 1) 4
(1)

[3.2.2.e]

Valoarea medie a lui M nu este media aritmetic a lui Mmin i Mmax , ea obinndu-se printr-un raionament probabilistic n felul urmtor. Se consider o secven de m chei. Fie o permutare oarecare a celor m chei notat cu k1 , k2 , ..., km. Se determin numrul termenilor kj avnd proprietatea de a fi mai mici dect toi termenii precedeni k1 , k2 , ...,.kj-1.

Se adun toate valorile obinute pentru cele m! permutri posibile i se mparte suma la m! Se demonstreaz c rezultatul acestui calcul este Hm-1, unde Hm este suma parial a seriei armonice [3.2.2.f][Wi76]:

-----------------------------------------------------------1 1 1 Hm = 1 + + + + [3.2.2.f] 2 3 m ----------------------------------------------------------- Aceast valoare reprezint media numrului de atribuiri ale lui temp, executate pentru o secven de m elemente, deoarece temp se atribuie ori de cte ori se gsete un element mai mic dect toate elementele care-l preced. innd cont i de atribuirile temp:=a[i],a[min]:=a[i] i a[i]:=temp, valoarea medie a numrului total de atribuiri pentru m termeni este Hm+2.

Se demonstreaz c dei seria este divergent, o sum parial a sa se poate calcula cu ajutorul formulei [3.2.2.g]: -----------------------------------------------------------1 1 1 H m ln m + + + [3.2.2.g] 2 m 12 m 2 120 m 4 -----------------------------------------------------------unde = 0.5772156649... este constanta lui Euler [Kn76]. Pentru un m suficient de mare valoarea lui Hm se poate aproxima prin expresia: -----------------------------------------------------------[3.2.2.h] H m ln m + ----------------------------------------------------------- ntruct n procesul de sortare se parcurg succesiv secvene care au respectiv lungimea m = n , n-1 , n-2 ,..., 1, fiecare dintre ele necesitnd n medie Hm+2 atribuiri, numrul mediu total de atribuiri Mmed va avea expresia: -----------------------------------------------------------M med
m=1

(H m + 2)

m=1

(ln m + + 2) = n ( + 2) +

m=1

ln m

[3.2.2.i]

----------------------------------------------------------- Suma de termeni discrei, poate fi aproximat cu ajutorul calculului integral [3.2.2.j] -----------------------------------------------------------n 1

ln x dx = x (ln x 1) 1 = n ln (n) n + 1
n

[3.2.2.j]

-----------------------------------------------------------aproximare care conduce la rezultatul:


M med n (ln m + + 1) + 1 = O (n ln n)

n concluzie, algoritmul bazat pe selecie este de preferat celui bazat pe inserie, cu toate c n cazurile n care cheile sunt ordonate, sau aproape ordonate, sortarea prin inserie este mai rapid. Optimizarea performanei sortrii prin selecie poate fi realizat prin reducerea numrului de micri de elemente ale tabloului. n [Se88] se propune o astfel de variant n care n loc de a memora de fiecare dat elementul minim curent n variabila temp, se reine doar indicele acestuia, mutarea urmnd a se realiza doar pentru ultimul element gsit, la prsirea ciclului FOR interior [3.2.2.k].

-----------------------------------------------------------{Sortare prin selecie optimizat - Varianta Pascal} PROCEDURE SelectieOptimizat; VAR i,j,min: TipIndice; temp: TipElement; a: TipTablou; BEGIN FOR i:= 1 TO n-1 DO [3.2.2.k] BEGIN min:= i; FOR j:= i+1 TO n DO IF a[j].cheie<temp.cheie THEN min:= j; temp:= a[min]; a[min]:= a[i]; a[i]:= temp END {FOR} END; {SelectieOptimizat} ----------------------------------------------------------- Msurtorile experimentale efectuate ns asupra acestei variante nu evideniaz vreo ameliorare nici chiar pentru valori mari ale dimensiunii tabloului de sortat. Explicaia rezid n faptul ca atribuirile care necesit n plus accesul la componenta unui tablou se realizeaz practic n acelai timp ca i o atribuire normal.

3.2.3. Sortarea prin interschimbare Clasificarea metodelor de sortare n diferite familii ca inserie, interschimbare sau selecie nu este ntotdeauna foarte bine definit. Paragrafele anterioare au analizat algoritmi care dei implementeaz inseria sau selecia, se bazeaz pe fapt pe interschimbare. n acest paragraf se prezint o metod de sortare n care interschimbarea a dou elemente este caracteristica dominant. Principiul de baz al acestei metode este urmtorul: se compar i se interschimb perechile de elemente alturate, pn cnd toate elementele sunt sortate. Ca i la celelalte metode, se realizeaz treceri repetate prin tablou, de la capt spre nceput, de fiecare dat deplasnd cel mai mic element al mulimii rmase spre captul din stnga al tabloului. Dac se consider tabloul n poziie vertical i se asimileaz elementele sale cu nite bule de aer n interiorul unui lichid, fiecare bul avnd o greutate proporional cu valoarea cheii, atunci fiecare trecere prin tablou se soldeaz cu ascensiunea unei bule la nivelul specific de greutate. Din acest motiv aceast metod de sortare este cunoscut n literatur sub denumirea de bubblesort (sortare prin metoda bulelor).

Algoritmul aferent acestei metode apare n continuare [3.2.3.a]: -----------------------------------------------------------{Sortarea prin interschimbare Bubblesort - Varianta 1} PROCEDURE Bubblesort;

VAR i,j: TipIndice; temp: TipElement; BEGIN FOR i:= 2 TO n DO BEGIN [3.2.3.a] FOR j:= n DOWNTO i DO IF a[j-1].cheie>a[j].cheie THEN BEGIN temp:= a[j-1]; a[j-1]:= a[j]; a[j]:= temp END {IF} END {FOR} END; {Bubblesort} ----------------------------------------------------------- Profilul temporal al algoritmului de sortare prin interschimbare este prezentat n figura 3.2.a i el conduce la o estimare a ncadrrii performanei algoritmului n ordinul O(n2).
Bubblesort FOR (n - 1 iteraii) FOR (i - 1 iteraii) 1 comparaie 3 atribuiri O(n) O(nn) = O(n )
2

O(n2)

Fig.3.2.a. Profilul temporal al sortrii prin interschimbare n multe cazuri ordonarea se termin nainte de a se parcurge toate iteraiile buclei FOR exterioare. n acest caz, restul iteraiilor sunt fr efect, deoarece elementele sunt deja ordonate.

O modalitate evident de mbuntire a algoritmului bazat pe aceast observaie este aceea prin care se memoreaz dac a avut sau nu loc vreo interschimbare n cursul unei treceri.

i n acest caz este ns necesar o ultim trecere, fr nici o interschimbare care marcheaz finalizarea algoritmului. ----------------------------------------------------------{Sortarea prin interschimbare - Varianta 2} PROCEDURE Bubblesort1; VAR i: TipIndice; modificat: boolean; temp: TipElement; BEGIN REPEAT modificat:= false; FOR i:= 1 TO n-1 DO IF a[i].cheie>a[i+1].cheie THEN [3.2.3.b] BEGIN temp:= a[i]; a[i]:= a[i+1]; a[i+1]:= temp; modificat:= true END UNTIL NOT modificat

END; {Bubblesort1} ----------------------------------------------------------- Aceast mbuntire, poate fi la rndul ei perfecionat, memornd nu faptul c a avut loc sau nu o interschimbare ci indicele k al ultimei schimbri. Este evident faptul c toate perechile de elemente situate sub acest indice (care au indici mai mici) sunt ordonate, n consecin trecerile urmtoare pot fi terminate la acest indice n loc s fie terminate la indicele predeterminat ca limit i. La o analiz atent se poate observa o asimetrie particular: Un element uor plasat la captul greu al tabloului este readus la locul su ntr-o singur trecere, n schimb un element greu plasat la captul uor al tabloului va fi readus spre locul su doar cu cte o poziie la fiecare trecere.

Spre exemplu tabloul: 12 18 22 34 65 67 83 04 va fi sortat cu ajutorul metodei bubblesort mbuntite ntr-o singur trecere, n schimb ce tabloul 83 04 12 18 22 34 va necesita apte treceri n vederea sortrii. 65 67

Aceast neobinuit asimetrie, sugereaz o a treia mbuntire: alternarea sensurilor de parcurgere ale trecerilor consecutive.

Algoritmul care conine aceste mbuntiri se numete shakersort (sortare prin amestecare) i este prezentat n [3.2.3.c]. -----------------------------------------------------------{Sortarea prin interschimbare - Varianta 3} PROCEDURE Shakersort; VAR j,ultim,stanga,dreapta: TipIndice; temp: TipElement; BEGIN stanga:= 2; dreapta:= n; ultim:= n; REPEAT FOR j:= dreapta DOWNTO stanga DO [3.2.3.c] IF a[j-1].cheie>a[j].cheie THEN BEGIN temp:= a[j-1]; a[j-1]:= a[j]; a[j]:= temp; ultim:= j END;{FOR} stanga:= ultim+1; FOR j:=stanga TO dreapta DO IF a[j-1].cheie>a[j].cheie THEN BEGIN temp:=a[j-1]; a[j-1]:=a[j]; a[j]:=temp; ultim:=j END;{FOR} dreapta:=ultim-1 UNTIL (stanga>dreapta) END; {Shakersort} ------------------------------------------------------------

Analiza sortrilor bubblesort i shakersort.

Numrul comparaiilor la algoritmul bubblesort este constant i are valoarea: -----------------------------------------------------------C = (i 1) =


i =1 n 1

n2 3 n + 2 2

[3.2.3.d]

----------------------------------------------------------- Valorile minim, maxim i medie ale numrului de micri sunt: -----------------------------------------------------------3 M max = 3 C = (n 2 + 3 n + 2) [3.2.3.e] 2 3 M max = (n 2 + 3 n + 2) 4 ------------------------------------------------------- Analiza metodei mbuntite (shakersort) arat c C min = n - 1 .
M min = 0

Pentru ceilali indicatori, Knuth ajunge la un numr mediu de treceri proporional cu n - k1 n i la un numr mediu de comparaii de chei proporional cu 1/2 (n2 - n ( k2 + ln n)). Trebuie ns remarcat faptul c toate mbuntirile propuse nu afecteaz n nici un fel numrul de interschimbri; ele reduc numai numrul de verificri redundante. Din pcate ns interschimbarea a dou chei este mult mai costisitoare ca timp dect compararea lor, prin urmare toate aceste mbuntirile atent studiate au un efect mult mai redus dect s-ar atepta n mod intuitiv. Analiza comparativ a performanelor algoritmilor de sortare prezentai, scoate n eviden faptul c sortarea prin interschimbare este mai puin performant dect sortrile prin inserie sau selecie, astfel nct utilizarea ei nu este recomandabil. Algoritmul shakersort poate fi utilizat n mod avantajos n cazurile n care elementele sunt aproape sortate, caz ns destul de rar ntlnit n practic. Se poate demonstra c distana medie pe care fiecare element al unui tablou de dimensiune n, o parcurge n procesul sortrii este de n /3 locuri. Astfel, deoarece n metodele prezentate pn acum, fiecare element i modific doar cu un singur loc poziia la fiecare pas elementar, este necesar un numr de treceri proporional cu n2. O mbuntire efectiv a performanei trebuie s aib n vedere deplasarea elementelor pe distane mai mari ntr-un singur pas.

3.2.4. Sortarea prin inserie cu diminuarea incrementului D.L. Shell a propus n 1959, o perfecionare a metodei de sortare prin inserie direct. o La nceput, toate articolele care sunt desprite prin cte 4 poziii, sunt grupate i sortate separat prin metoda inseriei.

Acest proces se numete sortare-4. n exemplul din fig.324, unde se sorteaz 8 elemente, s-au format 4 grupe de elemente separate prin cte 4 poziii.

o Dup aceast prim trecere, se formeaz grupuri n care elementele sunt separate prin cte dou poziii i din nou sunt sortate prin inserie. Acest proces se numete sortare-2.

o n final, la cea de-a treia trecere, elementele sunt sortate obinuit (sortare1). Se precizeaz faptul c fiecare k-sortare este de fapt o sortare prin inserie la care pasul este k (nu 1 ca la inseria normal).

34 sortare-4 34 sortare-2 04 sortare-1 04 18 12

65

12

22

83

18

04

67

18 12 18

04 22 22

22 34 34

83 65 65

65 83 67

12 67 83

67

Fig. 3.2.4. Sortare prin inserie cu diminuarea incrementului Dei la prima vedere aceast metod care presupune cteva treceri asupra tuturor elementelor, nu pare foarte performant, totui la o analiz mai atent, fiecare trecere realizeaz relativ puine modificri ale poziiilor elementelor. Este evident c metoda conduce la sortarea elementelor i c fiecare pas profit de cei anteriori (fiecare sortare-i combin grupuri deja sortate-j). Este posibil orice secven de incremeni atta timp ct ultimul este egal cu unitatea: n cel mai ru caz n acest pas se va realiza n ntregime procesul de sortare. Este mai puin evident, dar practica demonstreaz c o secven de incremeni descresctori care nu sunt puteri ale lui 2 asigur o eficien superioar procesului de sortare.

Programul prezentat n [3.2.4.b], este dezvoltat pentru o secven oarecare de incremeni descresctori format din t elemente care ndeplinesc condiiile [3.2.4.a.]. -----------------------------------------------------------h1 , h2 , ... , ht , unde ht = 1, hi > hi+1 i 1 i < t [3.2.4.a] ----------------------------------------------------------- Incremenii sunt pstrai n tabloul h. Fiecare sortare-h este programat ca o sortare prin inserie utiliznd tehnica fanionului n vederea simplificrii condiiei de terminare a procesului de cutare.

Deoarece fiecare sortare necesit propriul su fanion, pentru a face procesul de cutare ct mai simplu, tabloul a se extinde spre stnga nu cu o singur component a[0] ci cu h[1] componente, adic un numr egal cu valoarea celui mai mare increment. -----------------------------------------------------------{Sortarea cu diminuarea incrementului - Shellsort} PROCEDURE Shellsort; CONST t=4; VAR i,j,pas,s: TipIndice; temp: TipElement; m: 1..t; h: ARRAY[1..t] OF integer; BEGIN {atribuire incrementi} h[1]:= 9; h[2]:= 5; h[3]:= 3; h[4]:= 1; FOR m:= 1 TO t DO BEGIN {s este indicele fanionului curent} pas:= h[m]; s:= -pas; FOR i:= pas+1 TO n DO BEGIN temp:= a[i]; j:= i-pas; IF s=0 THEN s:= -pas; s:= s+1; a[s]:= temp; [3.2.4.b] WHILE temp.cheie<a[j].cheie DO BEGIN a[j+k]:= a[j]; j:= j-pas {deplasare} END;{WHILE} a[j+k]:= temp {inserie element} END{FOR} END{FOR} END; {Shellsort} ----------------------------------------------------------- Analiza metodei shellsort. Analiza acestui algoritm pune probleme deosebite din punct de vedere matematic, multe din ele nc nerezolvate. n particular, nu se cunoate nici mcar secvena de incremeni cea mai potrivit. Ceea ce este deosebit de interesant este faptul c incremenii nu trebuie s fie unii multiplii altora. Pentru o eficien sporit a sortrii este de dorit ca ntre diferitele lanuri de parcurgere s aib loc ct mai multe interaciuni. De asemenea este valabil urmtoarea teorem pe care de fapt se bazeaz metoda. o Dac o secven sortat-k este sortat-i ea rmne i sortat-k. o Cu alte cuvinte, procesul de sortare cu diminuarea incrementului este cumulativ. Knuth indic drept cele mai potrivite secvene de incremeni cele prezentate n [3.2.4.c] respectiv [3.2.4.d] (furnizate n ordine cresctoare): -----------------------------------------------------------1, 4, 13, 40, 121, ... h t , h t-1 , ..., h k , h k-1 , ..., h1 [3.2.4.c] unde h k-1 = 3 h k +1 , h t = 1 i t = log 3 n - 1

-----------------------------------------------------------l, 3, 7, 15, 3l, ... [3.2.4.d] unde h k-1 = 2 h k +1 , h t = 1 i t = log 2 n - 1 ----------------------------------------------------------- Pentru ultima secven, analiza matematic a sortrii a n elemente cu metoda Shellsort demonstreaz necesitatea unui efort proporional cu n1.2. n [3.2.4.e] se prezint o alt variant de implementare acestei metode.

Algoritmul utilizeaz incremeni bazai pe formula [3.2.4.c], unde t se calculeaz funcie de dimensiunea tabloului n prima bucl REPEAT [Se88]. -----------------------------------------------------------{Metoda Shellsort (Varianta Sedgewick)} PROCEDURE Shellsort1; VAR i,j,h: TipIndice; temp: TipElement; BEGIN h:= 1; REPEAT h:= 3*h+1 UNTIL h>n; REPEAT h:= h DIV 3; FOR i:= h+1 TO n DO [3.2.4.e] BEGIN temp:= a[i]; j:= i; WHILE (a[j-h].cheie>temp.cheie) AND (j>h) DO BEGIN a[j]:= a[j-h]; j:= j-h END; {WHILE} a[j]:= temp END;{FOR} UNTIL h=1 END;{Shellsort1} -----------------------------------------------------------3.2.5. Sortarea prin metoda ansamblelor Metoda sortrii prin selecie se bazeaz pe selecia repetat a celei mai mici chei dintre n elemente, apoi dintre cele n -1 rmase, etc. Este evident c determinarea celei mai mici chei dintre n elemente necesit n -1 comparaii, dintre n -1 elemente necesit n -2 comparaii etc. Activitatea de selecie poate fi mbuntit, dac la fiecare trecere se vor reine mai multe informaii i nu doar elementul cu cheia cea mai mic. o Astfel spre exemplu din n/2 comparaii se poate determina cea mai mic cheie a fiecrei perechi de elemente, o Din alte n/4 comparaii, cea mai mic cheie a fiecrei perechi de chei mici deja determinate i aa mai departe. o n final, utiliznd doar n/2 + n/4 + ... + 4 + 2 + 1 = n -1 comparaii, se poate construi un arbore de selecii avnd drept rdcin cheia cea mai mic (fig.3.2.5.a).

o Arborele de selecii este de fapt un arbore binar parial ordonat.


04

12 34 12 18

04 04

34

65

12

22

83

18

04

67

Fig.3.2.5.a. Arbore de selecii Cum se poate utiliza acest arbore la sortare? o Se extrage cheia cea mai mic din rdcina arborelui. o n continuare se parcurge n sens invers drumul urmat de cheia cea mai mic i se elimin succesiv aceast cheie nlocuind-o fie cu un loc liber, la baza structurii arbore, fie cu elementul ramurii alternative n cazul unui nod intermediar

12 34 12 18

34

65

12

22

83

18

67

Fig. 3.2.5.b. Selecia celei mai mici chei Din nou, elementul care va rzbate spre rdcina arborelui va fi cel cu cheia cea mai mic (al doilea dup cel anterior), element care poate fi extras.

12 12 34 12 18 18 67

34

65

12

22

83

18

67

Fig. 3.2.5.c. Completarea locurilor libere Dup n astfel de pai de selecie, s-au extras succesiv cele n elemente ale mulimii n ordine cresctoare, arborele devine vid i procesul de sortare este ncheiat. Trebuie notat faptul c fiecare din cei n pai de selecie necesit numai log2 n comparaii. n consecin, procesul de sortare integral necesit: o n pai pentru construcia arborelui, o Un numr de operaii elementare de ordinul lui n log2 n pentru sortarea propriu-zis. Aceasta este o mbuntire considerabil fa de metodele directe care necesit un efort de ordinul O(n2) i chiar fa de Shellsort care necesit O(n1.2). Este evident faptul c n cazul metodei de sortare bazat pe structura arbore, complexitatea pailor de sortare individuali crete. De asemenea, n vederea reinerii unei cantiti sporite de informaie, trebuie conceput o structur de date aparte care s permit organizarea eficient a informaiei. o Astfel, n primul rnd trebuiesc eliminate locurile goale, care pe de o parte sporesc dimensiunea arborelui, iar pe de alt parte sunt sursa unor comparaii care nu sunt necesare. o n al doilea rnd, arborele ar trebui reprezentat utiliznd locaii de memorie pentru n elemente i nu pentru 2n -1 elemente aa cum rezult din figurile 3.2.5.a, b, c. Aceste probleme au fost rezolvate de ctre J. Williams, creatorul metodei de sortare heapsort (sortare de ansamble). Metoda n sine, reprezint o realizare de excepie printre metodele convenionale de sortare i utilizeaz o reprezentare special a unui arbore binar parial ordonat, numit "ansamblu".

Un ansamblu ("heap") este definit ca o secven de chei hs , hs+1 , ..., hd care se bucur de proprietile [3.2.5.a]: -----------------------------------------------------------h i h 2i pentru toi i = s, ..., d/2 [3.2.5.a] h i h 2i +1 ----------------------------------------------------------- Un ansamblu poate fi asimilat cu un arbore binar parial ordonat i reprezentat printr-un tablou. Spre exemplu, ansamblul h1 , h2 , ...., h15 poate fi asimilat cu arborele binar din figura 3.2.5.d i poate fi reprezentat prin tabloul h n baza urmtoarei tehnici: o Se numeroteaz elementele ansamblului, nivel cu nivel, de sus n jos, de la stnga la dreapta; o Se asociaz elementelor ansamblului, locaiile unui tablou de elemente h, astfel nct elementului hi al ansamblului i corespunde locaia h[i].

h1 h2 h4 h5 h6 h3 h7

h8
1 2

h9
3 4

h10
5 6 7

h11
8

h12
9 10 11

h13

h14
14

h15
15

12 13

Fig. 3.2.5.d. Reprezentarea unui ansamblu printr-un tablou liniar h Un ansamblu se bucur de proprietatea c primul su element este cel mai mic dintre toate elementele ansamblului adic h1 = min (h1 , ...., hn) .

Se presupune un ansamblu hs+1 , hs+2 , ...., hd definit prin indicii s +1 i d Acestui ansamblu i se adaug la stnga, pe poziia hs un nou element x ,
obinndu-se un ansamblu extins spre stnga hs , ..., hd .
34 h1 22 04 22 04

12

65
1 2

83
3 4

18
5 6

12
7 1

65
2 3

83
4

18
5 6

34
7

22 04

65

83 (a)

18

12

04

22 12

65

83 (b)

18 34

Fig. 3.2.5.e. Deplasarea unei chei ntr-un ansamblu

n figura 3.2.5.e.(a) apare ca exemplu ansamblul h2 , ...., h7 , iar n aceeai figur


(b), ansamblul extins spre stnga cu un element x = 34 . o Noul ansamblu se obine din cel anterior plasnd pe x n vrful ansamblului i deplasndu-l "n jos" de-a lungul drumului indicat de componentele cele mai mici, care n acelai timp urc. o Astfel valoarea 34 este mai nti schimbat cu valoarea 04, apoi cu valoarea 12, genernd structura din figura amintit. o Se poate verifica cu uurin c aceast deplasare conserv condiiile care definesc un ansamblu [3.2.5.a]. Notnd cu i i j indicii elementelor care se interschimb, i presupunnd c x a fost introdus pe poziia hs , tehnica de implementare a unei astfel de deplasri apare n [3.2.5.b] n variant pseudocod.

------------------------------------------------------------

*Deplasarea unei chei varianta pseudocod

de

sus

jos

ntr-un

ansamblu

procedure Deplasare(s,d: TipIndice){limitele ansamblului} i:= s; {indic elementul curent) j:= 2*i; {indic fiul stng al elementului curent} temp:= h[i] {elementul care se deplaseaz} ct timp exist niveluri n ansamblu (ji) i locul de plasare nu a fost gsit execut *selecteaz pe cel mai mic dintre fii elementului indicat de i (pe h[j] sau pe h[j+1] dac temp>fiul_selectat atunci *deplaseaz fiul selectat n locul tatlui su (h[i]:=h[j]); *avanseaz pe nivelul urmtor al ansamblului (i:=j; j:=2*i) altfel [3.2.5.b] retur {locul a fost gsit} *plaseaz pe temp la locul su n ansamblu (h[i]:=temp); -----------------------------------------------------------Procedura Pascal care implementeaz algoritmul de deplasare apare n [3.2.5.c]. -----------------------------------------------------------{Deplasarea unei chei de sus n jos ntr-un ansamblu Varianta Pascal} PROCEDURE Deplasare(s,d: TipIndice); VAR i,j: TipIndice; temp: TipElement; ret: boolean; BEGIN i:= s; j:= 2*i; temp:= h[i]; ret:= false; WHILE(j<=d) AND (NOT ret) DO BEGIN IF j<d THEN IF h[j].cheie>h[j+1].cheie THEN j:= j+1; IF temp.cheie>h[j].cheie THEN BEGIN h[i]:= h[j]; i:= j; j:= 2*i [3.2.5.c] END ELSE ret:= true END;{WHILE} h[i]:= temp END; {Deplasare} ----------------------------------------------------------- R.W. Floyd a sugerat o metod de a construi un ansamblu in situ, utiliznd procedura de deplasare n ansamblu prezentat mai sus: Se consider un tablou h1 , ..., hn n care n mod evident elementele hn/2 , ..., hn formeaz deja un ansamblu deoarece nu exist nici o pereche de indici i i j care s satisfac relaia j=2*i (sau j=2*i+1). Aceste elemente formeaz cea ce poate fi considerat drept irul de baz al ansamblului asociat.

n continuare, ansamblul este extins spre stnga, la fiecare pas cu cte un element, introdus n vrf i deplasat pn la locul su.

Prin urmare, considernd c tabloul iniial este memorat n h, procesul de generare "in situ" al unui ansamblu poate fi descris prin secvena [3.2.5.d]. -----------------------------------------------------------{Faza creare ansamblu} s:= (n DIV 2)+1; WHILE s>1 DO BEGIN [3.2.5.d] s:= s-1; Deplasare(s,n) END;{WHILE} ----------------------------------------------------------- n vederea sortrii elementelor, se execut n pai de Deplasare, dup fiecare pas selectndu-se vrful ansamblului. Problema care apare este aceea a locului n care se memoreaz vrfurile consecutive ale ansamblului, respectiv elementele sortate, respectnd constrngerea "in situ". Aceast problem poate fi rezolvat astfel: n fiecare pas al procesului de sortare se interschimb ultima component a ansamblului cu componenta aflat n vrful acestuia (h[1]). Dup fiecare astfel de interschimbare ansamblul se restrnge la dreapta cu o component. n continuare se las componenta din vrf (h[1]) s se deplaseze spre locul su n ansamblu.

n termenii procedurii Deplasare aceast tehnic poate fi descris ca n secvena [3.2.5.e]. -----------------------------------------------------------{Faza sortare} d:= n; WHILE d >1 DO BEGIN temp:= h[1]; h[1]:= h[d]; h[d]:= temp; [3.2.5.e] d:= d-1; Deplasare(1,d) END;{WHILE} ----------------------------------------------------------- Cheile se obin sortate n ordine invers, lucru care poate fi uor remediat modificnd sensul relaiilor de comparaie din cadrul procedurii Deplasare. Rezult urmtorul algoritm care ilustreaz tehnica de sortare heapsort [3.2.5.f]. -----------------------------------------------------------{Sortare prin metoda ansamblelor Heapsort - Varianta Pascal} PROCEDURE Heapsort; VAR s,d: TipIndice; temp: TipElement; PROCEDURE Deplasare; VAR i,j: TipIndice; ret: boolean; BEGIN i:=s; j:= 2*i; temp:= h[i]; ret:= false; WHILE (j<=d) AND (NOT ret) DO BEGIN

IF j<d THEN IF h[j].cheie<h[j+1].cheie THEN j:= j+1; IF temp.cheie<h[j] THEN BEGIN h[i]:= h[j]; i:= j; j:= 2*i END ELSE ret:= true END;{WHILE} h[i]:= temp END; {Deplasare} BEGIN {Faza construcie ansamblu} s:= (n DIV 2)+1; d:= n; WHILE s>1 DO BEGIN s:= s-1; Deplasare END;{WHILE}

[3.2.5.f]

WHILE d>1 DO {Faza sortare} BEGIN temp:= h[1]; h[1]:= h[d]; h[d]:= temp; d:= d-1; Deplasare END END; {Heapsort} ---------------------------------------------------------- Analiza metodei heapsort. La prima vedere nu rezult n mod evident faptul c aceast metod conduce la rezultate bune. Analiza performanelor metodei heapsort contrazice ns aceast prere. La faza de construcie a ansamblului sunt necesari n/2 pai de deplasare, n fiecare fiecare pas se mut elemente de-a lungul a respectiv log(n/2) , log(n/2 +1) , ... , log(n-1) poziii, (n cel mai defavorabil caz), unde logaritmul se ia n baza 2 i se trunchiaz la prima valoare ntreag.

n continuare, faza de sortare necesit n-1 deplasri cu cel mult log (n-2) , log (n1) , ... , 1 micri. n plus mai sunt necesare 3 (n -1) micri pentru a aeza elementele sortate n ordine.

Toate acestea dovedesc c n cel mai defavorabil caz, tehnica heapsort are nevoie de un numr de pai de ordinul O(n log n) [3.2.5.g]. -----------------------------------------------------------O(n/2 log 2(n-1) + (n-1) log 2(n-1) + 3 (n-1)) = O(n log 2 n) [3.2.5.g] ----------------------------------------------------------- Este greu de determinat cazul cel mai defavorabil i cazul cel mai favorabil pentru aceast metod. n general ns, tehnica heapsort este mai eficient n cazurile n care elementele sunt ntr-o mai mare msur sortate n ordine invers; evident faza de creare a arborelui nu necesit nici o micare dac elementele sunt chiar n ordine invers.

Numrul mediu de micri este aproximativ egal cu 1/2 n log n , deviaiile de la aceast valoare fiind relativ mici. n manier specific metodelor de sortare avansate, valorile mici ale numrului de elemente n, nu sunt suficient de reprezentative, eficiena metodei crescnd o dat cu creterea lui n .

n secvena [3.2.5.h] se prezint varianta C a algoritmului de sortare prin metoda ansamblelor. -----------------------------------------------------------//sortare prin metoda ansamblelor - heapsort - varianta C deplasare(int s,int d) { //globale: int a[],int n int i=s,j=2*s,x=a[i-1],ret=0; while(j<=d && !ret) { if(j<d && a[j-1]<a[j])j++; (x<a[j-1])?(a[i-1]=a[j-1],i=j,j=2*i):(ret=1); } a[i-1]=x; } [3.2.5.h] heapsort() { //globale: int a[],int n //construire ansamblu int s=n/2+1,d=n; while(s-1)deplasare(--s,n); //sortare while(d-1) { int x=a[0]; a[0]=a[d-1]; a[d-1]=x; deplasare(1,--d); } } -----------------------------------------------------------3.2.6. Sortarea prin partiionare Dei metoda bubblesort bazat pe principiul interschimbrii este cea mai puin performant dintre metodele studiate, C.A.R. Hoare, pornind de la acelai principiu, a conceput o metod cu performane spectaculare pe care a denumit-o quicksort (sortare rapid). Aceasta se bazeaz pe aceeai idee de a crete eficiena interschimbrilor prin mrirea distanei dintre elementele implicate. Sortarea prin partiionare pornete de la urmtorul algoritm. Fie x un element oarecare al tabloului de sortat a1 , ..., an . Se parcurge tabloul de la stnga spre dreapta pn se gsete primul element ai > x . n continuare se parcurge tabloul de la dreapta spre stnga pn se gsete primul element aj < x . Se interschimb ntre ele elementele ai i aj , Se continu parcurgerea tabloului de la stnga respectiv de la dreapta (din punctele n care s-a ajuns anterior), pn se gsesc alte dou elemente care se interschimb, .a.m.d.

Procesul se termin cnd cele dou parcurgeri se "ntlnesc" undeva n interiorul tabloului. Efectul final este c acum irul iniial este partiionat ntr-o partiie stnga cu chei mai mici dect x i o partiie dreapta cu chei mai mari dect x.

Considernd elementele irului memorate n tabloul a , principiul partiionrii apare prezentat sintetic n [3.2.6.a]. -----------------------------------------------------------*Partiionarea unui tablou - varianta pseudocod procedure Partiionare {Partiioneaz tabloul a[s..d]} *selecteaz elementul x (de regul de la mijlocul intervalului de partiionat) repet *caut primul element a[i]>x, parcurgnd intervalul de la stnga la dreapta *caut primul element a[j]<x, parcurgnd intervalul de la dreapta la stnga dac i<=j atunci [3.2.6.a] *interschimb pe a[i] cu a[j] pn cnd parcurgerile se ntlnesc (i>j)

---------------------------------------------------------- nainte de a trece la sortarea propriu-zis, se d o formulare mai precis partiionrii n forma unei proceduri [3.2.6.b]. Se precizeaz c relaiile > respectiv < au fost nlocuite cu respectiv ale cror negate utilizate n instruciunile WHILE sunt < respectiv >.

n acest caz x joac rol de fanion pentru ambele parcurgeri. -----------------------------------------------------------{Procedura Partiionare - Varianta Pascal} PROCEDURE Partitionare; VAR x,temp: TipElement; BEGIN [1] i:= 1; j:= n; [2] x:= a[n DIV 2]; [3.2.6.b] [3] REPEAT [4] WHILE a[i].cheie<x.cheie DO i:= i+1; [5] WHILE a[j].cheie>x.cheie DO j:= j-1; [6] IF i<=j THEN BEGIN [7] temp:= a[i]; a[i]:= a[j]; a[j]:= temp; [8] i:= i+1; j:= j-1 END [9] UNTIL i>j END; {Partitionare} ----------------------------------------------------------- n continuare, cu ajutorul partiionrii, sortarea se realizeaz simplu: Dup o prim partiionare a secvenei de elemente se aplic aceeai procedur celor dou partiii rezultate, Apoi celor patru partiii ale acestora, .a.m.d.

Pn cnd fiecare partiie se reduce la un singur element.

Tehnica sortrii bazat pe partiionare este ilustrat n secvena [3.2.6.c]. -----------------------------------------------------------*Sortarea prin partiionare -quicksort - varianta pseudocod procedure QuickSort(s,d); *partiioneaz intervalul s,d fa de Mijloc dac exist partiie stnga atunci QuickSort(s,Mijloc-1) [3.2.6.c] dac exist partiie dreapta atunci QuickSort(Mijloc+1,d); ------------------------------------------------------- n secvena [3.2.6.d] apare o implementare a sortrii Quicksort n variant Pascal iar n secvena [3.2.6.e] varianta de implementare n limbajul C. -----------------------------------------------------------{Sortarea prin partiionare Quicksort - Varianta Pascal} PROCEDURE Quicksort; PROCEDURE Sortare(VAR s,d: TipIndice); VAR i,j: TipIndice; x,temp: TipElement; BEGIN i:= s; j:= d; x:= a[(s+d) DIV 2]; REPEAT WHILE a[i].cheie<x.cheie DO i:= i+1; WHILE x.cheie<a[j].cheie DO j:= j-1; [3.2.6.d] IF i<=j THEN BEGIN temp:= a[i]; a[i]:= a[j]; a[j]:= temp; i:= i+1; j:= j-1 END UNTIL i>j; IF s<j THEN Sortare(s,j); IF i<d THEN Sortare(i,d); END; {Sortare} BEGIN Sortare(1,n) END; {Quicksort} -----------------------------------------------------------//Sortarea prin partiionare - quicksort - varianta C quicksort(int s,int d) { //int a[],int n int i=s,j=d,x=a[(s+d)/2]; do { while(a[i]<x)i++; while(a[j]>x)j--; if(i<=j) { int w=a[i]; a[i]=a[j]; a[j]=w; i++;j--; } }while(i<=j); if(s<j)quicksort(s,j); if(d>i)quicksort(i,d);

[3.2.6.e]

} ----------------------------------------------------------- n continuare se prezint o manier de implementare a aceluiai algoritm utiliznd o procedur nerecursiv. Elementul cheie al soluiei iterative rezid n meninerea unei liste a cererilor de partiionare. Astfel, la fiecare trecere apar dou noi partiii. Una dintre ele se prelucreaz imediat, cealalt se amn, prin memorarea ei ca cerere n list. n mod evident, lista de cereri trebuie rezo lvat n sens invers, adic prima solicitare se va rezolva ultima i invers, Cu alte cuvinte lista se trateaz ca o stiv.

n secvena [3.2.6.f ] apare o schi de principiu a acestei implementri n variant pseudocod. -----------------------------------------------------------*Sortare QuickSort. Implementare nerecursiv - Varianta pseudocod procedure QuickSortNerecursiv; *se introduc n stiv limitele intervalului iniial de sortare (amorsarea procesului) repet *se extrage intervalul din vrful stivei care devine IntervalCurent *se reduce stiva cu o poziie [3.2.6.f] repet repet *se partiioneaz IntervalCurent pn cnd terminare partiionare dac exist interval drept atunci *se introduc limitele sale in stiv *se face intervalul stng IntervalCurent pn cnd intervalul ajunge de lime 1 sau 0 pn cnd stiva se golete ----------------------------------------------------------- n procedura Pascal care implementeaz algoritmul din secvena anterioar, O cerere de partiionare este reprezentat printr-o pereche de indici care delimiteaz zona de tablou ce urmeaz a fi partiionat. Stiva este modelat cu ajutorul unui tablou cu dimensiunea variabil numit stiva i de un index is care precizeaz vrful acesteia [3.2.6.g].

Dimensiunea maxim m a stivei va fi discutat pe parcursul analizei metodei. -----------------------------------------------------------{Sortare QuickSort. Implementare nerecursiv - Varianta Pascal}

PROCEDURE QuickSortNerecursiv; CONST m = ...; VAR i,j,s,d: TipIndice; x,temp: TipElement; is: 0..m; stiva: ARRAY[1..m] OF RECORD s,d: TipIndice END; BEGIN is:= 1; stiva[1].s:= 1; stiva[1].d:= n; REPEAT {se ia cererea din vrful stivei} s:= stiva[is].s; d:= stiva[is].d; is:= is-1; REPEAT {partiionarea lui a[s],a[d]} i:= s; j:= d; x:= a[(s+d) DIV 2]; REPEAT WHILE a[i].cheie<x.cheie DO i:= i+1; WHILE x.cheie<a[j].cheie DO j:= j-1; IF i<=j THEN [3.2.6.g] BEGIN temp:= a[i]; a[i]:= a[j]; a[j]:= temp; i:= i+1; j:= j-1 END UNTIL i>j; IF i<d THEN BEGIN {partiia dreapta se introduce n stiv} is:= is+1; stiva[is].s:= i; stiva[is].d:= d END; d:= j {partiia stnga devine curent} UNTIL s>=d UNTIL is=0 END; {QuickSortNerecursiv} ----------------------------------------------------------- Analiza metodei quicksort. Pentru a analiza performana acestei metode, se analizeaz mai nti partiionarea. Se presupune pentru simplificare, (1) C setul ce va fi partiionat const din n chei distincte i unice cu valorile { 1, 2, 3, ..., n} (2) C dintre cele n chei a fost selectat cheia cu valoarea x n vederea partiionrii.

n consecin aceast cheie ocup a x-a poziie n mulimea ordonat a cheilor i ea poart denumirea de pivot.

Se ridic urmtoarele ntrebri:

1. Care este probabilitatea ca dup ce a fost selectat cheia cu valoarea x ca pivot, o cheie oarecare a partiiei s fie interschimbat ? Pentru ca o cheie s fie interschimbat ea trebuie s fie mai mare ca x. Sunt n-x+1 chei mai mari ca x

Rezult c probabilitatea ca o cheie oarecare s fie interschimbat este (n x + 1) / n (raportul dintre numrul de cazuri favorabile i numrul de cazuri posibile).

2. Care este numrul de interschimbri necesar la o partiionare a n chei pentru care sa selectat ca pivot cheia situat pe poziia x ? La dreapta pivotului exist n x poziii care vor fi procesate n procesul de partiionare. Numrul posibil de interschimbri n acest context este deci egal cu produsul dintre numrul de chei care vor fi selectate (n x ) i probabilitatea ca o cheie selectat s fie interschimbat.
NrInt = (n x) (n x + 1) n

3. Care este numrul mediu de interschimbri pentru partiionarea unei secvene de n chei? La o partiionare poate fi selectat oricare din cele n chei ca i pivot, deci x poate lua orice valoare cuprins ntre 1 i n.

Numrul mediu M de interschimbri pentru partiionarea unei secvene de n chei se obine nsumnd toate numerele de interschimbri pentru toate valorile lui x cuprinse ntre 1 i n i mprind la numrul total de chei n [3.2.6.h]. -----------------------------------------------------------1 n 1 n (n x + 1) 6 1 n = M = NrInt = (n x) [3.2.6.h] n x =1 n x =1 n n 6n 6 ----------------------------------------------------------- Presupunnd n mod exagerat c ntotdeauna va fi selectat mediana partiiei (mijlocul su valoric), fiecare partiionare va divide tabloul n dou jumti egale. Se face precizarea c mediana este elementul situat ca i valoare n mijlocul partiiei, dac aceasta este ordonat. n aceste condiii este necesar un numr de treceri prin toate elementele tabloului egal cu log n (fig.3.2.6.a). Dup cum rezult din figura 3.2.6.a, pentru un tablou de 15 elemente sunt necesare log 2 15 = 4 treceri prin toate elementele tabloului sau 4 pai de partiionare integral a tabloului.
Numr elemente de partiionat pe apel 15 elemente 7 elemente 3 elem 1 1 1 1 3 elem 1 1 1 1 7 elemente 3 elem 1 3 elem 1 1 1 1 1 1 15 7 3 1

Numr apeluri 1 apel sortare (15 elemente) 2 apeluri sortare (7 elemente) 4 apeluri sortare (3 elemente) 8 apeluri sortare (1 element)

Fig. 3.2.6.a. Funcionarea principial a sortrii prin partiionare

Din pcate numrul de apeluri recursive ale procedurii este egal cu 15, adic exact cu numrul de elemente. Rezult c numrul total de comparaii este n log n (la o trecere sunt comparate toate cheile) Numrul de micri este n/6 log n deoarece conform formulei [3.2.6.h] la partiionarea a n chei sunt necesare n medie n/6 micri [3.2.6.i].

-----------------------------------------------------------1 C = n log 2 n M = n log 2 n [3.2.6.i] 6 ----------------------------------------------------------- Aceste rezultate sunt excepional de bune, dar se refer numai la cazul optim n care s-a presupus c la fiecare trecere se selecteaz mediana, eveniment care de altfel are probabilitatea doar 1/n. Marele succes al algoritmului quicksort se datorete ns faptului surprinztor c performana sa medie, la care alegerea pivotului se face la ntmplare, este inferioar performanei optime doar cu un factor egal cu 2 1n (2) = 1.4 deci cu aproximativ 40 % [Wi76]. Tehnica prezentat are ns i dezavantaje. n primul rnd ca i la toate metodele de sortare avansate, performanele ei sunt moderate pentru valori mici ale lui n. Acest dezavantaj poate fi contracarat prin ncorporarea unor metode de sortare directe pentru partiiile mici, lucru realizabil relativ simplu la aceast metod n raport cu alte metode avansate.

Un al doilea dezavantaj, se refer la cazul cel mai defavorabil n care performana metodei scade catastrofal. Acest caz apare cnd la fiecare partiionare este selectat cea mai mare (cea mai mic) valoare ca i pivot. Fiecare pas va partaja n acest caz secvena format din n elemente, ntr-o partiie stnga cu n 1 elemente i o partiie dreapta cu un singur element. Vor fi necesare astfel n partiionri n loc de log (n), iar performana obine valori de ordinul O(n2).

n mod aparent, elementul esenial al acestei metode l reprezint selecia pivotului x [GG78]. n exemplul prezentat, pivotul a fost ales la mijlocul partiiei. El poate fi ns ales la extremitatea stng sau dreapt a acesteia, situaie n care, cazul cel mai defavorabil l reprezint partiia deja sortat.

Tehnica quicksort se comport straniu: Are performane slabe n cazul sortrilor banale

Are performane deosebite n cazul tablourilor dezordonate.

De asemenea, dac x se alege ntotdeauna la mijloc (mediana), atunci tabloul sortat invers devine cazul optim al sortrii quicksort. De fapt performana medie este cea mai bun n cazul alegerii pivotului la mijlocul partiiei.

Hoare sugereaz ca alegerea s se fac: (1) Fie la "ntmplare", (2) Fie prin selecia medianei unui numr redus de chei (spre exemplu trei chei).

O astfel de alegere judicioas a pivotului influeneaz serios n mod negativ performana medie a algoritmului quicksort, dar nbuntete n mod considerabil performana cazului cel mai defavorabil.

Pentru programator, n multe situaii cazul cel mai defavorabil are o influen deosebit. Spre exemplu n secvena [3.2.6.g] care implementeaz metoda quiqsort n manier iterativ, n cazul cel mai defavorabil, la fiecare partiionare rezult o partiie dreapta cu un singur element, a crei cerere de sortare se introduce n stiv. Este evident c n acest caz, dimensiunea maxim a stivei trebuie s fie egal cu numrul de elemente n, situaie care nu este acceptabil. Acest lucru este i mai grav n cazul recursivitii unde stiva gestionat n mod automat este mult mai substanial, fiind necesar cte un nod pentru fiecare apel recursiv, nod care presupune spaiu de memorie pentru stocarea valorilor parametrilor locali ai apelului la care se adaug de regul i codul efectiv al procedurii. Aceast situaie se rezolv, introducnd n stiv cererea de sortare a partiiei mai mari i continund cu partiionarea partiiei mai mici. n acest caz dimensiunea m a stivei poate fi limitat la m = log 2 n .

Pentru a implementa aceast tehnic, secvena [3.2.6.g] se modific n poriunile n care se proceseaz cererile de partiionare conform [3.2.6.j]. -----------------------------------------------------------{Reducerea dimensiunii stivei n implementarea iterativ a sortrii Quicksort} IF j-s < d-i THEN BEGIN IF i<d THEN BEGIN {cerere sortare dreapta n stiv} is:= is+1; stiva[is].s:= i; stiva[is].d:= d END; d:= j {se continu sortarea partiiei stnga} END ELSE BEGIN [3.2.6.j]

IF s<j THEN BEGIN {cerere sortare stnga n stiv} s:= is+1; stiva[is].s:= s; stiva[is].d:= j END; s:= i {se continu sortarea partiiei dreapta} END; -----------------------------------------------------------3.2.7. Determinarea medianei Mediana a n elemente este definit ca fiind acel element care este mai mic (sau egal) dect jumtate din elemente i este mai mare (sau egal) dect cealalt jumtate. o Spre exemplu mediana secvenei 16, 12, 99, 95, 18, 87, 10 este 18 . Problema aflrii medianei este corelat direct cu cea a sortrii deoarece, o metod sigur de a determina mediana este urmtoarea: o Se sorteaz cele n elemente o Se extrage elementul din mijloc. Tehnica partiionrii poate ns conduce la o metod general mai rapid, cu ajutorul creia se poate determina cel de-al k-lea element ca valoare dintre n elemente. o Gsirea medianei reprezint cazul special k = n / 2 . o n acelai context, k = 1 precizeaz aflarea minimului, iar k = n , aflarea maximului.

Algoritmul conceput de C.A.R. Hoare funcioneaz dup cum urmeaz. o Se presupune c elementele avute n vedere sunt memorate n tabloul a cu dimensiunea n, o Pentru nceput se realizeaz o partiionare cu limitele s = 1, d = n i cu a[k] selectat pe post de pivot x. o n urma acestei partiionri rezult valorile index i i j care satisfac relaiile [3.2.7.a]. -----------------------------------------------------------1) x = a[k] 2) a[h] x pentru toi h < i 3) a[h] x pentru toi h > j [3.2.7.a] 4) i > j -----------------------------------------------------------o Sunt posibile trei situaii: 1. Valoarea pivotului x a fost prea mic, astfel nct limita dintre cele dou partiii este sub valoarea dorit k. Procesul de partiionare trebuie continuat pentru elementele a[i],...,a[d] (partiia dreapta) (fig.3.2.7.a (a)). 2. Valoarea pivotului x a fost prea mare. Operaia de partiionare continu pentru partiia a[s],...,a[j] (partiia stnga) (fig.3.2.7.a (b)). 3. j < k < i . n acest caz elementul a[k] separ tabloul n dou partiii, el desemnnd mediana (fig.3.2.7.a (c)). o Procesul de partiionare se repet pn la realizarea cazului 3.

s s

j k

i j

d d

(a) (b)

j k i

(c)

Fig. 3.2.7.a. Determinarea medianei Algoritmul aferent este prezentat n variant peseudocod n secvena [3.2.7.b] respectiv o prim rafinare n secvena de program [3.2.7.c].

-----------------------------------------------------------procedure Mediana (s,d,k); ct timp exist partiie [3.2.7.b] *alege pivotul (elementul din poziia k) *partiioneaz intervalul curent faa de valoarea pivotului dac poziie pivot<k atunci *selecteaz partiia dreapta dac poziie pivot>k atunci *selecteaz partiia stnga -----------------------------------------------------------{Procedura Mediana} s:= 1; d:= n ; WHILE s<d DO BEGIN x:= a[k]; [3.2.7.c] *se partiioneaz a[s]...a[d] IF j<k THEN s:= i; IF k<i THEN d:= j END; ----------------------------------------------------------- Programul aferent apare n secvena [3.2.7.d] n variant Pascal. -----------------------------------------------------------PROCEDURE Mediana (k:integer); VAR s,d,i,j: TipIndice; x,temp: TipElement; BEGIN s:=1; d:=n; WHILE s<d DO BEGIN x:= a[k]; i:= s; j:= d; REPEAT {partitionarea} [3.2.7.d] WHILE a[i]<x DO i:= i+1; WHILE x<a[j] DO j:= j-1; IF i<=j DO BEGIN temp:= a[i]; a[i]:= a[j]; a[j]:= temp; i:= i+1; j:= j-1 END UNTIL i>j; IF j<k THEN s:= i; IF k<i THEN d:= j END {WHILE} END; {Mediana} ---------------------------------------------------------- Dac se presupune c n medie fiecare partiionare njumtete partiia n care se gsete elementul cutat, atunci numrul necesar de comparaii C este de ordinul lui n [3.2.7.e]. -----------------------------------------------------------n n C = n + + + +1 = 2 n 1 [3.2.7.e] 2 4

---------------------------------------------------------- Numrul de micri M nu poate depi numrul de comparaii, el fiind de regul mai mic. Valorile indicatorilor C i M estimai pentru determinarea medianei subliniaz superioritatea acestei metode i explic performana ei fa de metodele bazate pe sortarea tabloului i extragerea celui de-al k-lea element, a cror performan n cel mai bun caz este de ordinul O(n log n). n cel mai defavorabil caz, fiecare partiionare reduce setul de candidai numai cu 1, rezultnd un numr de comparaii de ordinul O(n2). i n acest caz metoda medianei este indicat pentru valori mari ale lui n (n > 10).

3.2.8. Sortarea binsort. Determinarea distribuiei cheilor n general algoritmii de sortare bazai pe metode avansate au nevoie de O(n log n) pai pentru a sorta n elemente. Trebuie precizat ns faptul c acest lucru este valabil n situaia n care nu exist nici o alt informaie suplimentar referitoare la chei, dect faptul c pe mulimea acestora este definit o relaie de ordonare, prin intermediul creia se poate preciza dac valoarea unei chei este mai mic respectiv mai mare dect o alta. Dup cum se va vedea n continuare, sortarea se poate face i mai rapid dect n limitele performanei O(n log n), dac: o Exist i alte informaii referitoare la cheile care urmeaz a fi sortate o i se renun mcar parial la constrngerea de sortare "in situ".

Spre exemplu, se cere s se sorteze un set de n chei de tip ntreg, ale cror valori sunt unice i aparin intervalului de la 1 la n. o n acest caz, dac a i b sunt tablouri identice cu cte n elemente, a coninnd cheile care urmeaz a fi sortate, atunci sortarea se poate realiza direct n tabloul b conform secvenei [3.2.8.a]. -----------------------------------------------------------FOR i:= 1 TO n DO b[a[i].cheie]:= a[i]; {O(n)} [3.2.8.a] -----------------------------------------------------------o n secvena [3.2.8.a] se determin locul elementului a[i] i se plaseaz elementul la locul potrivit. o ntregul ciclu necesit O(n) pai. o Rezultatul este ns corect numai n cazul n care exist un singur element cu cheia x, pentru fiecare valoare cuprins ntre [1,n]. Un al doilea element cu aceeai cheie va fi introdus tot n b[x] distrugnd elementul anterior. Acest tip de sortare poate fi realizat i "in situ", element care prezint interes din punctul de vedere al tehnicilor analizate. o Astfel, fiind dat tabloul a de dimensiune n, ale crui elemente au respectiv cheile 1, ..., n, se baleeaz pe rnd elementele sale. o Dac elementul a[i] are cheia j, atunci se realizeaz interschimbarea lui a[i] cu a[j]. o Fiecare interschimbare plaseaz elementul aflat n locaia i exact la locul su n tabloul ordonat, fiind necesare n cel mai ru caz 3 n micri pentru ntreg procesul de sortare. o Secvena de program care ilustreaz aceast tehnic apare n [3.2.8.b]. -----------------------------------------------------------FOR i:= 1 TO n DO WHILE a[i].cheie<>i DO BEGIN [3.2.8.b] temp:= a[i];a[i]:= a[a[i].cheie]; a[temp.cheie]:= temp END; ----------------------------------------------------------- Secvenele [3.2.8.a, b] ilustreaz tehnica de sortare numit binsort, n cadrul creia se creaz bin-uri, fiecare bin pstrnd un element sortat cu o anumit cheie [AHU85]. Tehnica sortrii este simpl: o se examineaz fiecare element de sortat i se introduce n bin-ul corespunztor valorii cheii. n secvena [3.2.8.a] bin-urile sunt chiar elementele tabloului b, unde b[i] este binul cheii avnd valoarea i, iar n secvena [3.2.8.b] bin-urile sunt chiar elementele tabloului a dup reaezare.

Tehnica aceasta att de simpl i de performant se bazeaz pe urmtoarele cerine apriorice: o (1) Domeniul limitat al cheilor (1, n) o (2) Unicitatea fiecrei chei. Dac cea de-a doua cerin nu este respectat, i de fapt acesta este cazul obinuit, este necesar ca ntr-un bin s fie memorate mai multe elemente avnd aceeai cheie. Acest lucru se realizeaz fie prin niruire, fie prin concatenare, fiind utilizate n acest scop structuri de date list. Aceast situaie nu deterioreaz prea mult performanele acestei tehnici, efortul de sortare ajungnd egal cu O(n+m), unde n este numrul de elemente iar m numrul de chei, motiv pentru care aceast metod reprezint punctul de plecare al mai multor tehnici de sortare a structurilor list [AHU85]. Spre exemplu, o metod de rezolvare a unei astfel de situaii este cea bazat pe determinarea distribuiei cheilor ("distribution counting") [Se88]. Problema se formuleaz astfel: se cere s se sorteze un tablou cu n articole ale cror chei sunt cuprinse ntre 0 i m-1. Dac m nu este prea mare pentru rezolvarea problemei poate fi utilizat algoritmul de "determinare a distribuiei cheilor".

Ideea algoritmului este: o Se contorizeaz ntr-o prim trecere numrul de chei pentru fiecare valoare de cheie care apare n tabloul a; o Se ajusteaz valorile contoarelor; o ntr-o a doua trecere, utiliznd aceste contoare, se mut direct articolele n poziia lor ordonat n tabloul b. o Formularea algoritmului este cea din secvena [3.2.8.c]. Pentru simplificare se presupune c tabloul a este un tablou care conine doar chei. -----------------------------------------------------------TYPE TipCheie = 0..m-1; TipTablou = ARRAY [1..n] OF TipCheie; VAR numar: ARRAY[0..m-1] OF TipCheie; a,b: TipTablou; i,j: TipIndice; { Sortare bazat pe determinarea distribuiei cheilor (distribution counting)} BEGIN FOR j:= 1 TO m-1 DO numar[j]:= 0; FOR i:= 1 TO n DO numar[a[i]]:= numar[a[i]]+1; FOR j:= 1 TO m-1 DO numar[j]:= numar[j-1]+numar[j]; FOR i:=n DOWNTO 1 DO BEGIN b[numar[a[i]]]:= a[i]; [3.2.8.c] numar[a[i]]:= numar[a[i]]-1 END; FOR i:= 1 TO n DO a[i]:= b[i]; END; {Sortare cu determinarea distributiilor} ----------------------------------------------------------- Contoarele asociate cheilor sunt memorate n tabloul numar de dimensiune m . Iniial locaiile sunt iniializate pe zero (prima bucl FOR) Sunt contorizate cheile (a doua bucl FOR).

n continuare sunt ajustate valorile contoarelor tabloului numar (a treia bucl FOR) astfel nct, Parcurgnd tabloul a de la sfrit spre nceput, cheile s poat fi introduse exact la locul lor n tabloul b cu ajutorul contoarelor memorate n tabloul numar (a patra bucl FOR). Concomitent cu introducerea are loc i ajustarea contoarelor astfel nct cheile identice s fie introduse n binul specific n ordinea relativ n care apar ele n secvena iniial. Ultima bucl realizeaz mutarea integral a elementelor tabloului b n tabloul a, dac acest lucru este necesar. Dei se realizeaz mai multe treceri prin elementele tabloului totui n ansamblu, performana algoritmului este O(n) . Aceasta metod de sortare pe lng faptul c este rapid are avantajul de a fi stabil, motiv pentru care ea st la baza mai multor metode de sortare de tip radix.

n continuare se prezint un exemplu de funcionare a algoritmului de sortare bazat pe determinarea distribuiei cheilor. ----------------------------------------------------------- Exemplul 3.2.8. Schematic, n vederea sortrii cu determinarea distribuiei cheilor se parcurg urmtorii pai.

1) Se consider iniial c tabloul a are urmtorul coninut:


1 a 0 2 b 1 3 b 1 4 a 0 5 c 2 6 a 0 7 D 3 8 a 0 9 b 1 10 b 1 11 a 0 12 d 3 13 d 3 14 a 0

2) Se iniializeaz tabloul numar:


0 0 1 0 2 0 3 0

3) Se contorizeaz valorile cheilor tabloului a:


0 6 1 4 2 1 3 3

4) Se ajusteaz valorile tabloului numar:


0 6 1 10 2 11 3 14

5) Se iau elementele tabloului a de la dreapta la stnga i se introduc pe rnd n tabloul b, fiecare n poziia indicat de contorul propriu din tabloul numar. Dup introducerea fiecrui element n tabloul b, contorul specific din tabloul numar este decrementat cu o unitate.

1 a

2 a

3 a

4 a

5 a

6 a

7 b

8 b

9 b

10 b

11 c

12 d

13 d

14 d

-----------------------------------------------------------------

3.2.9. Sortarea bazat pe baze de numeraie. Radix sort Metodele de sortare prezentate pn n prezent, concep cheile de sortat ca entiti pe care le prelucreaz integral prin comparare sau interschimbare. n unele situaii ns se poate profita de faptul c aceste chei sunt de fapt numere aparinnd unui domeniu mrginit. Metodele de sortare care iau n considerare proprietile digitale ale numerelor sunt metodele de sortare bazate pe baze de numeraie ("radix sort").

Algoritmii de tip baz de numeraie, trateaz cheile ca i numere reprezentate ntr-o baz de numeraie m, unde m poate lua diferite valori ("radix") i proceseaz cifrele individuale ale numrului. Un exemplu sugestiv l reprezint sortarea unui teanc de cartele care au perforate pe ele numere formate trei cifre. o Se grupeaz cartelele n 10 grupe distincte, prima cuprinznd cheile mai mici dect 100, a doua cheile cuprinse ntre 100 i 199, etc., adic se realizeaz o sortare dup cifra sutelor. o n continuare se sorteaz pe rnd grupele formate aplicnd aceeai metod, dup cifra zecilor, o Apoi fiecare grup nou format, dup cifra unitilor. o Acesta este un exemplu simplu de sortare radix cu m = 10.

Pentru sistemele de calcul, unde prelucrrile se fac exclusiv n baza 2, se preteaz cel mai bine metodele de sortare radix care opereaz cu numere binare. n general, n sortarea radix a unui set de numere, operaia fundamental este extragerea unui set contiguu de bii din numrul care reprezint cheia. Spre exemplu pentru a extrage primii 2 bii ai unui numr binar format din 10 cifre binare: o Se realizeaz o deplasare la dreapta cu 8 poziii a reprezentrii numrului, o Apoi se opereaz configuraia obinut, printr-o operaie "i" cu masca 0000000011. Aceste operaii pot fi implementate direct cu ajutorul facilitilor de prelucrare a configuraiilor la nivel de bii puse la dispoziie de limbajele moderne de programare sau pot fi simulate cu ajutorul operatorilor ntregi DIV i MOD. o Spre exemplu, n situaia anterioar, dac x este numrul binar n cauz, primii doi bii se obin prin expresia (x DIV 28)MOD 22. n cadrul acestui paragraf, pentru exemplificarea sortrilor radix, se consider definit o funcie biti(x,k,j:integer):integer care combin cele dou operaii returnnd j bii care apar la k poziii de la marginea dreapt a lui x.

O posibil implementare n limbajul C a acestei funcii apare n secvena [3.2.9.a]. -----------------------------------------------------------unsigned biti(unsigned x, int k, int j){ return (x>>k)&~(~0<<j);} [3.2.9.a] ----------------------------------------------------------- Exist dou metode de baz pentru implementarea sortrii radix. Prima metod examineaz biii cheilor de la stnga la dreapta i se numete sortare radix prin interschimbare ("radix exchange sort"). o Ea se bazeaz pe faptul c rezultatul comparaiei a dou chei este determinat de valoarea biilor din prima poziie la care ele difer. o Astfel, elementele ale cror chei au primul bit 0 sunt trecute n faa celor care au primul bit 1; o n continuare n fiecare grup astfel format se aplic aceeai metod pentru bitul urmtor i aa mai departe. o Sortarea propriu-zis se realizeaz prin schimbarea sistematic a elementelor n maniera precizat. A doua metod se numete sortare radix direct ("straight radix sort").

o Ea examineaz biii din cadrul cheilor de la dreapta la stnga i se bazeaz pe principiul interesant care reduce sortarea cheilor de b-bii la b sortri ale unor chei de 1bit.

3.2.9.1. Sortarea radix prin interschimbare

Se presupune c se dorete sortarea elementelor tabloului a astfel nct toate elementele ale cror chei ncep cu un bit zero s fie trecute n faa celor care ncep cu 1. o Aceasta va avea drept consecin formarea a dou partiii ale tabloului iniial, o Aceste partiii la rndul lor se sorteaz independent, conform aceleai metode dup cel de-al doilea bit al cheilor elementelor, o Cele 4 partiii rezultate dup al 3-lea bit i aa mai departe. Acest mod de lucru sugereaz abordarea recursiv a implementrii metodei de sortare. Procesul se desfoar exact ca i la partiionare: o Se baleaz tabloul de la stnga spre dreapta pn se gsete un element a crui cheie care ncepe cu 1;

o Se baleeaz tabloul de la dreapta spre stnga pn se gsete un element a crui cheie ncepe cu 0; o Se interschimb cele dou elemente; o Procesul continu pn cnd pointerii de parcurgere se ntlnesc formnd dou partiii. o Se reia aceeai procedur pentru cel de-al doilea bit al cheilor elementelor n cadrul celor dou partiii rezultate .a.m.d. [3.2.9.1.a]. -----------------------------------------------------------PROCEDURE RadixInterschimb (s,d: TipIndice, b:INTEGER); {s,d - limitele curente ale tabloului de sortat} {b - lungimea n bii a cheii de sortat} VAR i,j: TipIndice; t: TipElement; BEGIN [3.2.9.1.a] IF (d>s) AND (b>=0) THEN BEGIN i:= s; j:= d; b:= b-1; REPEAT WHILE(biti(a[i].cheie,b,1)=0)AND(i<j) DO i:= i+1; WHILE(biti(a[j].cheie,b,1)=1)AND(i<j) DO j:= j+1; t:= a[i]; a[i]:= a[j]; a[j]:= t UNTIL j=i; IF biti(a[d].cheie,b,1)= 0 THEN j:= j+1; {dac toi biii testai sunt 0 se reface lungimea partiiei} RadixInterschimb(s,j-1,b-1); RadixInterschimb(j,d,b-1); END {IF} END; {RadixInterschimb} ----------------------------------------------------------- Astfel dac se presupune c tabloul a[1..15] conine chei ntregi care au valoarea mai mic dect 25 (adic se reprezint utiliznd 5 cifre binare), atunci apelul RadixInterschimb(1,15,5) va realiza sortarea dup cum se prezint schematic n figura 3.2.9.1.

A S O R T I N G E X A M P L E

00001 10011 01111 10010 10100 01001 01110 00111 00101 11000 00001 01101 10000 01100 00101

A E O L M I N G E A X T P R S

0 0 0 0 0 0 0 0 0 0 1 1 1 1 1

0001 0101 1111 1100 1101 1001 1110 0111 0101 0001 1000 0100 0000 0010 0011

A E A E G I N M L O S T P R X

0 0 0 0 0 0 0 0 0 0 1 1 1 1 1

0 0 0 0 0 1 1 1 1 1 0 0 0 0 1

001 101 001 101 111 001 110 101 100 111 011 100 000 010 000

A A E E G I M M L O S R P T

00 00 00 00 00 01 01 01 01 01 10 10 10 10

0 1 1 1 1 0 1 1 1 1 0 0 0 1

01 01 01 01 11 01 10 01 00 11 11 10 00 00

A A E E G L M N O P R S

000 000 001 001 001 011 011 011 011 100 100 100

0 0 0 0 1 0 0 1 1 0 1 1

1 1 1 1 1 0 1 0 1 0 0 1

A A E E

0000 0000 0010 0010

1 1 1 1

L M N O

0110 0110 0111 0111

0 1 0 1

R 1001 0 S 1001 1

A A E E G I L M N O P R S T X

Fig. 3.2.9.1. Sortare radix prin interschimbare

O problem serioas care afecteaz aceast metod se refer la degenerarea partiiilor. o Degenerarea partiiilor apare de obicei n situaia n care cheile sunt reprezentate prin numere mici (care ncep cu multe zerouri). o Aceast situaie apare frecvent n cazul caracterelor interpretate pe 8 bii. Din punctul de vedere al performanei, metoda de sortare radix prin interschimbare sorteaz n chei de b bii utiliznd un numr de comparaii de bii egal cu n b . o Cu alte cuvinte, sortarea radix prin interschimbare este liniar cu numrul de bii ai unei chei. o Pentru o distribuie normal a biilor cheilor, metoda lucreaz ceva mai rapid dect metoda quicksort [Se88].

3.2.9.2. Sortarea radix direct

O alt variant de implementare a sortrii radix este aceea de a examina biii cheilor elementelor de la dreapta la stnga. Este metoda utilizat de vechile maini de sortat cartele. o Teancul de cartele trece de 80 de ori prin main, cte odat pentru fiecare coloan ncepnd de la dreapta spre stnga, fiecare trecere realiznd sortarea dup o coloan. o n cazul cheilor, cnd se ajunge la bitul i venind dinspre dreapta, cheile sunt gata sortate pe ultimii i-1 bii ai lor. o Sortarea continu extrgnd toate elementele ale cror chei au zero pe poziia i i plasndu-le n faa celor care au 1 pe aceeai poziie. Nu este uor de demonstrat c metoda este corect: de fapt ea este corect numai n situaia n care sortarea dup 1 bit este stabil. Datorit acestei cerine, sortarea radix prin interschimbare nu poate fi utilizat deoarece nu este o metod de sortare stabil. n ultim instan trebuie sortat stabil un tablou cu numai dou valori 0 i 1.

Metoda bazat pe determinarea distribuiilor (distribution counting) poate fi utilizat cu succes n acest scop. o Se consider algoritmul de sortare bazat pe determinarea distribuiei cheilor n care: Se ia m = 2 i Se nlocuiete a[i].cheie cu biti(a[i].cheie,k,1) pentru k = 0, 1, 2, ..., b -1 (adic se extrage din cheie 1 bit situat la distana k fa de sfritul cheii) . o Se obine astfel, o metod de sortare stabil a tabloului a, dup bitul k, de la dreapta la stnga, rezultatul fiind memorat ntr-o tablou temporar t . o Se reia sortarea bazat pe determinarea distribuiilor pentru fiecare bit al cheii respectiv pentru pentru k = 0, 1, 2, ..., b -1 o Rezult c pentru sortare sunt necesare b treceri unde b este lungimea cheii. De fapt, nu este indicat s se lucreze cu m = 2 , ci este convenabil ca m s fie ct mai mare. Dac se prelucreaz m bii odat, timpul de sortare scade, dar tabela de distribuii crete ca dimensiune ea trebuind s conin m1 = 2m locaii. n acest mod, sortarea radix-direct devine o generalizare a sortrii bazate pe determinarea distribuiilor. Algoritmul din secvena [3.2.9.2.a] sorteaz dup aceast metod tabloul a[1..n], o Cheile sunt de b bii lungime, o Cheile sunt parcurse de la dreapta la stnga, procesnd cte m bii odat. o Se utilizeaz tabloul suplimentar t[1..n]. Procedura funcioneaz numai dac b este multiplu de m deoarece algoritmul de sortare divizeaz biii cheilor ntr-un numr ntreg de pri de dimensiuni egale care se proceseaz deodat. Dac se ia m = b se obine sortarea bazat pe determinarea distribuiilor; Dac se ia m = 1 rezult sortarea radix direct. Implementarea propus sorteaz tabloul a n tabloul t i dup fiecare pas de sortare recopiaz tabloul t n a (ultima bucl FOR).

Acest lucru poate fi evitat, concatennd n aceeai procedur dou copii ale algoritmului de sortare: una care sorteaz din a n t, cealalt din t n a. -----------------------------------------------------------PROCEDURE RadixDirect; VAR i,j,trecere: integer; numar: ARRAY[0..m1-1] OF integer; {m1:=2m} BEGIN FOR trecere:= 0 TO (b DIV m)-1 DO BEGIN [3.2.9.2.a] FOR j:= 0 TO m1-1 DO numar[j]:= 0; FOR i:= 1 TO n DO numar[biti(a[i].cheie,trecere*m,m)]:= numar[biti(a[i].cheie,trecere*m,m)] + 1; FOR j:= 1 TO m1-1 DO numar[j]:= numar[j-1]+numar[j]; FOR i:= n DOWNTO 1 DO BEGIN t[numar[biti(a[i].cheie,trecere*m,m)]]:= a[i]; numar[biti(a[i].cheie,trecere*m,m)]:= numar[biti(a[i].cheie,trecere*m,m)]-1 END; FOR i:= 1 TO n DO a[i]:= t[i]

END {FOR} END; {RadixDirect} ---------------------------------------------------------- Ca performan, sortarea radix direct: o Poate sorta n elemente cu chei de b bii n b/m treceri, o Utiliznd spaiu suplimentar de memorie pentru 2m contoare o i un buffer pentru rearanjarea tabloului cu dimensiunea egal cu cea a tabloului original.

3.2.9.2. Performana sortrilor radix

Timpul de execuie al celor dou metode de sortare radix fundamentale, pentru n elemente avnd chei de b bii este n esen proporional cu n * b . Pe de alt parte, timpul de execuie poate fi aproximat ca fiind n log (n) , deoarece dac toate cheile sunt diferite, b trebuie s fie cel puin log (n) . n realitate, nici una din metode nu atinge de fapt limita precizat n * b : o Metoda de la stnga la dreapta se oprete cnd apare o diferen ntre chei, o Metoda de la dreapta la stnga poate prelucra mai muli bii deodat. Sortrile radix se bucur de urmtoarele proprieti [Se88]. o Proprietatea 1. Sortarea radix prin interschimbare examineaz n medie n log (n) bii. o Proprietatea 2. La sortarea a n chei de cte b bii, ambele sortri radix examineaz de regul mai puini dect n * b bii. o Proprietatea 3. Sortarea radix direct poate sorta n elemente cu chei de b bii, n b/m treceri utiliznd un spaiu suplimentar pentru 2m contoare i un buffer pentru rearanjarea tabloului.

3.2.9.3. Sortarea liniar

Sortarea radix direct, anterior prezentat realizeaz b/m treceri prin tabloul de sortat. Dac se alege pentru m o valoare suficient de mare se obine o metod de sortare foarte eficient, cu condiia s existe un spaiu suplimentar de memorie cu 2m locaii. O alegere rezonabil a valorii lui m este b/4 (un sfert din dimensiunea cheii) sortarea radix reducndu-se n acest caz la 4 sortri bazate pe determinarea distribuiilor, care sunt practic liniare. o De obicei valorile m = 4 sau m = 8 sunt propice pentru actuala organizare a sistemelor de calcul i conduc la dimensiuni rezonabile ale tabloului de contoare (16 respectiv 256 de locaii). o n consecin fiecare pas este liniar i deoarece sunt necesari numai 8 (4) pai pentru chei de 32 de bii, procesul de sortare este practic liniar. o Rezult astfel este una din cele mai performante metode de sortare, care concureaz metoda quicksort, departajarea ntre ele fiind o problem dificil. Dezavantajele majore ale metodei sunt: o Necesitatea distribuiei uniforme a cheilor o Necesitatea unor spaii suplimentare de memorie pentru tabloul de contoare i pentru zona de sortare.

3.2.10. Sortarea tablourilor cu articole de mari dimensiuni. Sortarea indirect n situaia n care tablourile de sortat au elemente de mari dimensiuni, regia mutrii acestor elemente n procesul sortrii este mare. De aceea este mult mai convenabil ca algoritmul de sortare s opereze indirect asupra tabloului original prin intermediul unui tablou de indici, urmnd ca tabloul original s fie sortat ulterior ntr-o singur trecere. o Astfel, unui tablou a[1..n] cu elemente de mari dimensiuni, o I se asociaz un tablou de indici (indicatori) p[1..n]; o Iniial se definete p[i]:=i pentru i=1,n; o Algoritmul utilizat n sortare se modific astfel nct s se acceseze elementele tabloului a prin construcia a[p[i]] n loc de a[i]. o Accesul la a[i] prin p[i] se va realiza numai pentru comparaii, mutrile efectundu-se n tabloul p[i]. o Cu alte cuvinte algoritmul va sorta tabloul de indici astfel nct p[1] va conine indicele celui mai mic element al tabloului a, p[2] indicele elementului urmtor, etc. n acest mod se evit regia mutrii unor elemente de mari dimensiuni. Se realizeaz de fapt o sortare indirect a tabloului a. Principial o astfel de sortare este prezentat n figura 3.2.10.

Tabloul a nainte de sortare:

1 32 1 1 1 32 1 3

2 22 2 2 2 22 2 4

3 0 3 3 3 0 3 9

4 1 4 4 4 1 4 8

5 5 5 5 5 5 5 5

6 16 6 6 6 16 6 6

7 99 7 7 7 99 7 2

8 4 8 8 8 4 8 1

9 3 9 9 9 3 9 10

10 50 10 10 10 50 10 7

Tabloul de indici p nainte de sortare:

Tabloul a dup sortare:

Tabloul de indici p dup sortare:

Fig. 3.2.10. Exemplu de sortare indirect

Aceast idee poate fi aplicat practic oricrui algoritm de sortare. Pentru exemplificare n secvena [3.2.10.a] se prezint un algoritm care realizeaz
sortarea indirect bazat pe metoda inseriei a unui tablou a.

-----------------------------------------------------------VAR a: ARRAY[0..n] OF TipElement; p: ARRAY[0..n] OF TipIndice; PROCEDURE InsertieIndirecta; VAR i,j,v: TipIndice; BEGIN [3.2.10.a] FOR i:= 0 TO n DO p[i]:= i; FOR i:= 2 TO n DO BEGIN v:= p[i]; a[0]:= a[i]; j:= i-1; WHILE a[p[j]].cheie>a[v].cheie DO BEGIN p[j+1]:= p[j]; j:= j-1 END;{WHILE} p[j+1]:= v END {FOR} END; {InsertieIndirecta} ------------------------------------------------------------

Dup cum se observ, cu excepia atribuirii fanionului, accesele la tabloul a se


realizeaz numai pentru comparaii.

n multe aplicaii este suficient numai obinerea tabloului p nemaifiind necesar i


permutarea elementelor tabloului. Spre exemplu, n procesul tipririi, elementele pot fi listate n ordine, referirea la ele realizndu-se simplu, n mod indirect prin tabloul de indici.

Dac este absolut necesar mutarea, cel mai simplu acest lucru se poate realiza ntr-un
alt tablou b.

Dac acest lucru nu se accept, se poate utiliza procedura de reaezare in situ din
secvena [3.2.10.b]. -----------------------------------------------------------PROCEDURE MutareInSitu; VAR i,j,k: TipIndice; t: TipElement; BEGIN FOR i:= 1 TO n DO IF p[i]<>i THEN [3.2.10.b] BEGIN t:= a[i]; k:= i; REPEAT j:= k; a[j]:= a[p[j]]; k:= p[j]; p[j]:= j; UNTIL k=i; a[j]:= t END {IF} END; {MutareInSitu} -----------------------------------------------------------relativ a cheilor i articolelor.

n cazul unor aplicaii particulare, viabilitatea acestei tehnici depinde de lungimea Desigur ea nu se justific pentru articole mici deoarece necesit o zon de memorie
suplimentar pentru tabloul p i timp suplimentar pentru comparaiile indirecte. realiza permutarea efectiv a elementelor. inclusiv permutarea elementelor [Se88].

Pentru articole de mari dimensiuni se indic de regul sortarea indirect, fr a se mai Pentru articolele de foarte mari dimensiuni metoda se indic a se utiliza integral,

3.3. Sortarea secvenelor. Sortarea extern Metodele de sortare prezentate n paragraful anterior nu pot fi aplicate unor date care
nu ncap n memoria central a sistemului, dar care pot fi spre exemplu memorate pe dispozitive periferice secveniale cum ar fi benzile magnetice. drept caracteristic esenial faptul c n fiecare moment este accesibil doar o singur component. Aceasta este o restricie foarte sever comparativ cu accesul direct oferit de structura tablou, motiv pentru care tehnicile de sortare sunt de cu totul alt natur. "interclasare" (merging).

n acest caz datele pot fi descrise cu ajutorul unei structuri de tip secven avnd

Una dintre cele mai importante tehnici de sortare a secvenelor este sortarea prin

3.3.1. Sortarea prin interclasare

Interclasarea presupune combinarea a dou sau mai multe secvene ordonate ntr-o
singur secven ordonat, prin selecii repetate ale componentelor curent accesibile. complex al sortrii secveniale.

Interclasarea este o operaie simpl, utilizat ca auxiliar n procesul mult mai O metod de sortare bazat pe interclasare a unei secvene a este urmtoarea:

1. Se mparte secvena de sortat a n dou jumti b i c. 2. Se interclaseaz b cu c, combinnd cte un element din fiecare, n perechi ordonate obinndu-se o nou secven a. 3. Se repet cu secvena interclasat a, paii 1 i 2 de aceast dat combinnd perechile ordonate n qvadruple ordonate. 4. Se repet paii iniiali, interclasnd qvadruplele n 8-uple, .a.m.d, de fiecare dat dublnd lungimea subsecvenelor de interclasare pn la sortarea ntregii secvene.

Spre exemplu fie secvena:


34 65 34 83 34 04 83 12 | 18 34 12 22 65 18 65 83 | 83 12 04 | 04 18 18 22 67 12 22 | 22 65 67 67 04 67

Execuia pasului 1 conduce la dou jumti de secven; Interclasarea componentelor unice n perechi ordonate conduce la secvena njumtind din nou i interclasnd perechile n qvadruple se obine: Cea de-a treia njumtire i interclasarea celor dou qvadruple ntr-un 8-uplu
conduc la secvena gata sortat: 04 12 18 22 34 65 67 83

Fiecare operaie care trateaz ntregul set de date se numete faz; Procesul prin repetarea cruia se realizeaz sortarea se numete trecere. Procesul de sortare anterior descris const din trei treceri fiecare cuprinznd o faz de
njumtire i una de interclasare.

Pentru a realiza sortarea sunt necesare trei secvene motiv pentru care sortarea se

numete interclasare cu trei secvene. Este de fapt o interclasare neechilibrat cu 3 secvene.

3.3.1.1. Interclasarea neechilibrat cu trei secvene

Interclasarea neechilibrat cu trei secvene reprezint implementarea procedeului de


sortare precizat anterior. Schema de principiu a acestui procedeu apare n figura 3.3.1.1. b a c

Fig. 3.3.1.1. Interclasare neechilibrat cu trei secvene

ntr-o prim etap n secvenele [3.3.1.1.a, b] se prezint structurile de date i schia


de principiu a algoritmului.

Dup cum se observ, fiecare trecere care const dintr-o reluare a buclei REPEAT,

conine dou faze: O faz de njumtire adic de distribuie a n-uplelor secvenei a pe cele dou secvene b i c, respectiv

n n-uple de dimensiune dubl pe secvena a. Variabila p iniializat pe 1, precizeaz dimensiunea n-uplelor curente, dimensiune care dup fiecare trecere se dubleaz. n consecin numrul total de treceri va fi log 2 n . -----------------------------------------------------------TYPE TipElement = RECORD cheie: TipCheie; {alte campuri} [3.3.1.1.a] TipSecventa = FILE OF TipElement; VAR a,b,c: TipSecventa; -----------------------------------------------------------{Interclasare neechilibrata cu 3 secvene} PROCEDURE Interclasare3Secvente; p:= 1; {dimensiune n-uplu} [3.3.1.1.b] REPEAT *injumatatire; {distribuie pe a pe b i c} *interclasare; {interclaseaz de pe b i c pe a] p:= 2*p UNTIL k=1; {k este contorul de n-uple} ------------------------------------------------------------

O faz de interclasare n care n-uplele de pe secvenele b i c se interclaseaz

Variabila k contorizeaz numrul de n-uple create n procesul de interclasare. Procesul de sortare se ncheie cnd n final rmne un singur n-uplu de dimensiune n
(k=1).

n continuare se procedeaz la rafinarea celor dou faze. n secvena [3.3.1.1.c] apare primul pas de rafinare al fazei de njumtire, iar n
secvena urmtoare rafinarea enunului scrie un n-uplu de dimensiune p n secvena d. -----------------------------------------------------------PROCEDURE Injumatatire(p: integer); {distribuie n-uplele de pe a pe b si c} {p - dimensiune n-uplu} RESET(a); REWRITE(b); REWRITE(c); WHILE NOT Eof(a) DO BEGIN [3.3.1.1.c] *scrie un n-uplu pe b *scrie un n-uplu pe c END; -----------------------------------------------------------PROCEDURE ScrieNuplu(d: TipSecventa); {scrie un n-uplu de dimensiune p n secvena d} i:= 0; {contor elemente n-uplu} WHILE (i<p) AND NOT Eof(a) DO BEGIN *citeste(a,x); [3.3.1.1.d] *scrie(d,x) i:= i+1 END; ------------------------------------------------------------

Variabila i reprezint contorul de elemente care poate lua valori ntre 0 i p. Scrierea
se termin la atingerea numrului p de elemente sau la terminarea secvenei surs.

Rafinarea fazei de interclasare apare n secvena [3.3.1.1.e]. Variabila de intrare p reprezint dimensiunea n-uplelor care se interclaseaz, iar k
este contorul de n-uple.

Practic interclasarea propriu-zis (bucla REPEAT) se ncheie la terminarea prelucrrii

secvenelor b i c. -----------------------------------------------------------PROCEDURE Interclasare(p: integer; VAR k: integer); {p - dimensiune n-uplu, k - contor n-uple} Rewrite(a); Reset(b); Reset(c); k:= 0; [3.3.1.1.e] *iniializare interclasare *citeste n x respectiv n y primul element din b respectiv din c {tehnica lookahead} REPEAT *interclaseaza cte un n-uplu de pe b si c pe a UNTIL EndPrelucr_b AND EndPrelucr_c Close(a); Close(b); Close(c); ------------------------------------------------------------

Datorit particularitilor de implementare a fiierelor sunt necesare cteva precizri:

Variabila Eof(f) se poziioneaz pe true la citirea ultimului element al

fiierului f. Citirea dintr-un fiier cu Eof poziionat pe true conduce la eroare. Din punctul de vedere al algoritmului de interclasare, terminarea prelucrrii unui fiier nu coincide cu poziionarea lui Eof pe true, deoarece mai trebuie prelucrat ultimul element citit.

Pentru rezolvarea acestor constrngeri se utilizeaz tehnica scrutrii (lookahead). Tehnica scrutrii const n introducerea unei ntrzieri ntre momentul citirii
i momentul prelucrrii unui element. se citete un nou element.

Astfel n fiecare moment se prelucreaz elementul citit n pasul anterior i n acest scop pentru fiecare fiier implicat n prelucrare se utilizeaz o
variabil special de TipElement pentru memorarea elementului curent, n tandem cu o variabil boolean EndPrelucrare a crei valoare true semnific terminarea prelucrrii ultimului element al fiierului.

Rafinarea enunului interclaseaz cte un n-uplu de pe b i c pe


a apare n secvena [3.3.1.1.f ] care aplic tehnica anterior precizat.

Variabilele specifice asociate secvenelor b i c sunt x i y respectiv


EndPrelucr_b i EndPrelucr_c. -----------------------------------------------------------{interclaseaza cte un n-uplu de pe b si c pe a} i:= 0; {contor n-uplu b} j:= 0; {contor n-uplu c} WHILE (i<p)AND(j<p) AND NOT EndPrelucr_b AND NOT EndPrelucr_c DO BEGIN IF x.cheie<y.cheie THEN BEGIN *scrie(a,x); i:= i+1; *citeste(b,x) [3.3.1.1.f] END

ELSE BEGIN *scrie(a,y); j:= j+1; *citeste(c,y) END END; {WHILE} *copiaz restul n-uplului de pe b pe a (dac exist) *copiaz restul n-uplului de pe c pe a (dac exist) k:= k+1; ----------------------------------------------------------- O variant de implementare integral a procesului de sortare neechilibrat cu 3 benzi apare n PROCEDURA Interclasare3Secvente secvena [3.3.1.1.g]. -----------------------------------------------------------PROCEDURE Interclasare3Secvente; VAR a,b,c: TipSecventa; p,k: integer; PROCEDURE Injumatatire(p: Integer); VAR x: TipElement; PROCEDURE ScrieNuplu(VAR d: TipBanda); VAR i: integer; BEGIN {ScrieNuplu} i:= 0; WHILE (i<n) AND (NOT Eof(a)) DO BEGIN Read(a,x); Write(d,x); i:= i+1 END; {WHILE} END; {ScrieNuplu} [3.3.1.1.g] BEGIN {Injumatatire} Reset(a); Rewrite(b); Rewrite(c); WHILE NOT Eof(a) DO BEGIN ScrieNuplu(b); ScrieNuplu(c); END; {WHILE} Close(a); Close(b); Close(c); END; {Injumatatire} PROCEDURE Interclasare(p: integer; VAR k: integer); VAR i,j: integer; x,y: TipElement; EndPrelucr_b,EndPrelucr_c: Boolean; BEGIN {Interclasare} Reset(b); Reset(c); Rewrite(a); k:= 0; EndPrelucr_b:= Eof(b); EndPrelucr_c:= Eof(c); IF NOT EndPrelucr_b THEN Read(b,x); {lookahead} IF NOT EndPrelucr_c THEN Read(c,y); {lookahead} REPEAT i:= 0; j:= 0; {interclasarea unui n-uplu} WHILE (i<p)AND(j<p) AND NOT EndPrelucr_b AND NOT EndPrelucr_c DO BEGIN IF x.cheie < y.cheie THEN BEGIN Write(a,x); i:= i+1; IF Eof(b) THEN EndPrelucr_b:= true ELSE Read(b,x) END ELSE BEGIN

Write(a,y); j:= j+1; IF Eof(c) THEN EndPrelucr_c:= true ELSE Read(c,y) END; END; {WHILE} {copiaz restului n-uplului de pe b pe a} WHILE (i<n) AND NOT EndPrelucr_b DO BEGIN Write(a,x); i:= i+1; IF Eof(b) THEN EndPrelucr_b:= true ELSE Read(b,x) END; {WHILE} {copiaz restului n-uplului de pe c pe a} WHILE (j<n) AND NOT EndPrelucr_c DO BEGIN Write(a,y); j:= j+1; IF Eof(c) THEN EndPrelucr_c:= true ELSE Read(c,y) END; {WHILE} k:= k+1; UNTIL EndPrelucr_b AND EndPrelucr_c; Close(a); Close(b); Close(c); END; {Interclasare} BEGIN {Interclasare3Secvente} p:= 1; REPEAT Injumatatire(p); {faza (1)} Interclasare(p,k); {faza (2)} p:= p*2; UNTIL k=1; END; {Interclasare3Secvente} -----------------------------------------------------------3.3.1.2. Interclasarea echilibrat cu 4 secvene

Faza de njumtire care de fapt nu contribuie direct la sortare (n sensul c ea nu


permut nici un element), consum jumtate din operaiile de copiere.

Acest neajuns poate fi remediat prin combinarea fazei de njumtire cu cea de


interclasare. Astfel simultan cu interclasarea se realizeaz i redistribuirea n-uplelor interclasate pe dou secvene care vor constitui sursa trecerii urmtoare.

Acest proces se numete interclasare cu o singur faz sau interclasare echilibrat


cu 4 secvene (2 ci).

Fig. 3.3.1.2.a. Interclasare echilibrat cu patru secvene

ntr-o prim etap, se va prezenta un program de interclasare al unui tablou, care va fi


parcurs strict secvenial.

ntr-o etap ulterioar, interclasarea va fi aplicat unor structuri de tip fiier,

permind compararea celor dou abordri i n acelai timp demonstrnd puternica dependen a formei programului fa de structurile de date pe care le utilizeaz.

Un tablou poate fi utilizat n locul a dou fiiere, dac este privit ca o secven cu dou
capete.

Astfel n loc de a interclasa dou fiiere surs, elementele se vor lua de la cele

dou capete ale tabloului. Faza combinat njumtire-interclasare apare reprezentat schematic n figura 3.3.1.2.b. Destinaia articolelor interclasate este comutat dup fiecare pereche ordonat la prima trecere, dup fiecare quadruplu la a doua trecere, .a.m.d, astfel nct cele dou secvene destinaie, sunt de fapt cele dou capete ale unui singur tablou. Dup fiecare trecere cele dou tablouri se interschimb sursa devenind noua destinaie i reciproc.
SURS DESTINAIE

Interclasare

njumtire

Fig. 3.3.1.2.b. Model tablou pentru interclasarea echilibrat

n continuare lucrurile se pot simplifica reunind cele dou tablouri conceptuale ntrunul singur de lungime dubl: a: ARRAY[1..2*n] OF TipElement;

n acest tablou, indicii i i j precizeaz dou elemente surs, iar k i l dou


destinaii.

Secvena iniial va fi coninut de prima parte a tabloului a[1],...,a[n].

Se introduce o variabil boolean sus care va preciza sensul micrii elementelor; de

la a[1],...,a[n] spre a[n+1],...,a[2*n] cnd sus este adevrat i n sens invers cnd sus este fals. n mod evident valoarea lui sus alterneaz de la o trecere la alta.

Se mai introduce o variabil p care precizeaz lungimea subsecvenelor ce urmeaz s


fie interclasate: valoarea sa iniial 1, se dubleaz dup fiecare trecere.

Pentru a simplifica i mai mult lucrurile se presupune c n este o putere a lui 2. n aceste condiii, n secvena [3.3.1.2.a] apare varianta pseudocod a procedurii de
interclasare iar n secvena [3.3.1.2.b] primul pas de rafinare. -----------------------------------------------------------PROCEDURE Interclasare sus: boolean; p: integer; [3.3.1.2.a] sus:= true; p:= 1; repeta daca sus atunci stnga <- sursa; dreapta <- destinaie altfel dreapta <- sursa; stnga <- destinatie; *se interclaseaz secvenele de lungime p de la dou capete ale sursei, alternativ n cele dou capete ale destinaiei; sus:= not sus; p:= 2*p pn cnd p=n; -------------------------------------------------------PROCEDURE Interclasare; VAR i,j,k,l: indice; sus: boolean; p: integer; BEGIN sus:= true; p:= 1; REPEAT {initializare indici} IF sus THEN BEGIN i:= 1; j:= n; k:= n+1; l:= 2*n END ELSE [3.3.1.2.b] BEGIN k:= 1; l:= n; i:= n+1; j:= 2*n END; *interclaseaz p-tuplele secv. i-j n secv. k-l sus:= NOT sus; p:= 2*p UNTIL p=n END; {Interclasare} --------------------------------------------------------

Interclasarea este de fapt o bucl REPEAT pentru p mergnd de la 1 la n. La fiecare trecere p se dubleaz iar sus comut. n cadrul unei treceri: Funcie de variabila sus se asigneaz indicii surs-destinaie

Se interclaseaz p-tuplele secvenelor surs n p-tuple de dimensiune dubl i


se depun n secvena destinaie.

n pasul urmtor al detalierilor succesive se rafineaz, enunul "interclaseaz ptuplele secvenei i-j n secvena k-l".

Este clar c interclasarea a n elemente este la rndul ei o succesiune de interclasri


pariale ale unor subsecvene de lungime precizat p, n particular p-tuple.

Dup fiecare astfel de interclasare parial, destinaia este comutat de la un capt al


tabloului destinaie la cellalt, asigurnd astfel distribuia egal a subsecvenelor .

Dac destinaia elementului interclasat este captul inferior al tabloului destinaie, atunci
k este indicele destinaie i valoarea sa este incrementat cu 1 dup fiecare mutare.

Dac mutrile se execut la captul superior, atunci l este indicele destinaie i


valoarea sa va fi decrementat cu 1 dup fiecare mutare.

Pentru simplificare, destinaia va fi precizat tot timpul de indicele k, intercomutnd

valorile lui k i l dup interclasarea fiecrui p-tuplu i preciznd pentru incrementul permanent h valoarea 1 respectiv -1.

Rezult urmtoarea rafinare [3.3.1.2.c]:


-------------------------------------------------------{interclaseaz p-tuplele secvenei i-j n secvena k-l} h:= 1; m:= n; {m = numrul de elemente de interclasat} REPEAT q:= p; r:= p; m:= m-2*p; [3.3.1.2.c] *interclaseaz q elemente de la i cu r elemente de la j; indicele destinaie este k cu raia h h:= -h; *interschimb indicii destinaie (k i l) UNTIL m=0; -------------------------------------------------------

Referitor la secvena [3.3.1.2.c] se precizeaz urmtoarele:

S-au notat cu r respectiv q lungimile secvenelor care se interclaseaz. De

regul la nceputul intercalsrii r=q=p, ele modificndu-se eventul n zona final dac n nu este putere a lui 2. S-a notat cu m numrul de elemente care mai sunt de interclasat n cadrul unei treceri. Iniial m = n i el scade dup fiecare interclasare a dou subsecvene cu 2p . Procesul de intercalsare pentru o trecere se ncheie cnd m=0.

Pasul urmtor de detaliere rezolv interclasarea. n prealabil se precizeaz faptul c n


situaia n care procesul de interclasare nu epuizeaz o subsecven, restul rmas neprelucrat trebuie adugat secvenei destinaie printr-o simpl operaie de copiere [3.3.1.2.d]. -----------------------------------------------------------{interclaseaz q elemente de la i cu r elem. de la j} WHILE (q<>0) AND (r<>0) DO BEGIN {selecteaz un element de la i sau j} IF a[i].cheie<a[j].cheie THEN BEGIN

*mut un element de la i la k, avanseaz i i k q:= q-1 END ELSE [3.3.1.2.d] BEGIN *mut un element de la j la k, avanseaz j i k r:= r-1 END END; {WHILE} *copiaz restul secvenei i *copiaz restul secvenei j ------------------------------------------------------------

nainte de a se trece la redactarea propriu-zis a programului, se va elimina restricia


ca n s fie o putere a lui 2. n acest caz, se continu interclasarea p-tuplelor pn cnd secvenele surs care rmn au lungimea mai mic dect p. Se observ uor c singura parte de program care este influenat de aceast situaie este aceea n care se determin valorile lui q i r, care sunt lungimile secvenelor de interclasat din [3.3.1.2.c]. n consecin cele trei instrucii: q:= p; r:= p; m:= m-2*p;

Vor fi nlocuite cu:


IF m>=p THEN q:= p ELSE q:= m; m:= m-q; IF m>=p THEN r:= p ELSE r:= m; m:= m-r; unde m este numrul de elemente care au mai rmas de interclasat. n plus, condiia care controleaz terminarea programului p= n, trebuie modificat n p n.

Varianta final a procedurii Interclasare apare n [3.3.1.2.e]


-----------------------------------------------------------PROCEDURE Interclasare; VAR i,j,k,l,t: indice; h,m,p,q,r: integer; sus: boolean; BEGIN sus:= true; p:= 1; REPEAT {R1} h:= 1; m:= n; [3.3.1.2.e] IF sus THEN BEGIN i:= 1; j:= n; k:= n+1; l:= 2*n END ELSE BEGIN k:= 1; l:= n; i:= n+1; j:= 2*n END; {IF} REPEAT {interclaseaz de la i si j la k q=lungimea de la i; r=lungimea de la j} IF m>=p THEN q:= p ELSE q:= m; m:= m-q; IF m>=p THEN r:= p ELSE r:= m; m:= m-r; WHILE(q<>0) AND (r<>0) DO BEGIN {interclasarea} IF a[i].cheie<a[j].cheie THEN BEGIN a[k]:= a[i]; k:= k+h; i:= i+1; q:= q-1 END ELSE

BEGIN a[k]:= a[j]; k:= k+h; j:= j-1; r:= r-1 END {IF} END; {WHILE} {copiaz restul secvenei j} WHILE r<>0 DO BEGIN a[k]:= a[j]; k:= k+h; j:= j-1; r:= r-1 END; {copiaz restul secvenei i} WHILE q<>0 DO BEGIN a[k]:= a[j]; k:= k+h; i:= i+1; q:= q-1 END; h:= -h; t:= k; k:= l; l:= t {interschimb indicii destinaie (k si l)} UNTIL m=0; sus:= NOT sus; p:= 2*p UNTIL p>=n; {R1} IF NOT sus THEN FOR i:= 1 TO n DO a[i]:= a[i+n] END; {Interclasare} ------------------------------------------------------------

Analiza interclasrii. Deoarece la fiecare trecere p se dubleaz, ndeplinirea


condiiei p > n O( log 2 n). presupune log2 n

treceri, deci o eficien de ordinul

Fiecare pas, prin definiie, copiaz ntregul set de n elemente exact odat, prin urmare
numrul de micri M este cunoscut exact: M = n log2 n

Numrul de comparaii C este chiar mai mic dect M deoarece operaia de copiere a
resturilor nu presupune nici o comparaie.

Cu toate astea, ntruct tehnica interclasrii presupune utilizarea unor dispozitive

periferice, efortul de calcul necesar operaiilor de mutare poate domina efortul necesar operaiilor de comparare cu mai multe ordine de mrime, motiv pentru care analiza numrului de comparaii nu prezint interes. tehnici de sortare.

n aparen tehnica interclasrii obine performane la nivelul celor mai performante n realitate ns: Regia manipulrii indicilor este relativ ridicat, Necesarul dublu de zon de memorie este un dezavantaj decisiv. Din aceste motive aceast tehnic este rar utilizat n sortarea tablourilor. Msurtorile reale efectuate situeaz tehnica interclasrii la nivelul performanelor
metodei heapsort, deci sub performanele quicksortului.

3.3.2. Sortarea prin interclasare natural

Tehnica de sortare prin interclasare nu ia n considerare faptul c datele iniiale pot fi


parial sortate, subsecvenele avnd o lungime predeterminat (2k n trecerea k).

De fapt, oricare dou subsecvene ordonate de lungimi m i n pot fi interclasate ntr-o


singur subsecven ordonat de lungime m+n.

Tehnica de interclasare care n fiecare moment combin cele mai lungi secvene
ordonate posibile se numete sortare prin interclasare natural. clarificat pe baza urmtorului exemplu.

n cadrul acestei tehnici un rol central l joac noiunea de monotonie, care va fi Se consider urmtoarea secven de chei,
1 13 2 4 7 6 18 9 10 14 11 3 75

Se pun linii verticale la extremitile secvenei precum i ntre elementele aj

i aj+1, ori de cte ori aj > aj+1. n felul acesta secvena a fost defalcat n secvene pariale monotone. Acestea sunt de lungime maxim, adic nu pot fi prelungite fr a-i pierde proprietatea de a fi monotone.

n general fie a1,a2, ..., an o secven oarecare de numere ntregi. Se nelege


1) 2) 3) 4) 1 i j n ; ak ak+1 pentru orice i k <j; ai-1 > ai sau i = 1 ; aj > aj+1 sau j = n .

prin monotonie orice secven parial ai,...,aj care satisface urmtoarele condiii:

[3.3.2.a]

Aceast definiie include i monotoniile cu un singur element, deoarece n acest caz


i=j i proprietatea 2 este ndeplinit, neexistnd nici un k cuprins ntre i i j-1.

Sortarea natural interclaseaz monotonii. Sortarea se bazeaz pe urmtoarea proprietate: Dac se intercaleaz dou secvene a cte n monotonii fiecare, rezult o

secven cu exact n monotonii. Ca atare la fiecare trecere numrul acestora se njumtete i n cel mai ru caz, numrul necesar de micri este n*log2 n, n medie mai redus. Numrul de comparaii este ns mult mai mare deoarece pe lng comparaiile necesare interclasrii elementelor sunt necesare comparaii ntre elementele consecutive ale fiecrui fiier pentru a determina sfritul fiecrei monotonii.

n continuare n dezvoltarea programului aferent acestei tehnici va fi utilizat metoda


detalierilor succesive.

Se va utiliza o structur de date de tip fiier secvenial asupra creia se va aplica


sortarea prin interclasare neechilibrat n dou faze, utiliznd trei secvene.

Algoritmul lucreaz cu secvenele a, b i c. Secvena c este cea care trebuie

procesat i care n final devine secvena sortat. n practic, din motive de securitate, c este de fapt o copie a secvenei iniiale.

Se utilizeaz urmtoarele structuri de date [3.3.2.b].


-------------------------------------------------------TYPE TipSecventa = FILE OF TipElement; [3.3.2.b] VAR a,b,c: TipSecventa; --------------------------------------------------------

Secvenele a i b sunt auxiliare i ele servesc la defalcarea provizorie a lui c pe


monotonii.

Fiecare trecere const din dou faze alternative care se numesc defalcare respectiv

interclasare. n faza de defalcare monotoniile secvenei c se defalc alternativ pe secvenele a i b. n faza de interclasare se recombin n c, monotoniile de pe secvenele a i b (fig. 3.3.2).
a c c a c c a c

b faza defalcare faza interclasare

trecere 1

trecere 2

trecere n

Fig. 3.3.2. Treceri i faze n interclasarea natural

Sortarea se termin n momentul n care numrul monotoniilor secvenei c


devine egal cu 1. Pentru numrarea monotoniilor se utilizeaz variabila l.

Prima form a algoritmului apare n secvena [3.3.2.c]. Cele dou faze apar ca dou instrucii, care n continuare urmeaz s
fie rafinate. Procesul de rafinare poate fi realizat Fie prin substituia direct a celor dou instrucii cu secvenele care le corespund (tehnica inseriei), Fie prin interpretarea lor ca proceduri i procednd n consecin la dezvoltarea lor (tehnica seleciei). -----------------------------------------------------------PROCEDURE InterclasareNaturala; VAR l: integer; a,b,c: TipSecventa; sm: boolean; BEGIN [3.3.2.c] REPEAT Rewrite(a); Rewrite(b); Reset(c); Defalcare;

Reset(a); Reset(b); Rewrite(c); l:= 0; Interclasare; UNTIL l=1 END; {InterclasareNaturala} --------------------------------------------------------

n continuare se va continua procesul de rafinare prin tehnica seleciei. n secvenele [3.3.2.d] respectiv [3.3.2.e] apar primii pai de rafinare pentru
Defalcare respectiv Interclasare. -----------------------------------------------------------PROCEDURE Defalcare; {din c pe a si b} BEGIN REPEAT [3.3.2.d] CopiazaMonotonia(c,a); IF NOT Eof(c) THEN CopiazaMonotonia(c,b) UNTIL Eof(c) END; {Defalcare} ------------------------------------------------------------

Aceast metod de defalcare distribuie fie un numr egal de monotonii pe secvenele

a respectiv b, fie cu o monotonie mai mult pe secvena a, dup cum numrul de monotonii de pe secvena c este par respectiv impar. -----------------------------------------------------------PROCEDURE Interclasare; BEGIN {din a si b pe c} REPEAT InterclasareMonotonie; l:= l+1; UNTIL Eof(b); IF NOT Eof(a) THEN [3.3.2.e] BEGIN {monotonia nepereche} CopiazaMonotonia(a,c); l:= l+1 END END; {Interclasare}; ------------------------------------------------------------

Dup interclasarea monotoniilor perechi, monotonia nepereche (dac exist) trebuie


recopiat pe c.

Procedurile Defalcare i Interclasare sunt redactate n termenii unor proceduri


subordonate (InterclasareMonotonie, CopiazaMonotonia) care se refer la o singur monotonie i care vor fi rafinate n continuare n [3.3.2.f] respectiv [3.3.2.g].

Se introduce variabila boolean sm (sfrit monotonie) care specific dac s-a ajuns

sau nu la sfritul unei monotonii. La epuizarea uneia dintre monotonii restul celeilalte este copiat n secvena destinaie. -----------------------------------------------------------PROCEDURE CopiazaMonotonia( x,y: TipSecventa); {x - fiierul in care se delimiteaz monotonia y - fiierul in care se copiaz monotonia} BEGIN REPEAT [3.3.2.f] CopiazaElement(x,y) UNTIL sm END; {CopiazaMonotonia} ------------------------------------------------------------

PROCEDURE InterclasareMonotonie; BEGIN REPEAT IF a.elemCurent.cheie < b.elemCurent.cheie THEN BEGIN CopiazaElement(a,c); IF sm THEN CopiazaMonotonia(b,c) END [3.3.2.g] ELSE BEGIN CopiazaElement(b,c); IF sm THEN CopiazaMonotonia(a,c) END {ELSE} UNTIL sm END; {InterclasareMonotonie} ------------------------------------------------------------

Pentru redactarea procedurilor de mai sus se utilizeaz o procedur subordonat


CopiazaElement(x,y: TipSecventa), care transfer elementul curent al secvenei surs x n secvena destinaie y, poziionnd variabila sm funcie de atingerea sau nu a sfritului monotoniei. pasul curent a elementului pentru pasul urmtor, primul element fiind introdus n tamponul fiierului naintea demarrii procesului de defalcare respectiv de interclasare.

n acest scop se utilizeaz tehnica "lookahead" (scrutare n fa), bazat pe citirea n

Pentru acest scop se modific i structura de date aferent secvenei dup cum
urmeaz [3.3.2.h]. -----------------------------------------------------------TYPE TipSecventa = RECORD [3.3.2.h] secventa: FILE OF TipElement; elemCurent: TipElement; {tamponul fisierului} termPrelucr: boolean END; -----------------------------------------------------------Procedura CopiazaElement apare n secvena [3.3.2.i]. -----------------------------------------------------------PROCEDURE CopiazaElement(VAR x,y: TipSecventa); VAR aux: TipElement; BEGIN Write(y.secvena,x.elemCurent); IF Eof(x.secventa) THEN BEGIN sm:= true; x.termPrelucr:= true END [3.3.2.i] ELSE BEGIN aux:= x.elemCurent; Read(x.secventa,x.elemCurent); sm:= aux.cheie > x.elemCurent.cheie END; END; {CopiazaElement} ------------------------------------------------------------

Dup cum se observ: La momentul curent se scrie pe secvena destinaie y elementul


x.elemCurent citit n pasul anterior

Se citeste noul x.elemCurent n vederea determinrii sfritului de

monotonie sm sau a sfritului prelucrrii termPrelucr. n acest scop se utilizeaz variabila aux: TipElement.

Desigur unii dintre paii de rafinare precizai pot suferi anumite modificri, funcie de
natura secvenelor reale care se utilizeaz i de setul de operaii disponibile asupra acestora.

Din pcate, programul dezvoltat cu ajutorul acestei metode nu este corect n toate
cazurile. Spre exemplu, defalcarea secvenei c cu 10 monotonii: 13 5717 1911 5923 297 6131 375 6741 432 347 71 are drept consecin datorit distribuiei cheilor formarea a 5 monotonii pe secvena a i a unei singure monotonii pe secvena b, n loc de 5 cum era de ateptat. a: 13 5711 597 615 672 3 b: 17 19 23 29 31 37 41 43 47 71

Faza de interclasare conduce la o secven cu dou monotonii (n loc de 5)


c: 13 17 19 23 29 31 37 41 43 47 57 7111 59 deoarece n procesul de interclasare s-a ajuns la sfritul secvenei b i conform lui [3.3.2.d] se mai copiaz o singur monotonie din a. Dup trecerea urmtoare sortarea se ncheie, dar rezultatul este incorect: c:11 13 17 19 23 29 31 37 41 43 47 57 59 71

Acest lucru se ntmpl deoarece nu a fost luat n considerare faptul c dei procesul
de distribuire repartizeaz n mod egal monotoniile pe secvenele a respectiv b, numrul de monotonii pe cele dou secvene poate diferi foarte mult datorit distribuiei cheilor. modificat astfel nct, n momentul n care se ajunge la sfritul unei secvene, s copieze n c tot restul celeilalte secvene.

Pentru a remedia aceast situaie, este necesar ca procedura Interclasare s fie

Versiunea revizuit a algoritmului de sortare prin interclasare natural apare n


[3.3.2.j]. -----------------------------------------------------------PROCEDURE InterclasareNaturala; VAR l: integer; sm: boolean; a,b,c: TipSecventa; PROCEDURE CopiazaElement(VAR x,y: TipSecventa); VAR aux: TipElement; BEGIN Write(y.secventa,x.elemCurent); IF Eof(x.secventa) THEN BEGIN sm:= true; x.termPrelucr:= true END [3.3.2.j] ELSE BEGIN aux:= x.elemCurent; Read(x.secventa,x.elemCurent); sm:= aux.cheie > x.elemCurent.cheie

END; END; {CopiazaElement} PROCEDURE CopiazaMonotonia(VAR x,y: TipSecventa); BEGIN REPEAT CopiazaElement(x,y) UNTIL sm END; {CopiazaMonotonia} PROCEDURE Defalcare; BEGIN Rewrite(a.secventa);Rewrite(b.secventa); Reset(c.secventa); c.termPrelucr:= Eof(c.secventa); Read(c.secventa,c.elemCurent); REPEAT CopiazaMonotonia(c,a); IF NOT c.termPrelucr THEN CopiazaMonotonia(c,b) UNTIL c.termPrelucr; Close(a.secventa); Close(b.secventa); Close(c.secventa) END; {Defalcare} PROCEDURE InterclasareMonotonie; BEGIN REPEAT IF a.elemCurent.cheie < b.elemCurent.cheie THEN BEGIN CopiazaElement(a,c); IF sm THEN CopiazaMonotonia(b,c) END ELSE BEGIN CopiazaElement(b,c); IF sm THEN CopiazaMonotonia(a,c) END UNTIL sm [3.3.2.j] continuare END; {InterclasareMonotonie} PROCEDURE Interclasare; BEGIN Reset(a.secventa); Reset(b.secventa); Rewrite(c.secventa); a.termPrelucr:= Eof(a.secventa); b.termPrelucr:= Eof(b.secventa); IF NOT a.termPrelucr THEN Read(a.secventa,a.elemCurent); {primul element} IF NOT b.termPrelucr THEN Read(b.secventa,b.elemCurent); {primul element} WHILE NOT b.termPrelucr DO BEGIN InterclasareMonotonie; l:= l+1 END; {WHILE} IF NOT a.termPrelucr THEN BEGIN CopiazaMonotonia(a,c); l:= l+1 END; {IF} Close(a.secventa);Close(b.secventa); Close(c.secventa); END; {Interclasare BEGIN {InterclasareNaturala}

REPEAT Defalcare; l:= 0; Interclasare; UNTIL l=1; END; {InterclasareNaturala}; ------------------------------------------------------------

Analiza sortrii prin interclasare natural. Dup cum s-a mai precizat, la analiza unei metode de sortare extern, numrul
comparaiilor de chei nu are importan practic, deoarece durata prelucrrilor n unitatea central a sistemului de calcul este neglijabil fa de durata acceselor la memoriile externe. performan.

Din acest motiv numrul mutrilor M va fi considerat drept unic indicator de n cazul sortrii prin intercalsare natural: La o trecere, n fiecare din cele dou faze (defalcare i interclasare) se mut

toate elementele, deci M = 2*n . Dup fiecare trecere numrul monotoniilor se micoreaz de dou ori, uneori chiar mai substanial, motiv pentru care a fost necesar i modificarea anterioar a procedurii Interclasare. tiind c numrul maxim de monotonii iniiale este n, numrul maxim de treceri este log2 n , astfel nct n cel mai defavorabil caz numrul de micri M=2*n*log2 n , n medie simitor mai redus.

3.3.3. Sortarea prin interclasare multipl echilibrat

ntruct efortul de sortare este proporional cu numrul de treceri, o cale de reducere


a acestuia este aceea de a distribui monotoniile pe mai mult dect dou secvene.

n consecin: Interclasarea a r monotonii care sunt distribuite pe N secvene conduce la o


succesiune de r/N monotonii. Al doilea pas conduce la r/N 2, monotonii Al treilea la r/N 3, monotonii .a.m.d.

Aceast metod de sortare se numete interclasare multipl-N. Numrul total de treceri k, n cazul sortrii a n elemente prin interclasare multipl-N
este k = log
N

n , iar numrul total de micri M = n log

n .

O modalitate de implementare a acestei situaii o reprezint interclasarea multipl


echilibrat care se realizeaz ntr-o singur faz.

Interclasarea multipl echilibrat presupune c la fiecare trecere exist un numr

egal de secvene de intrare i de ieire, monotoniile de pe primele fiind interclasate i imediat redistribuite pe celelalte.

Dac se utilizeaz N secvene (N par), avem de fapt de-a face cu o interclasare


multipl echilibrat cu N/2 ci.

Schema de principiu a acestei metode apare n figura 3.3.3.a.

N/2 secvene surs

N/2 secvene destinaie

Fig. 3.3.3.a. Modelul interclasrii multiple echilibrate

Algoritmul care va fi dezvoltat n continuare se bazeaz pe o structur specific de


date: tabloul de secvene.

Fa de procedeul utilizat n paragraful anterior, care era de fapt o interclasare

multipl cu 2 ci, trecerea la mai multe ci presupune modificri eseniale: Procesul de interclasare trebuie s gestioneze o list a secvenelor active, din care va elimina pe rnd secvenele ale cror monotonii s-au epuizat. De asemenea trebuie implementat comutarea grupelor de secvene de intrare i ieire dup fiecare trecere.

Pentru aceasta se definesc structurile de date din secvena [3.3.3.a].


-----------------------------------------------------------TYPE TipSecventa = RECORD fisier: File of TipElement; curent: TipElement; termPrelucr: boolean [3.3.3.a] END; NrSecventa: 1..n; VAR f0: TipSecventa; F: ARRAY[NrSecventa] OF TipSecventa; t,td: ARRAY[NrSecventa] OF NrSecventa; ------------------------------------------------------------

Tipul NrSecventa este utilizat ca tip indice al tabloului de secvene F, tablou ale
crui elemente aparin lui TipSecventa.

Se presupune c secvena de elemente care urmeaz s fie sortat este furnizat ca o


variabil f0: TipSecventa i c pentru procesul de sortare se utilizeaz N secvene (N par).

Problema comutrii secvenelor este rezolvat introducnd tabloul t care are drept
componente indici care identific secvenele.

Astfel n loc de a adresa o secven din tabloul F direct prin indicele i, aceasta va fi
adresat via tabloul t, respectiv F[t[i]] n loc de F[i].

Iniial t[i]=i pentru toate valorile lui i, Comutarea const de fapt n interschimbarea perechilor de componente ale tabloului
dup cum urmeaz, unde NH = N/2. t[1] <-> t[NH+1] t[2] <-> t[NH+2] .... t[NH] <-> t[N]

n continuare secvenele F[t[1]],...,F[t[NH]]vor fi considerate ntotdeauna

secvene de intrare, iar secvenele F[t[NH+1]],..., F[t[N]]drept secvene de ieire (fig.3.3.3.b).


Tabela de fiiere F 1 2 3 B1 B2 B3

4 B4

5 B5

6 B6

Tabloul de indici t (nainte de comutare) 1 1 2 2 3 3 NH 1 4 2 5 3 6 4 4 NH+1 4 1 5 2 6 3 5 5 6 6

Tabloul de indici t (dup comutare)

NH Secvene intrare

NH+1 Secvene ieire

Fig. 3.3.3.b. Comutarea secvenelor n interclasarea multipl echilibrat

Forma iniial a algoritmului de sortare este cea din secvena [3.3.3.b].


-----------------------------------------------------------PROCEDURE SortareMultiplaEchilibrata; VAR i,j: NrSecventa; l: integer {numarul monotoniilor distribuite} t,td: ARRAY[NrSecventa] OF NrSecventa; F: ARRAY[NrSecventa] OF TipSecventa; BEGIN {NH=N/2} [3.3.3.b] j:= NH; l:= 0; {numar de monotonii} REPEAT {se distribuie monotoniile initiale de pe f0 pe t[1],...,t[NH]} IF j<NH THEN j:= j+1 ELSE j:= 1; *copiaza o monotonie de pe f0 pe secventa j l:= l+1 UNTIL SfarsitPrelucr(f0);

{initializare tablou de indici} REPEAT {interclaseaza de pe t[1],...,t[NH] pe t[NH+1],...,t[N]} *iniializare secvene de intrare l:= 0; {numr monotonii} j:= NH+1; {j este indicele secvenei de ieire} REPEAT l:= l+1; *interclaseaz cte o monotonie de pe fiecare intrare pe t[j] IF j<N THEN j:= j+1 ELSE j:= NH+1 UNTIL *toate intrrile au fost epuizate *comut secvenele UNTIL l=1 {secvena sortat este t[1]} END; {SortareMultiplaEchilibrata} --------------------------------------------------------

FOR i:= 1 TO n DO t[i]:= i;

Dup cum se observ, procesul de sortare multipl echilibrat const din trei pai. (1) Primul pas se refer la distribuia monotoniilor iniiale i este materializat de
prima bucl REPEAT. n acest pas: Monotoniile de pe secvena iniial f0 sunt distribuite succesiv pe secvenele surs care sunt indicate de j. Dup copierea fiecrei monotonii, indicele j care parcurge ciclic domeniul [1..NH] este incrementat. Distribuia se termin cnd se epuizeaz f0, iar numrul de monotonii distribuite se contorizeaz n l.

(2) Pasul al doilea realizeaz iniializarea tabloului t (bucla FOR), (3) Pasul al treilea realizeaz interclasarea secvenelor de intrare
F[t[1]]...F[t[NH]] n secvenele de ieire F[t[NH+1]]...F[t[N]].

Principiul interclasrii este urmtorul: Se iau toate intrrile active i se interclaseaz cte o monotonie de pe fiecare

ntr-o singur monotonie care se depune pe secvena j. Se avanseaz la secvena de ieire urmtoare (j parcurge ciclic domeniul [NH+1..N]) Se reia procedeul pn la epuizarea tuturor intrrilor (bucla REPEAT interioar). n acest moment se comut secvenele (intrrile devin ieiri i invers) i se reia interclasarea. Procesul continu pn cnd numrul de monotonii de interclasat ajunge egal cu 1 (bucla REPEAT exterioar).

n continuare se prezint rafinarea enunurilor implicate n algoritm. n secvena [3.3.3.c] apare rafinarea enunului *copiaz o monotonie de pe
f0 n secvena j, utilizat n distribuia iniial a monotoniilor. -----------------------------------------------------------{copiaz o monotonie de pe secvena f0 pe secvena j} {f0 nu este vid. Primul element este deja citit}

VAR buf: TipElement; REPEAT [3.3.3.c] buf:= f0.curent; IF Eof(f0.fisier) THEN f0.termPrelucr:= true {tehnica lookahead} ELSE Read(f0.fisier, f0.curent); Write(F[j].fisier,buf) UNTIL (buf.cheie>f0.curent.cheie) OR f0.termPrelucr; ------------------------------------------------------------

Urmeaz rafinarea enunului *iniializare secvene de intrare. Pentru nceput trebuiesc identificate secvenele curente de intrare deoarece numrul
celor active poate fi mai mic dect N.

De fapt, numrul secvenelor se reduce pe msur ce se reduce numrul monotoniilor. Practic nu pot exista mai multe secvene dect monotonii astfel nct sortarea se
termin cnd mai rmne o singur secven.

Se introduce variabila k1 pentru a preciza numrul actual de secvene de intrare


active.

Cu aceste precizri secvena de iniializare va fi [3.3.3.d]:


----------------------------------------------------------{iniializare secvene intrare} [3.3.3.d] FOR i:= 1 TO k1 DO Reset(F[t[i]]); ------------------------------------------------------------

Datorit procesului repetat de interclasare, tendina lui k1 este de a descrete odat


cu epuizarea monotoniilor, deci condiia *toate intrrile epuizate se poate exprima prin relaia k1 = 0 .

Enunul *interclaseaz

cte o monotonie de pe fiecare intrare pe t[j] este mai complicat. El const n selecia repetat a elementului cu cea mai mic cheie dintre sursele disponibile i trecerea lui pe secvena de ieire curent. De la fiecare secven surs se parcurge cte o monotonie al crei sfrit poate fi atins fie: (1) Prin epuizarea secvenei curente; (2) Prin citirea unei chei mai mici dect cea curent.

n cazul (1) secvena este eliminat i k1 decrementat. n cazul (2) secvena este exclus temporar de la selecia cheilor, pn cnd se
termin crearea monotoniei curente pe secvena de ieire, operaie numit nchidere monotonie.

n acest scop se utilizeaz un al doilea indice k2 care precizeaz numrul de

secvene surs disponibile curent pentru selecia cheii urmtoare. Valoarea sa iniial egal cu k1, se decrementeaz ori de cte ori se epuizeaz o monotonie din cauza condiiei (2).

Cnd k2 devine 0, s-a terminat interclasarea cte unei monotonii de la


fiecare intrare activ pe secvena de ieire curent.

Primul pas de rafinare al acestui enun apare n [3.3.3.d].


-----------------------------------------------------------{interclaseaz cte o monotonie de pe fiecare intrare pe F[t[j]]} FOR i:= 1 TO k1 DO Reset(F[t[i]].fisier); k2:= k1; REPEAT *selecteaza cheia minima. Fie t[mx] indicele secvenei care o conine buf:= F[t[mx]].curent; IF Eof(F[td[mx]].fisier) THEN [3.3.3.d] F[td[mx]].termPrelucr:= true ELSE Read(F[td[mx].fisier,F[td[mx].curent); Write(F[T[j]].fisier,buf); IF Eof(F[td[mx]].fisier) THEN *elimin secvena ELSE IF buf.cheie>F[t[mx]].curent.cheie THEN *nchide monotonia UNTIL k2=0; ------------------------------------------------------------

Din pcate, introducerea lui k2 nu este suficient, deoarece pe lng numrul


secvenelor disponibile trebuie s se tie exact care sunt acestea. booleene care indic disponibilitatea secvenelor.

O soluie n acest sens poate s o constituie utilizarea unui tablou cu componente O soluie mai eficient ns este utilizarea unui al doilea tablou de indici td. Acest tablou este utilizat n locul lui t pentru secvenele de intrare astfel nct

td[1],...,td[k2] sunt indicii secvenelor disponibile. Tabloul td se iniializeaz la nceputul fiecrei interclasri prin copierea indicilor secvenelor de intrare din tabloul t ( t[1],...,t[k1]). Indicele k1 se iniializeaz pe valoarea N/2 dac numrul de monotonii l este mai mare ca N/2 respectiv cu valoarea lui l n caz contrar (l reprezint numrul de monotonii interclasate n faza anterioar). Tabloul td apare reprezentat schematic n figura 3.3.3.c.
Tabloul td 1 2 3 N/2

mx

k2

k1

secvene de intrare

Fig. 3.3.3.c. Tabloul td utilizat ca auxiliar n procesul de interclasare

Indicele k1 precizeaz numrul de secvene active. Restul secvenelor, (pn la N/2) nu mai au monotonii fiind terminate fizic, motiv
pentru care acestea nici nu se mai consemneaz.

Indicele k2 care se iniializeaz cu valoarea lui k1, precizeaz numrul de secvene


care mai au monotonii n trecerea curent.

Restul secvenelor (pn la k1) i-au epuizat monotoniile n trecerea curent fr a fi


ns epuizate fizic.

n aceste condiii, enunul *nchide monotonia corespunztor condiiei (2), se


rafineaz dup cum urmeaz.

Fie mx indicele secvenei pentru care s-a terminat monotonia curent: se n ceea ce privete enunul *elimina secvena, corespunztor condiiei (1),
considernd ca tot mx este indicele secvenei care s-a epuizat, rafinarea presupune urmtoarele. secvena precizat de k1 n locul secvenei indicate de k2. interschimb n td poziia mx cu poziia k2 i se decrementeaz k2.

Se mut secvena precizat de k2 n locul secvenei precizate de mx i Se decrementeaz att k1 ct i k2. Forma final a sortrii multiple echilibrate apare n secvena [3.3.3.e].
-----------------------------------------------------------PROCEDURE InterclasareMultiplaEchilibrata; VAR i,j,mx,tx: NrSecventa; k1,k2,l: integer; x,min: integer; t,td: ARRAY[NrSecventa] OF NrSecventa; F: ARRAY[NrSecventa] OF TipSecventa; f0: TipSecventa; BEGIN FOR i:= 1 TO NH DO Rewrite(f[i]); {iniializare secvene} j:= NH; l:= 0; Read(f0.fisier,f0.curent); {primul element din fo} REPEAT {distribuirea monotoniilor iniiale pe t[1],...,t[NH]} IF j<NH THEN j:= j+1 ELSE j:= l; l:= l+1; REPEAT {copiaz o monotonie de pe secvena f0 pe secvena j} buf:= f0.curent; [3.3.3.e] IF Eof(f0.fisier) THEN f0.termPrelucr:= true {tehnica lookahead} ELSE Read(f0.fisier, f0.curent); Write(F[j].fisier,buf0) UNTIL (buf.cheie>f0.curent.cheie) OR f0.termPrelucr; UNTIL f0.termPrelucr; FOR i:= 1 TO N DO t[i]:= i; {iniializare tablou de indici} REPEAT {interclaseaz de pe t[1],...,t[NH] pe t[NH+1],...,t[N]} IF l<NH THEN k1:= l ELSE k1:= NH; FOR i:= 1 TO k1 DO{iniializare secvene intrare} BEGIN {k1 este numrul secvenelor de intrare}

Reset(F[t[i]]); td[i]:= t[i]; Read(F[td[i]].fisier,F[td[i]].curent) END ; l:= 0; {numrul de monotonii interclasate} j:= NH+1; {j=indexul secvenei de ieire} REPEAT {interclaseaz cte o monotonie de pe t[1],...t[k2] pe t[j]} k2:= k1; l:= l+1; {k2=nr de secvene active} REPEAT {selecteaz elementul cu cheia minim} i:= 1; mx:= 1; min:= F[td[1]].curent.cheie; WHILE i<k2 DO BEGIN i:= i+1; x:= F[td[i]].curent.cheie; IF x<min THEN BEGIN min:= x; mx:= i END END; {WHILE} {td[mx] conine elementul minim. Se trece pe t[j]} buf:= F[td[mx]].curent; IF Eof(F[td[mx]].fisier) THEN F[td[mx]].termPrelucr:= true ELSE Read(F[td[mx]].fisier,F[td[mx]].curent); Write(F[td[j]].fisier,buf) IF F[td[mx]].termPrelucr THEN BEGIN {elimin secvena} Rewrite(F[td[mx]]); td[mx]:= td[k2]; td[k2]:= td[k1]; k1:= k1-1; k2:= k2-1 END ELSE IF buf.cheie>F[td[mx]].curent.cheie THEN BEGIN {nchide monotonia} tx:= td[mx]; td[mx]:= td[k2]; td[k2]:= tx; k2:= k2-1 END UNTIL k2=0; {terminare interclasare cate o monotonie de la intrari pe t[j]] IF j<n THEN j:= j+1 ELSE j:= NH+1 {selecteaz urmtoarea secven destinaie} UNTIL k1=0; {toate intrrile au fost epuizate} FOR i:= 1 TO NH DO {comut secvenele} BEGIN tx:= t[i]; t[i]:= t[i+NH]; t[i+NH]:= tx END UNTIL l=1; {fiierul sortat se gsete pe t[1]} END; {InterclasareMultiplaEchilibrata} ----------------------------------------------------------3.3.4. Sortarea polifazic

Metoda interclasrii echilibrate contopete operaiile de distribuire i interclasare ntr-o


aceeai faz, utiliznd mai multe secvene de intrare i mai multe secvene de ieire, care nu sunt folosite n totalitate. n cadrul acestei metode nsi noiunea de trecere devine difuz.

R.L. Gilstad, a propus metoda sortrii polifazice care nltur acest neajuns. n plus

Pentru nceput se consider un exemplu cu trei secvene. n fiecare moment, elementele de pe dou secvene sunt interclasate pe cea dea treia. Ori de cte ori una din secvenele surs se epuizeaz, ea devine imediat destinaia operaiei de interclasare a celorlalte dou secvene (secvena neterminat i fosta destinaie).

Dup cum se cunoate, din procesul de interclasare a n monotonii de pe fiecare


secven de intrare rezult n monotonii pe secvena de ieire.

n cadrul acestei metode, pentru simplificare, se va vizualiza numai numrul de


monotonii de pe fiecare secven n locul cheilor propriu-zise. respectiv 8 monotonii.

Astfel, n fig. 3.3.4.a (a) se presupune c secvenele de intrare f1 i f2 conin 13 i La prima "trecere", sunt interclasate de pe f1 i f2 pe f3 8 monotonii, la a doua

"trecere" sunt interclasate de pe f1 i f3 pe f2 cele 5 monotonii rmase, etc. n final f1 este secvena sortat. n aceeai figur (b) se prezint un exemplu de sortare polifazic a 65 de monotonii pe 6 secvene.
f1 13 5 0 3 1 0 (b) 1 f2 8 0 5 2 0 1 8 3 0 2 1 (a) f3 f1 f2 f3 14 6 2 0 1 0 f4 12 4 0 2 1 0 f5 8 0 4 2 1 0 8 4 2 1 0 (b) f6

16 15 8 4 2 1 0 7 3 1 0 1

0(a) 0

Fig. 3.3.4.a. Exemple de sortare polifazic

Aceast tehnic este mai eficient dect interclasarea echilibrat deoarece, fiind date
N secvene, ea lucreaz la interclasare cu N-1 secvene n loc de N/2.

Astfel numrul de treceri este aproximativ logN n , unde n este numrul de elemente de
sortat iar N gradul operaiei de interclasare (numrul de secvene surs). grij.

n exemplele prezentate ns, distribuia iniial a monotoniilor a fost aleas cu mare Pentru a determina care dintre distribuiile iniiale ale monotoniilor asigur o

funcionare corespunztoare a algoritmului, se alctuiesc tabelele din figurile 3.3.4.b i 3.3.4.c.

Completarea celor dou tabele se realizeaz pornind de la cele dou exemple din fig.
3.3.4.a:

Fiecrei treceri i corespunznde un rnd n tablou. Trecerile se parcurg n ordine invers (adic de jos n sus). Fiecare rnd din tabel, cu excepia ultimului, conine pe prima poziie (a1),

numrul de monotonii ale secvenei destinaie din trecerea corespunztoare. n continuare se trec succesiv n tabel numerele de monotonii corespunztoare din cadrul trecerii. Fiecare trecere se parcurge de la stnga la dreapta (ncepnd cu secvena destinaie) i se consider circular. Ultimul rnd din tabel conine situaia iniial a distribuiei monotoniilor (cea dinaintea demarrii procesului de sortare).
k 0 1 2 3 4 5 6

a1

(k)

a2
0 1 1 2 3 5 8

(k)

a (k) i
1 2 3 5 8 13 21

1 1 2 3 5 8 13

Fig. 3.3.4.b. Distribuia perfect a monotoniilor pe trei secvene

Din tabloul din figura 3.3.4.b se pot deduce urmtoarele relaii [3.3.4.a]:
----------------------------------------------------------------+1) a (k = a (k) 2 1 (k +1) (k) (k) (k) (k -1) pentru k > 0 [3.3.4.a] a1 = a1 + a 2 = a1 + a1 (0) (0) unde a = 1 i a = 0 2 1 ----------------------------------------------------------------(1) prin nlocuire rezult [3.3.4.b]: Dac facem a (i) 1 = fi

----------------------------------------------------------------(1) (1) pentru i 1 f i(1) +1 = f i + f i1 (1) [3.3.4.b] f1 =1 (1) f0 =0 -----------------------------------------------------------------

Aceasta este ns regula recursiv care definete numerele lui Fibonacci de ordinul 1:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...

Fiecare numr din acest ir este suma celor doi predecesori ai si. n consecin, pentru cazul sortrii cu trei secvene, numrul iniial al monotoniile pe
cele dou secvene trebuie s fie dou numere Fibonacci consecutive pentru ca sortarea polifazic cu 3 secvene s funcioneze corect.
(k) (k) (k)

k 0

a1
1

a2
0

a3
0

a (k) a (k) 5 4
0 0

a (k) i
1

1 2 3 4 5

1 2 4 8 16

1 2 4 8 15

1 2 4 7 14

1 2 3 6 12

1 1 2 4 8

5 9 17 33 65

Fig. 3.3.4.c. Distribuia perfect a monotoniilor pe 6 secvene

Pentru cel de-al doilea exemplu se obin urmtoarele formule [3.3.4.c]:


-----------------------------------------------------------+1) a (k = a (k) 5 1 (k +1) (k) (k) (k) (k 1) a 4 = a1 + a 5 = a1 + a1

a3 a1

= a1 + a 4 = a1 + a1 + a1 (k +1) (k) (k) (k) (k 1) (k 2) (k 3) a 2 = a1 + a 3 = a1 + a1 + a1 + a1


(k +1)

(k +1)

(k)

(k)

(k)

(k 1)

(k 2)

[3.3.4.c]
(k 4)

= a1 + a 2 = a1 + a1

(k)

(k)

(k)

(k 1)

+ a1

(k 2)

+ a1

(k 3)

+ a1

-----------------------------------------------------------(4) Dac facem a(i) rezult [3.3.4.d] adic numerele Fibonacci de ordinul 4. 1 = fi ----------------------------------------------------------(4) (4) (4) (4) (4) (4) pentru i 4 fi+1 = fi + fi1 + fi2 + fi3 + fi4

unde pentru i < 4 [3.3.4.d] f4( 4 ) = 1 , fi ( 4 ) = 0 -----------------------------------------------------------n general, numerele Fibonacci de ordinul p sunt definite dup cum urmeaz [3.3.4.e]: -----------------------------------------------------------(p) (p) (p) (p) pentru i p f = f + f ++ f
(p) unde pentru 0 i < p [3.3.4.e] = 1 , fi = 0 ------------------------------------------------------------

i+1

i1

(p) fp

ip

Numerele Fibonacci de ordinul 1 sunt cele obinuite. n cazul sortrii polifazice cu n secvene, numerele iniiale de monotonii de pe cele
n -1 secvene surs sunt: O sum de n -1 numere Fibonacci consecutive de ordinul n -2 pe prima secven, O sum de n -2 numere Fibonacci consecutive de ordinul n -2 pe a doua secven, .a.m.d. Pe ultima secven (cea numerotat cu n -1) trebuie s existe iniial un numr de monotonii egal cu un numr Fibonacci de ordinul n -2. sortrii unei secvenei al crei numr iniial de monotonii este suma a n -1 de astfel de sume Fibonacci.

Aceasta implic faptul c metoda sortrii polifazice este aplicabil numai n cazul

O astfel de distribuie a monotoniilor iniiale se numete distribuie perfect. n cazul sortrii polifazice cu 6 secvene (n = 6), sunt necesare numere Fibonacci de
ordinul n -2 = 4: 0, 0, 0, 0, 1, 1, 2, 4, 8, 16, 31, 61, 120, ...

Distribuia iniial a monotoniilor, prezentat n fig. 3.3.4.a, se stabilete dup cum


urmeaz, pornind de la numrul Fibonacci 8 (cel de-al 9-lea din ir):

Secvena f1 va conine un numr de monotonii egal cu suma a n -1 = 5 numere Secvena f2 va conine un numr de monotonii egal cu suma a n -2 = 4 numere

Fibonacci consecutive: 1 + 1 + 2 + 4 + 8 = 16;

Fibonacci consecutive: 1 + 2 + 4 + 8 = 15; Secvena f3 , o sum de n -3 = 3 numere: 2 + 4 + 8 = 14; Secvena f4 , o sum de n -4 = 2 numere: 4 + 8 = 12; Secvena f5 , o sum de n -5 = 1 numere Fibonacci de ordinul 4, adic 8 monotonii.

n total secvena iniial care urmeaz s fie sortat trebuie s conin :


16 + 15 + 14 + 12 + 8 = 65 monotonii

Aceste monotonii se distribuie iniial pe cele 5 secvene surs n concordan cu


numerele determinate anterior realizndu-se astfel o distribuie perfect.

Se observ simplu c n fig. 3.3.4.c, distribuia monotoniilor pentru fiecare nivel k se

obine aplicnd acelai algoritm, alegnd ca baz, numere consecutive din irul Fibonacci 1, 1, 2, 4, 8, ... pentru respectiv k = 1, 2, 3, 4, 5, ... . n -1 sume Fibonacci, se simuleaz un numr de monotonii ipotetice vide, astfel nct suma s devin perfect. Aceste monotonii se numesc "fictive". Problema care se pune se refer la maniera n care aceste monotonii sunt recunoscute. cea a distribuiei monotoniilor fictive.

Dac numrul iniial de monotonii nu satisface condiia de a fi o astfel de sum de

Pentru nceput se va investiga problema distribuiei iniiale a monotoniilor, iar apoi Este evident faptul c selecia unei monotonii fictive de pe secvena i nseamn c
secvena respectiv este ignorat n timpul interclasrii curente, rezultnd o interclasare de pe mai puin de n -1 secvene surs.

Interclasarea unei monotonii fictive de pe toate cele n -1 secvene surs nu conduce

la nici o interclasare efectiv ci doar la nregistrarea unei monotonii fictive pe secvena de ieire. posibil pe cele n -1 secvene.

Din acest motiv, este necesar ca monotoniile fictive s fie distribuite ct mai uniform n primul rnd se va analiza problema repartizrii unui numr necunoscut de
monotonii pe n -1 secvene. Este clar c numerele Fibonacci de ordinul n -2 care reprezint numrul dorit de monotonii pot fi generate n mod progresiv.

Astfel, n cazul n = 6, avnd ca reper tabelul din fig. 3.3.4.c: Se pornete cu distribuia corespunztoare lui k = 1 (1,1,1,1,1); Dac exist mai multe monotonii se trece la rndul urmtor (2,2,2,2,1), Apoi la (4,4,4,3,2) .a.m.d. Indexul k al rndului se numete nivel. Pe msur ce crete numrul monotoniilor,
crete i nivelul numerelor Fibonacci, nivel care n acelai timp precizeaz i numrul de treceri sau comutri de secvene necesar pentru sortarea respectiv. 1) Fie scopul distribuiei, numerele lui Fibonacci de ordin n -2 nivelul l.

Algoritmul de distribuiie poate fi formulat astfel:

2) Se realizeaz distribuia monotoniilor conform acestui scop. 3) Dac scopul a fost atins, se calculeaz nivelul urmtor de numere Fibonacci; diferena dintre acestea i numerele corespunztoare ale nivelului anterior constituie noul scop al distribuiei. 4) Se revine la pasul 2. 5) Algoritmul se termin la epuizarea monotoniilor.

Regulile dup care se calculeaz urmtorul nivel al numerelor lui Fibonacci se

bazeaz pe definiia acestora [3.3.4.e], Atenia noastr se va concentra asupra pasului 2, n care n conformitate cu scopul curent se vor distribui monotoniile corespunztoare una dup alta pe cele n -1 secvene. n acest pas apar din nou monotoniile fictive.

Se presupune c la trecerea la nivelul urmtor, scopul urmtor va fi nregistrat prin

diferenele di pentru i = 1,2,...,n-1, unde di precizeaz numrul de monotonii care trebuie depus pe secvena i n acest pas. distribuirea monotoniilor poate fi privit ca i o renlocuire a monotoniilor fictive cu monotonii actuale, de fiecare dat nregistrnd o nlocuire prin scderea lui 1 din di. fictive de pe secvena i.

Se presupune n continuare c se pun iniial di monotonii fictive pe secvena i iar

Cnd sursa se epuizeaz, valoarea curent a lui di indic chiar numrul de monotonii Nu se cunoate algoritmul care conduce la distribuia optim, dar cel propus de Knuth,
numit distribuie orizontal este foarte bun [Kn76].

Acest termen poate fi neles considernd monotoniile cldite n forma unor silozuri.

n figura 3.3.4.d apar reprezentate aceste silozuri de monotonii pentru n = 6 , nivelul 5, conform fig. 3.3.4.c.

nceput secvene

34 35 38 42 46 51 56 61
f1

36 39 43 47 52 57 62
f2

37 40 44 48 53 58 63
f3

41 45 49 54 59 64
f4

50 55 60 65
f5

Fig.3.3.4.d. Distribuia orizontal a monotoniilor

Pentru a ajunge ct mai rapid la o distribuie egal a monotoniilor fictive, se

procedeaz la nlocuirea lor cu monotonii actuale. Acest proces reduce dimensiunea silozurilor prin extragerea monotoniilor fictive din nivelurile orizontale i nlocuirea lor cu monotonii reale, de la stnga la dreapta. n acest fel monotoniile sunt distribuite pe secvene dup cum indic numrul lor de ordine fig. 3.3.4.d.

Se precizeaz c n aceast figur este reprezentat ordinea de distribuie a

monotoniilor cnd se trece de la nivelul 4 (33 de monotonii) la nivelul 5 (65 de monotonii). Suprafeele haurate reprezint primele 33 de monotonii care au fost deja distribuite cnd s-a ajuns la nivelul 4. Dac spre exemplu ar fi numai 53 de monotonii iniiale, toate monotoniile numerotate cu 54 i mai mult vor fi tratate ca fictive. Monotoniile se nscriu n realitate la sfritul secvenei, dar este mai avantajos s ne imaginm c se scriu la nceput, deoarece n procesul sortrii se presupune c monotoniile fictive sunt la nceputul secvenei.

Pentru nceput se abordeaz descrierea procedurii SelecteazaSecventa care


este apelat de fiecare dat cnd trebuie copiat o nou monotonie.

Procedura realizeaz selecia noii secvene pe care se va copia urmtoarea monotonie


innd cont de distribuia perfect a monotoniilor pe secvenele surs.

Se presupune c variabila j este indexul secvenei curente de destinaie. Se utilizeaz


tipurile de date definite n [3.3.4.g]: -----------------------------------------------------------TYPE TipSecventa = RECORD fisier: FILE OF TipElement; curent: TipElement; termPrelucr: boolean [3.3.4.g] END; NrSecventa: 1..n; VAR j: NrSecventa; a,d: ARRAY[NrSecventa] OF integer; nivel: integer; -----------------------------------------------------------corespunztoare fiecrei secvene i . Aceste tablouri se iniializeaz cu valorile ai = 1, di = 1 i = 1,...,n -1 respectiv an = 0, dn = 0 . Variabilele j i nivel se iniializeaz cu valoarea 1.

Tablourile a i d memoreaz numerele de distribuii reale respectiv fictive


pentru

Procedura SelecteazaSecventa va calcula de fiecare dat cnd crete nivelul,

valorile rndului urmtor al tabelei din figura 3.3.4.c, respectiv valorile a1(k),...,an-1(k). Tot atunci se calculeaz i diferenele di = ai(k) - ai(k-1), care reprezint scopul urmtor. Algoritmul se bazeaz pe faptul c valorile di descresc odat cu creterea indicilor (vezi fig. 3.3.4.d). Se precizeaz c algoritmul ncepe cu nivelul 1 (nu cu nivelul 0). Procedura se termin cu decrementarea cu o unitate a lui dj ceea ce corespunde nlocuirii unei monotonii fictive cu una real pe secvena j [3.3.4.h].

-----------------------------------------------------------PROCEDURE SelecteazaSecventa; VAR i: NrSecventa; z: integer; BEGIN IF d[j]<d[j+1] THEN j:= j+1

ELSE BEGIN IF d[j]=0 THEN [3.3.4.h] BEGIN nivel:= nivel+1; z:= a[1]; FOR i:= 1 TO n-1 DO BEGIN d[i]:= z+a[i+1]-a[i] a[i]:= z+a[i+1] END END; j:= 1 END;{ELSE} d[j]:= d[j]-1 END; [SelecteazaSecventa] ------------------------------------------------------------

Presupunnd c se dispune de o rutin de copiere a unei monotonii de pe secvena

surs f0 pe secvena F[j], faza iniial de distribuire a monotoniilor poate fi schiat astfel [3.3.4.i]: -----------------------------------------------------------REPEAT SelecteazaSecventa; [3.3.4.i] CopiazaMonotonia UNTIL f0.termPrelucr; ------------------------------------------------------------

Spre deosebire de interclasarea natural, n cea polifazic este necesar s se cunoasc

numrul exact de monotonii de pe fiecare secven, deoarece procesul de interclasare poate conduce la diminuarea numrului de monotonii. secven. n acest scop se introduce variabila ultim: ARRAY[NrSecventa] OF TipCheie.

Pentru aceasta se va reine cheia ultimului element al ultimei monotonii de pe fiecare

Urmtoarea rafinare a algoritmului de distribuie este cea din [3.3.4.j]:


-----------------------------------------------------------REPEAT SelecteazaSecventa; [3.3.4.j] IF ultim[j]<= f0.curent.cheie THEN *continua vechea monotonie; CopiazaMonotonia; ultim[j]:= f0.curent.cheie UNTIL f0.termPrelucr; ------------------------------------------------------------

O problem evident este aceea c ultim[j] se poziioneaz numai dup copierea


primei monotonii, motiv pentru care la nceput distribuia monotoniilor trebuie realizat fr inspectarea lui ultim[j]. Restul monotoniilor se distribuie conform secvenei [3.3.4.k]. Se consider c asignarea lui ultim[j] se realizeaz n procedura CopiazaMonotonia. -----------------------------------------------------------WHILE NOT f0.termPrelucr DO BEGIN SelecteazaSecventa; IF ultim[j]<=f0.curent.cheie THEN BEGIN {continua vechea monotonie} CopiazaMonotonia; IF f0.termPrelucr THEN [3.4.4.k] d[j]:= d[j]+1 ELSE

CopiazaMonotonia END ELSE CopiazaMonotonia END; ------------------------------------------------------------

Structura algoritmului de sortare polifazic este n mare parte similar sortrii bazate

pe interclasare echilibrat cu n ci: O bucl exterioar care interclaseaz monotoniile i se execut pn cnd se epuizeaz sursele; O bucl cuprins n cea anterioar care interclaseaz o singur monotonie de pe fiecare secven surs; i n sfrit o a treia bucl cuprins n precedenta, care selecteaz cheile i transmite elementele implicate spre secvena destinaie.

Se remarc urmtoarele diferene: Avem o singur secven destinaie (n loc de N/2). n loc de a comuta N/2 secvene la fiecare trecere, acestea sunt rotite, utiliznd

un tablou t al indicilor secvenelor. Numrul de secvene de intrare variaz de la monotonie la monotonie; acest numr este determinat la nceputul sortrii fiecrei monotonii din valorile di ale monotoniilor fictive. Dac di > 0, pentru toi i, atunci vor fi "pseudo-interclasate" n -1 monotonii fictive ntr-o monotonie fictiv, ceea ce presupune doar incrementarea contorului dn al secvenei destinaie. Altfel, se interclaseaz cte o monotonie de pe toate secvenele care au di = 0, iar pentru restul secvenelor, di se decrementeaz indicnd faptul c a fost luat n considerare o monotonie fictiv. Se noteaz cu k1 numrul secvenelor implicate ntr-o interclasare.

Din cauza monotoniilor fictive, terminarea unei faze nu poate fi dedus din
condiia de sfrit a vreuneia din secvenele implicate, deoarece existena unor monotonii fictive pe aceast secven face necesar continuarea interclasrii. coeficienii ai.

n schimb se poate determina uor numrul teoretic de monotonii din Aceti coeficieni a i(k) care au fost calculai n timpul fazei de distribuie vor fi
recalculai n sens invers (backward) la interclasare.

Cu aceste observaii interclasarea propriu-zis se poate formula dup cum


urmeaz [3.3.4.l]. Se presupune c:

Toate cele n -1 secvene care conin monotoniile iniiale sunt resetate Tabloul indicilor secvenelor este poziionat conform relaiei
t[i] = i -----------------------------------------------------------REPEAT {interclasare de pe F[t[1]],...,F[t[n-1]] pe F[t[n]]} z:= a[n-1]; d[n]:= 0; Rewrite(F[t[n]]); REPEAT {interclasarea unei monotonii} k1:= 0 {se determ. nr k1 al secventelor de intrare active} FOR t:= 1 TO n-1 DO IF d[i]>0 THEN d[i]:= d[i]-1

ELSE BEGIN k1:= k1+1; td[k1]:= t[i] [3.3.4.l] END; IF k1=0 THEN d[n]:= d[n]+1 ELSE *se interclaseaza cte o monotonie de pe F[t[1]],...,F[t[k1]]; z:= z-1 UNTIL z=0; Reset(F[t[n]]); *roteste secventele n tabloul t. Calculeaza a[i] pentru nivelul urmator; Rewrite(F[t[n]]); nivel:= nivel-1 UNTIL nivel=0; {elementele sortate sunt pe F[t[1]]} ------------------------------------------------------------

Programul de sortare polifazic este similar cu cel de sortare prin interclasare cu n


ci, cu diferena c algoritmul de eliminare al secvenelor este mai simplu.

Rotirea indicilor secvenelor n tabloul indicilor, a indicilor di corespunztori, precum i


calcularea valorilor coeficienilor ai , sunt rafinate n programul urmtor [3.3.4.m]. -----------------------------------------------------------PROGRAM SortarePolifazica; {sortare polifazica cu n secvene} CONST n = 6; {numar secvene} [3.3.4.m] TYPE TipElement = RECORD cheie: integer END; TipSecvena = RECORD fisier: FILE OF TipElement; curent: TipElement; termPrelucr: boolean END; NrSecventa = 1..n; VAR dim,aleat,tmp: integer; {utilizate la generarea fiierului} eob: boolean; {sfrit de secven} buf: TipElement; f0: TipSecventa; {secvena de intrare coninnd numere aleatoare} F: ARRAY[NrSecventa] OF TipSecventa; PROCEDURE List(VAR f: TipSecvena; n: NrSecventa); VAR z: integer; BEGIN WriteLn(' secventa ',n); z:= 0; WHILE NOT Eof(f.fisier) DO BEGIN Read(f.fisier,buf); Write(buf.cheie); z:= z+1 END; WriteLn; Reset(f.fisier) END; {List}

PROCEDURE SortarePolifazica; VAR i,j,mx,tn: NrSecventa; k1,nivel: integer; a,d: ARRAY[NrSecventa] OF integer; {a[j] - nr-ul ideal de monotonii pe secvena j} {d[j] - nr-ul de monotonii fictive pe secv. j} dn,x,min,z: integer; ultim: ARRAY[NrSecventa] OF integer; {ultim[j]=cheia ultimului element al secvenei j} t,td: ARRAY[NrSecventa] OF NrSecventa; {tablouri de mapping pentru numerele secvenelor} PROCEDURE SelecteazaSecventa; VAR i: NrSecventa; z: integer; BEGIN IF d[j]<d[j+1] THEN j:= j+1 ELSE BEGIN IF d[j]=0 THEN BEGIN nivel:= nivel+1; z:= a[1]; FOR i:= 1 TO n-1 DO BEGIN d[i]:= z+a[i+1]-a[i]; a[i]:= z+a[i+1] END END; j:= 1 END; d[j]:= d[j]-1 END; {SelecteazaSecventa} PROCEDURE CopiazaMonotonia; VAR buf: TipElement; BEGIN {copiaz o monotonie de pe f0 pe secvena j} REPEAT buf:= f0.curent; IF Eof(fo.fisier) THEN f0.termPrelucr:= true ELSE Read(f0.fisier, f0.curent); Write(F[t[j]].fisier,buf) UNTIL (buf.cheie>f0.curent.cheie) OR f0.termPrelucr); ultim[j]:= buf.cheie END; {CopiazaMonotonia} BEGIN {distribuire monotonii iniiale} FOR i:= 1 TO n-1 DO BEGIN a[i]:= 1; d[i]:= 1; Rewrite(F[i].fisier) END; nivel:= 1; j:= 1; a[n]:= 0; d[n]:= 0; REPEAT SelecteazaSecventa; CopiazaMonotonia UNTIL f0.termPrelucr OR (j=n-1); WHILE NOT f0.termPrelucr DO BEGIN

SelecteazaSecventa; IF ultim[j]<=f0.curent.cheie THEN BEGIN {continu vechea monotonie} CopiazaMonotonia; IF f0.termPrelucr THEN d[j]:= d[j]+1 ELSE CopiazaMonotonia END ELSE CopiazaMonotonia END; FOR i:= 1 TO n-1 DO Reset(F[i]); FOR i:= 1 TO n DO t[i]:= i; REPEAT {interclasare de pe F[t[1]],...,F[t[n-1]] pe F[t[n]]} z:= a[n-1]; d[n]:= 0; Rewrite(F[t[n]]); REPEAT {interclasarea unei monotonii} k1:= 0; FOR i:= 1 TO n-1 DO IF d[i]>0 THEN d[i]:= d[i]-1 ELSE BEGIN k1:= k1+1; td[k1]:= t[i] END; IF k1=0 THEN d[n]:= d[n]+1 ELSE BEGIN {se interclaseaz o monotonie de pe F[t[1]],...,F[t[k1]] pe F[t[n]]} REPEAT i:= 1; mx:= 1; min:= F[td[1]].curent.cheie; WHILE i<k1 DO BEGIN i:= i+1; x:= F[td[i]].curent.cheie; IF x<min THEN BEGIN min:= x; mx:= i END END; {td[mx] conine elementul minim; se mut pe t[n]} buf:= F[td[mx]].curent; IF Eof(F[td[mx]].fisier) THEN F[td[mx]].termPrelucr:= true ELSE Read(F[td[mx].fisier, F[td[mx].curent); Write(F[t[n]].fisier,buf); IF (buf.cheie>F[td[mx]].curent.cheie) OR F[t[mx]].termPrelucr THEN BEGIN {omite aceasta secventa} td[mx]:= td[k1]; k1:= k-1 END UNTIL k=0

END; z:= z-1 UNTIL z=0; Reset(f[t[n]]); List(F[t[n]],t[n]); {rotire secvente} tn:= t[n]; dn:= d[n]; z:= a[n-1]; FOR i:= n DOWNTO 2 DO BEGIN t[i]:=t [i-1]; d[i]:= d[i-1); a[i]:= a[i-1]-z END; t[1]:= tn; d[1]:= dn; a[1]:= z; {elementele sortate sunt pe t[1]} List(f[t[1]],t[1]); nivel:= nivel-1 UNTIL nivel=0; END; {SortarePolifazica} BEGIN {generarea unui fiier aleator} dim:= 200; aleat:= 7789; REPEAT aleat:= (131071*aleat) MOD 2147483647; tmp:= aleat DIV 214784; Write(f0.fisier,tmp); dim:= dim-1 UNTIL dim=0; SortarePolifazica END. -----------------------------------------------------------3.3.5. Concluzii

Complexitatea metodelor de sortare extern prezentate nu permite formularea unor

concluzii generalizatoare, cu att mai mult cu ct evidenierea performanelor acestora este dificil. Se pot formula ns urmtoarele observaii: Exist o legtur indisolubil ntre un anumit algoritm care rezolv o anumit problem i structurile de date pe care acesta le utilizeaz, influena celor din urm fiind uneori decisiv pentru algoritm. Acest lucru este evideniat cu preponderen n cazul sortrilor externe care sunt complet diferite ca mod de abordare n raport cu metodele de sortare intern. n general, mbuntirea performanelor unui algoritm presupune elemente foarte sofisticate, chiar n condiiile utilizrii unor structuri de date dedicate. n mod paradoxal algoritmii cu performane superioare sunt mai complicai, ocup mai mult memorie i utilizeaz de regul structuri specializate.

4. iruri de caractere
Marea majoritate a celor preocupai de activitatea de programare sunt familiarizai cu
irurile de caractere ntruct aproape toate limbajele de programare includ irul sau caracterul printre elementele predefinite ale limbajului.

n prima parte a capitolului se va preciza tipul de date abstract ir. Apoi vor fi abordate modalitile majore de implementare, prin intermediul
tablourilor respectiv al pointerilor.

n ultima parte a capitolului vor fi precizate cteva din tehnicile de cutare n iruri. 4.1. Tipul de date abstract ir Un ir este o colecie de caractere, spre exemplu Structuri de date". n toate limbajele de programare n care sunt definite iruri, acestea au la baz tipul
primitiv char, care n afara literelor i cifrelor cuprinde i o serie de alte caractere. "CAL" i "LAC" dei conin aceleai caractere sunt diferite.

Se subliniaz faptul c ntr-un ir, ordinea caracterelor conteaz. Astfel irurile De asemenea, printr-un uor abuz de notaie se consider c un caracter este
interschimbabil cu un ir constnd dintr-un singur caracter, dei strict vorbind ele sunt de tipuri diferite.

Asemeni oricrui tip de date abstracte, definirea precis a TDA ir necesit: Descrierea modelului su matematic, Precizarea operatorilor care acioneaz asupra elementelor tipului. Din punct de vedere matematic, elementele tipului de date abstract ir pot fi definite
ca secvene finite de caractere (c1,c2,...,cn) unde ci este de tip caracter, iar n precizeaz lungimea secvenei.

Cazul n care n este egal cu zero, desemneaz irul vid. n continuare se prezint un posibil set de operatori care acioneaz asupra TDA ir. Ca i n cazul altor structuri de date, exist practic o libertate deplin n selectarea
acestor operatori motiv pentru care setul prezentat are un caracter orientativ. -----------------------------------------------------------TDA ir

Modelul matematic:secven finit de caractere. Notaii: s,sub,u - siruri; c - valoare de tip caracter; b - valoare boolean; poz,lung - ntregi pozitivi.

[4.1.a]

Operatori: CreazaSirVid(s) - procedur ce creeaz irul vid s; b:=SirVid(s) - funcie ce returneaz true dac irul este vid; b:=SirComplet(s) - funcie boolean ce returneaz valoarea true dac irul este complet; lung:=LungSir(s) - funcie care returneaz numrul de caractere ale lui s; poz:=PozitieSubsir(sub,s) - funcie care returneaz poziia la care subirul sub apare prima dat n s. Dac sub nu e gsit n s, se returneaz valoarea 0. Poziiile caracterelor sunt numerotate de la stnga la dreapta ncepnd cu 1; ConcatSir(u,s) - procedur care concateneaz la sfritul lui u attea caractere din s, pn cnd SirComplet(u) devine true; CopiazaSubsir(u,s,poz,lung) - procedur care-l face pe u copia subirului din s ncepnd cu poziia poz, pe lungime lung sau pn la sfritul lui s; dac poz>LungSir(s) sau poz<1, u devine irul vid; StergeSir(s,poz,lung) - procedur care terge din s, ncepnd cu poziia poz, subirul de lung caractere. Dac poz este invalid (nu aparine irului), s rmne nemodificat; InsereazaSir(sub,s,poz) - procedur care insereaz n s, ncepnd de la poziia poz, irul sub; c:=FurnizeazaCarSir(s,poz) - funcie care returneaz caracterul din poziia poz a lui s. Pentru poz invalid, se returneaz caracterul nul; AdaugaCarSir(s,c) - procedur care adaug caracterul c la sfritul irului s; StergeSubsir(sub,s,poz) - procedur care terge prima apariie a subirului sub n irul s i returneaz poziia poz de tergere. Dac sub nu este gsit, s rmne nemodificat iar poz este poziionat pe 0; StergeToateSubsir(s,sub) - terge toate apariiile lui sub n s. ------------------------------------------------------------

Operatorii definii pentru un TDA-ir pot fi mprii n dou categorii: Operatori primitivi care reprezint un set minimal de operaii strict necesare
n termenii crora pot fi dezvoltai operatorii nonprimitivi. Operatori nonprimitivi care pot fi dezvoltai din cei anteriori.

Aceast divizare este oarecum formal deoarece, de obicei e mai uor s defineti un

operator neprimitiv direct dect s-l defineti n termenii primitivelor. Spre exemplu operatorul Insereazir poate fi definit n termenii operatorilor CreazirVid i AdaugCar. Algoritmul este simplu: se construiete un ir de ieire temporar (rezultat) cruia i se adaug pe rnd: (1) caracterele irului surs pn la punctul de inserie, (2) toate caracterele irului de inserat (subir), urmate de (3) toate caracterele irului surs de dup punctul de inserie. irul astfel construit nlocuiete irul iniial (sursa) (secvena [4.1.b]). -----------------------------------------------------------PROCEDURE InsereazaSir(subsir: TipSir; VAR sursa: TipSir; p: TipIndice); {insereaz subir n surs ntre poziiile p i p+1} VAR rezultat: TipSir; i: TipIndice; BEGIN IF (p<1) OR (p>LungSir(sursa)) THEN *eroare (poziie ilegal in inserie) ELSE BEGIN [4.1.b] CreazaSirVid(rezultat); FOR i:= 1 TO p-1 DO AdaugaCarSir(rezultat, FurnizeazaCarSir(sursa,i)); FOR i:=1 TO LungSir(subsir) DO AdaugaCarSir(rezultat, FurnizeazaCarSir(subsir,i)); FOR i:=p TO LungSir(sursa) DO AdaugaCarSir(rezultat, FurnizeazaCarSir(sursa,i)); CreazaSirVid(sursa); FOR i:=1 TO LungSir(rezultat) DO AdaugaCarSir(sursa, FurnizeazaCarSir(rezultat,i)); END {ELSE} END;{InsereazaSir} ------------------------------------------------------------

4.2. Implementarea tipului de date abstract ir Sunt cunoscute dou tehnici majore de implementare a irurilor: Implementarea bazat pe tablouri Implementarea bazat pe pointeri.
4.2.1. Implementarea irurilor cu ajutorul tablourilor Cea mai simpl implementare a TDA-ir se bazeaz pe dou elemente: o (1) Un ntreg reprezentnd lungimea irului o (2) Un tablou de caractere care conine irul propriu-zis. n tablou caracterele pot fi pstrate ca atare sau ntr-o form mpachetat.

Un exemplu de astfel de implementare Pascal este urmtorul [4.2.1.a]. -------------------------------------------------------CONST LungimeMax = ...;

TYPE TipLungime = 0..LungimeMax; TipIndice = 1..LungimeMax; [4.2.1.a] TipSir = RECORD lungime: TipLungime; sir: ARRAY[TipIndice] OF char END; VAR s: TipSir; --------------------------------------------------------

Acest mod de implementare al irurilor nu este unic. Se utilizeaz tablouri ntruct tablourile ca i irurile sunt structuri liniare. Cmpul lungime este utilizat deoarece irurile au lungimi diferite n schimb ce
tablourile au lungimea fix.

Implementarea se poate dispersa de cmpul lungime , caz n care se poate utiliza un

caracter convenit pe post de santinel de sfrit (marker). n aceast situaie operatorul Lungimeir va trebui s contorizeze ntr-o manier liniar caracterele pn la detectarea markerului. Din acest motiv este preferabil ca lungimea s fie considerat un element separat al implementrii. complet ca fiind irul care are lungimea egal cu LungimeMaxim, adic dimensiunea tabloului definit spre a-l implementa. irurile implementate n acest mod nu pot depi aceast lungime, motiv pentru care n acest context opereaz operatorul boolean irComplet.

n contextul implementrii irurilor cu ajutorul tablourilor se definete noiunea de ir

n accepiunea modelului anterior prezentat, n continuare se prezint o implementare


a operatorilor primitivi [4.2.1.b,c,d,e]. -----------------------------------------------------------PROCEDURE CreazaSirVid(VAR s: TipSir); {O(1)} BEGIN s.lungime:= 0 [4.2.1.b] END;{CreazaSirVid} -----------------------------------------------------------FUNCTION LungSir(VAR s: TipSir): TipLungime; {O(1)} BEGIN LungSir:= s.lungime [4.2.1.c] END; {LungSir} -----------------------------------------------------------FUNCTION FurnizeazaCar(s: TipSir,poz: TipIndice): char; BEGIN {O(1)} IF (poz<1) OR (poz>s.lungime) THEN BEGIN [4.2.1.d] *eroare(poziie incorect); FurnizeazaCar:= chr(0) {caracterul vid} END ELSE FurnizeazaCar:= s.sir[poz] END; {FurnizeazaCar}

-----------------------------------------------------------PROCEDURE AdaugaCarSir(VAR s: TipSir; c: char); {O(1)} BEGIN IF s.lungime=LungimeMax THEN *eroare(se depete lungimea maxim a irului) ELSE BEGIN s.lungime:= s.lungime+1; [4.2.1.e] s.sir[s.lungime]:= c END END; {AdaugaCarSir} ------------------------------------------------------------

Se observ c toate aceste operaii ruleaz n O(1) uniti de timp indiferent de


lungimea irului.

Procedurile CopiazSubir, Concatir, tergeir i Insereazair se


execut ntr-un interval de timp liniar O(n), unde n este dup caz lungimea subirului sau a irului de caractere. Spre exemplu procedura CopiazSubir (u,s,poz,lung) returneaz n u subirul avnd lung caractere ncepnd cu poziia poz . Accesul la elementele subirului se realizeaz direct (s.ir[poz], s.ir[poz+1],...,s.ir[poz+lung-1]), astfel nct consumul de timp al execuiei este dominat de mutarea caracterelor [4.2.1.f]. -----------------------------------------------------------PROCEDURE CopiazaSubsir(VAR u,s: TipSir, poz, lung: TipLungime); {O(n)} {copiaz irul s n irul u} VAR indexSursa,indexCopiere: TipLungime; BEGIN IF (poz<1) OR (poz>s.lungime) THEN u.lungime:= 0 ELSE BEGIN [4.2.1.f] indexSursa:= poz-1; indexCopiere:= 0; WHILE (indexSursa<s.lungime) AND (indexCopiere<u.lungime) DO BEGIN indexSursa:= indexSursa+1; indexCopiere:= indexCopiere+1; u.sir[indexCopiere]:= s.sir[indexSursa] END; {WHILE} u.lungime:= indexCopiere END {ELSE} END; {CopiazaSubsir} ------------------------------------------------------------

Se observ faptul c n interiorul buclei WHILE exist 3 atribuiri, care pentru Se observ de asemenea c procedura CopiazSubir este liniar n raport
cu lungimea lung a subirului. o lungime lung a sub irului determin 3lung + 3 atribuiri.

Un alt exemplu l reprezint implementarea funciei PoziieSubir [4.2.1.g].


------------------------------------------------------------

FUNCTION PozitieSubsir(VAR sub,s: tipSir): TipLungime; {determin poziia lui sub n cadrul irului s (prima apariie)} VAR mark, {index pentru punctul de start al unei comparaii} indexSub, {index subir} indexSir: TipIndex; {index ir} poz: TipLungime; {poziia lui sub n s} stop: boolean; {devine adevrat cnd elementele corespunztoare din s i sub nu sunt egale} BEGIN indexSir:= 1; poz:= 0; REPEAT indexSub:= 1; mark:= indexSir; stop:= false; WHILE (NOT stop) AND (indexSir<=s.lungime) AND (indexSub<=sub.lungime) DO IF s.sir[indexSir]=sub.sir[indexSub] THEN BEGIN indexSir:= indexSir+1; indexSub:= indexSub+1 [4.2.1.g] END ELSE stop:= true; {WHILE} IF indexSub>sub.lungime THEN poz:= mark {potrivire} ELSE indexSir:= mark+1 UNTIL (poz>0) OR (indexSir+sub.lungime-1>s.lungime); PozitieSubsir:= poz END; {PozitieSubsir} ------------------------------------------------------------

Complexitatea funciei PoziieSubir(sub,s)este O(lungime s.lungime)

unde lungime este lungimea modelului iar s.lungime este lungimea irului n care se face cutarea. paragraf ulterior.

Exist ns i alte metode mult mai performante de cutare care fac obiectul unui Unul din marele avantaje ale utilizrii datelor abstracte este urmtorul: n situaia n care se gsete un algoritm mai performant pentru o anumit

operaie, se poate foarte simplu nlocui o implementare cu alta fr a afecta restul programului n condiiile pstrrii nealterate a prototipului operatorului.

4.2.2. Tabele de iruri

Utilizarea tabloului n implementarea irurilor are avantajul c operatorii sunt uor de


redactat, simplu de neles i suficient de rapizi.

Cu toate c acest mod de implementare este economic din punct de vedere al


timpului de execuie, el este ineficient din punctul de vedere al spaiului utilizat.

Lungimea maxim a tabloului trebuie aleas la dimensiunea celui mai lung ir


preconizat a fi utilizat dei n cea mai mare parte a cazurilor se utilizeaz iruri mult mai scurte.

O modalitate de a economisi spaiul n dauna vitezei de lucru specific implementrii


cu ajutorul tablourilor, este aceea de a utiliza: (1) Un tablou suficient de lung numit "heap" (grmad) pentru a memora toate irurile (2) O tabel auxiliar de iruri care pstreaz evidena irurilor n heap.

Aceasta tehnic este utilizat n unele interpretere BASIC. n acest mod de implementare a irurilor, un ir este reprezentat printr-o intrare n
tabela de iruri care conine dou valori (fig.4.2.2.a): Lungimea irului Un indicator n heap care precizeaz nceputul irului

1 S

2 T

3 R

10 D

12 D

16 .

lungime indicator 1 2 3 4 9 1 2 10 4 12 3 Tabela de iruri NumrDeiruri 16 Disponibil

Fig. 4.2.2.a. Modelul tabelei de iruri

Un exemplu de implementare n limbajul PASCAL apare n secvena [4.2.2.a]. Se observ c aceast implementare presupune: Variabilele globale TabelaDeiruri,
Heap, Disponibil i NumrDeiruri Structurarea n forma unui articol cu cmpurile lungime i indicator a elementelor tabelei de iruri.

-----------------------------------------------------------CONST LungimeHeap = {numr maxim de caractere}; LungimeTabela = {numr maxim de iruri}; TYPE ElementTabela = RECORD lungime, indicator: 1..LungimeHeap [4.2.2.a] END; TipSir = 1..LungimeTabela;

VAR TabelaDeSiruri: ARRAY[1..LungimeTabela] OF ElementTabela; Heap: ARRAY[1..LungimeHeap] OF char; Disponibil: 0..LungimeHeap; {primul caracter liber} NumarDeSiruri: 0..LungimeTabela; ------------------------------------------------------------

n continuare se prezint pentru exemplificare implementarea funciei AdaugCarir. Deoarece adugarea unui caracter produce depirea irului n cauz n dauna irului
urmtor din spaiul de lucru, funcia AdaugCarir trebuie: (1) S creeze o nou copie a irului n zona disponibil a heap-ului (2) S reactualizeze tabela de iruri i variabila Disponibil [4.2.2.b]. -----------------------------------------------------------PROCEDURE AdaugaCarSir(VAR s: TipSir; c: char); {O(n)} {adaug caracterul c la sfritul sirului s} VAR i,lungimeVeche,indicatorVechi: integer; BEGIN IF (s<1) OR (s>NumarDeSiruri) THEN *eroare(referin invalid de ir) ELSE [4.2.2.b] BEGIN lungimeVeche:= TabelaDeSiruri[s].lungime; indictorVechi:= TabelaDeSiruri[s].indicator; FOR i:=0 TO lungimeVeche-1 DO Heap[Disponibil+i]:=Heap[indicatorVechi+i]; Heap[Disponibil+lungimeVeche]:= c; TabelaDeSiruri[s].indicator:= Disponibil; TabelaDeSiruri[s].lungime:= lungimeVeche+1; Disponibil:= Disponibil+lungimeVeche+1 END {ELSE} END; {AdaugaCarSir} ------------------------------------------------------------

Se observ c n aceast implementare AdaugCarir necesit O(n) uniti de timp

pentru un ir de n caractere fa de implementarea bazat pe tablouri care necesit un timp constant (O(1)). Aceasta este tributul pltit facilitii de a putea lucra cu iruri de lungime variabil. De asemenea n urma crerii noii copii a irului supus operaiei de adugare, vechea instan a irului rmne n heap ocupnd memorie n mod inutil.

O alt problem potenial a acestei implementri este urmtoarea. Se consider spre exemplu o procedur care realizeaz copierea unui ir surs

ntr-un ir destinaie. n implementarea bazat pe tabel de iruri acest lucru se poate realiza simplu fcnd ca irului destinaie s-i corespund acelai indicator n heap ca i irului surs. Acest fenomen este cunoscut n programare sub denumirea de suprancrcare ("aliasing"), adic dou variabile diferite se refer la aceeai locaie de memorie. Dac se cere ns tergerea irului surs sau destinaie se pot pierde ambele iruri. Aceast problem poate fi ocolit printr-o implementare adecvat.

Spre exemplu se procedeaz ca i n cazul procedurii AdaugCarir, adic: (1) Pentru irul destinaie se creeaz o copie ncepnd cu prima locaie

disponibil n heap; (2) Se copiaz sursa n noua locaie; (3) Se introduce referina care indic aceast locaie, n tabela de iruri n intrarea corespunztoare irului nou creat. De fapt, n anumite variante ale limbajului BASIC, aceast manier de rezolvare a copierii st la baza implementrii instruciunii LET. 4.2.3. Implementarea irurilor cu ajutorul pointerilor

Esena implementrii irurilor cu ajutorul pointerilor rezid n reprezentarea acestora


printr-o colecie de celule nlnuite.

Fiecare celul conine [4.2.3.a]: Un caracter (sau un grup de caractere ntr-o implementare mai elaborat) Un pointer la celula urmtoare.
-----------------------------------------------------------TYPE PointerCelula = ^Celula; Celula = RECORD ch: char; urm: PointerCelula [4.2.3.a] END; TipSir = RECORD lungime: integer; cap,coada: PointerCelula END; ------------------------------------------------------------

Spre exemplu n fig. 4.2.3.a apare reprezentarea irului 'DATE' n aceast implementare,
lungime cap coad 4 .

Fig. 4.2.3.a. Implementarea irurilor cu ajutorul pointerilor

n secvenele [4.2.3.b, c, d, e] apare implementarea unor operatori primitivi specifici.


-----------------------------------------------------------PROCEDURE CreazaSirVid(VAR s: TipSir); {O(1)} BEGIN s.lungime:= 0; s.cap:= nil; [4.2.3.b] s:coada:= nil END; {CreazaSirVid} ------------------------------------------------------------

FUNCTION LungSir(s: TipSir): integer; {O(1)} BEGIN [4.2.3.c] LungSir:= s.lungime END; {LungSir} -----------------------------------------------------------FUNCTION FurnizeazaCarSir(s:TipSir; poz:integer): char; VAR contor: integer; {O(n)} p: PointerCelula; [4.2.3.d] BEGIN IF (poz<1) AND (poz>s.lungime) THEN BEGIN *eroare(index eronat) FurnizeazaCarSir:= chr(0) {caracterul nul} END ELSE BEGIN p:= s.cap; FOR contor:= 1 TO poz-1 DO p:= p^.urm; FurnizeazaCarSir:= p^.ch END END; {FurnizeazaCarSir} -----------------------------------------------------------PROCEDURE AdaugaCarSir(var s:TipSir; c:char); {O(1)} BEGIN IF s.lungime=0 THEN BEGIN new(s.cap); s.cap^.ch:= c; s.cap^.urm:= nil; s.coada:= s.cap [4.2.3.e] END ELSE BEGIN new(s.coada^.urm); s.coada^.urm^.ch:= c; s.coada^.urm^.urm:= nil; s.coada:= s.coada^.urm END;{ELSE} s.lungime:= s.lungime+1 END; {AdaugaCarSir} ------------------------------------------------------------

n ceea ce privete implementarea operaiei de copiere apar aceleai probleme legate


de suprancrcare care pot fi rezolvate ntr-o manier asemntoare prin generarea unui ir nou destinaie identic cu sursa. Un astfel de operator Copiaz(surs, destinaie) poate fi utilizat cu succes n implementarea operaiei Concatir [4.2.3.f]. -----------------------------------------------------------PROCEDURE ConcatSir(VAR u: TipSir; s: TipSir); VAR temp: TipSir; BEGIN IF u.lungime=0 THEN Copiaza(u,s) ELSE IF s.lungime<>0 THEN

BEGIN [4.2.3.f] Copiaza(temp,s); u.coada^.urm:= temp.cap; u.coada:= temp.coada END END; {ConcatSir} ------------------------------------------------------------

Acest mod de reprezentare a irurilor are unele avantaje. (1) Permite o mare flexibilitate n gestionarea irurilor (2) nltur dezavantajul lungimii fixe. Principalele dezavantaje rezid n: (1) Parcurgerea secvenial a irului n vederea realizrii accesului la o

anumit poziie, (2) Risipa de spaiu de memorie datorat prezenei pointerului asociat fiecrui caracter al irului.

4.2.4. Comparaie ntre metodele de implementare a irurilor

Dup cum s-a vzut, se utilizeaz trei maniere de implementare a irurilor, Implementarea bazat pe tablouri, Implementarea bazat pe pointeri Implementarea bazat pe o tabel care reunete parial caracteristicile
structurilor anterioare.

Din punctul de vedere al paragrafului de fa prezint interes primele dou metode,


caracteristicile celei de-a treia putnd fi uor deduse din acestea.

Diferenele marcante dintre cele dou moduri de implementare a irurilor i anume

prin tablouri i pointeri i au de fapt originea n caracterul fundamental al structurilor de date suport. Astfel: irul implementat ca tablou este o structur de date cu acces direct; irul implementat prin pointeri este o structur cu acces secvenial, de fapt o list nlnuit. proprietile irurilor n fiecare din cele dou moduri de reprezentare, ct i algoritmii care implementeaz operatorii specifici.

Aceste caracteristici fundamental diferite influeneaz ntr-o manier decisiv att

Implementarea irurilor cu ajutorul tablourilor Posibilitatea realizrii directe a accesului la caractere. Accesul la o poziie precizat ntr-un ir este imediat, element care

avantajeaz operaiile CopiazSubir, tergeir, Insereazir, FurnizeazCarir. Algoritmii specifici pentru FurnizeazCarir, AdaugCarir i Lungir sunt rapizi (O(1)), Restul operatorilor n marea lor majoritate avnd performana O(n). Operatorul PoziieSubir n implementarea bazat pe cutare direct are performana O(n m) unde n este lungimea irului iar m lungimea

subirului, (exist pentru aceast reprezentare i metode mult mai performante). Dezavantaje ale acestui mod de implementare: Trunchierea cauzat de inseria ntr-un ir complet. Inseria i suprimarea ntr-un ir presupun micri de elemente i consum de timp proporional cu lungimea irului.

Implementarea irurilor cu ajutorul pointerilor Se bucur de flexibilitatea specific acestei implementri Este grevat de proprietile rezultate din caracterul secvenial al reprezentrii. Faza de inserie propriu-zis i suprimare din cadrul operatorilor

Insereazir i tergeir se realizeaz n O(1) uniti de timp Cutarea poziiei ns necesit practic O(n) uniti. De fapt cutarea secvenial a poziiei numerice este marele dezavantaj al acestei reprezentri care diminueaz performana marii majoriti a operatorilor. Cu toate c n fiecare moment se consum spaiu de memorie numai pentru caracterele existente, totui risipa de memorie datorat pointerilor este mare. Performana operatorilor Concatir i AdaugCarir crete n mod semnificativ dac se utilizeaz pointerul "coad". Aceast implementare este indicat a fi utilizat atunci cnd: Este foarte important ca irurile s nu fie trunchiate Cnd se cunoate cu probabilitate redus lungimea maxim a irurilor. Admind trunchierea ca i un ru necesar, marea majoritate a versiunilor PASCAL care definesc i implementeaz n cadrul limbajului tipul string i operatorii afereni utilizeaz implementarea irurilor bazat pe tablouri.

4.3. Tehnici de cutare n iruri Una din operaiile cele mai frecvente care se executat asupra irurilor este cutarea. ntruct performana acestei operaii are o importan covritoare asupra marii
majoriti a prelucrrilor care se realizeaz ntr-un sistem de calcul, studiul tehnicilor de cutare performante reprezint o preocupare permanent a cercettorilor n domeniul programrii. tehnici de cutare n iruri de caractere.

n cadrul acestui paragraf vor fi trecute n revist cteva dintre cele mai cunoscute

4.3.1. Cutarea tabelar (Table search)

O cutare ntr-un tablou se numete i cutare tabelar ("table search"), cu


deosebire n situaia n care cheile sunt la rndul lor obiecte structurate spre exemplu tablouri de numere sau de caractere. cuvinte.

Cazul mai des ntlnit este acela n care cheile tablourilor sunt iruri de caractere sau

n continuare, pentru obiectivele acestui paragraf se definesc tipul ir respectiv


relaia de ordonare (lexicografic) a dou iruri x i y dup cum urmeaz [4.3.1.a,b,c]: -----------------------------------------------------------{Definirea tipului ir} TYPE TipSir = ARRAY[0..m-1] OF char; [4.3.1.a] -----------------------------------------------------------{Definirea relaiei de egalitate a dou iruri (coincidena) (x=y) <= (Aj:0j<m:xj=yj) [4.3.1.b] -----------------------------------------------------------{Definirea relaiei de ordonare a dou iruri} (x<y) <= Ei:0i<m:((Aj:0j<i:xj=yj)&(xi<yi)) [4.3.1.c] ------------------------------------------------------------

Pentru a stabili coincidena, este necesar stabilirea egalitii tuturor caracterelor


corespunztoare din irurile comparate. Presupunnd c lungimea acestora este mic, (spre exemplu mai mic dect 30), n acest scop se poate utiliza cutarea liniar. fiecrui ir o informaie suplimentar referitoare la lungimea sa.

n cele mai multe aplicaii se lucreaz cu iruri de lungime variabil, asociindu-se Utiliznd tipul ir anterior definit, lungimea nu poate depi valoarea maxim m. Dei este limitativ, aceast schem permite suficient flexibilitate evitnd alocarea
dinamic a memoriei.

Sunt utilizate n mod frecvent dou moduri de reprezentare a lungimii irurilor: (1) Lungimea este specificat implicit, plasnd pe ultima poziie a irului (dup

ultimul caracter) un caracter prestabilit (de exemplu caracterul avnd codul 0 (CHR(0)). Pentru aplicaiile ce urmeaz este important ca acesta s fie cel mai mic caracter al setului de caractere. n limbajul C este chiar codul cu valoarea zero. (2) Lungimea irului este memorat n mod explicit ca i prim element al tabloului. Astfel un ir are forma s = so,s1,s2,...,sn-1 unde s1,...,sn-1 sunt caracterele irului iar so memoreaz lungimea irului de caractere. Avantajul acestei soluii: lungimea irului este direct disponibil; Dezavantajul: lungimea este limitat la valoarea maxim reprezentabil pe unitatea de informaie alocat unui caracter, de regul un octet (255).

n continuare, pentru precizarea lungimii unui ir se va utiliza modalitatea (1),


respectiv cea bazat pe caracterul marker, definit drept caracterul al crui cod are valoarea 0.

n aceste condiii, compararea a dou iruri va lua forma [4.3.1.d]:


-----------------------------------------------------------VAR x,y: TipSir; {compararea liniar a dou iruri} {O(n)} [4.3.1.d] i:= 0;

WHILE (x[i]=y[i]) AND (x[i]<>chr(0)) DO i:= i+1; ------------------------------------------------------------

Caracterul corespunztor codului 0 acioneaz ca i fanion. Invariantul buclei, adic condiia a crei ndeplinire determin reluarea buclei
(ciclului), este [4.3.1.e], iar condiia de terminare [4.3.1.f]: -----------------------------------------------------------{Invariantul buclei de comparare} Aj:0ji:xj=yjchr(0) [4.3.1.e] -----------------------------------------------------------{Condiia de terminare a buclei de comparare} ((xi yi)OR(xi=yi=chr(0)))&(Aj:0j<i:xj=yj chr(0)) [4.3.1.f] --------------------------------------------------------

Aceast condiie stabilete: Coincidena celor dou iruri x = y dac la terminarea buclei

xi= yi= chr(0), sau Inegalitatea x < y(x > y) dac la terminarea buclei xi < yi (xi > yi). Se face precizarea c inegalitatea xi < yi poate apare i n situaia n care x coincide cu y dar x e mai scurt dect y. n acest caz se obine xi yi dar xi = chr(0) (s-a ajuns la sfritul irului x). ntruct n aceast situaie xi este cel mai mic caracter, aceasta echivaleaz cu xi < yi , deci x < y, ceea ce este corect inclusiv din punctul de vedere al ordonrii lexicografice.

n acest context, cutarea tabelar este de fapt o cutare ncuibat care const: Dintr-o parcurgere a intrrilor unei tabele de iruri, n cadrul fiecrei intrri, dintr-o secven de comparaii ntre componentele
individuale ale irului curent i cele ale irului cutat.

Se consider urmtoarele structuri de date [4.3.1.g], unde T este tabela de iruri iar x:
TipSir, argumentul cutat. -----------------------------------------------------------CONST n = {numrul de iruri din tabela de iruri}; m = {numrul maxim de caractere dintr-un sir}; {sfritul unui ir se marcheaz cu chr(0)} TYPE TipSir = ARRAY[0..m-1] OF char; TipTabela = ARRAY[0..n-1] OF TipSir; [4.3.1.g] VAR T: TipTabela; -----------------------------------------------------------utiliza cutarea binar.

Presupunnd c n este suficient de mare i c tabela este ordonat alfabetic, se poate Utiliznd algoritmul cutrii binare i compararea a dou iruri, se poate redacta
urmtoarea variant de cutare tabelar [4.3.1.h]. -----------------------------------------------------------FUNCTION CautareTabelara(VAR T: TipTabela; VAR x: TipSir; VAR poz: integer): boolean; VAR i,s,d,mij: integer;

BEGIN s:= 0;d:= n; WHILE s<d DO [4.3.1.h] BEGIN mij:= (s+d) div 2; i:= 0; WHILE (T[m,i]=x[i])AND(x[i]<>chr(0)) DO i:=i+1; IF T[m,i]<x[i] THEN s:= m+1 ELSE d:= m END;{WHILE} IF d<n THEN BEGIN i:= 0; WHILE (T[d,i]=x[i])AND(X[i]<>chr(0)) DO i:= i+1 END;{IF} poz:= d; CautareTabelara:= (d<n)AND(T[d,i]=x[i]) END; {CautareTabelara} ----------------------------------------------------------- Funcia CautareTabelar primete ca parametri de intrare tabela T i sirul de cautat x i returneaz true respectiv false dup cum cutarea este reuit sau nu.

n cazul unei cutri reuite, n variabila de ieire poz se returneaz intrarea


corespunztoare din tabel;

n cazul unei cutri nereuite variabila de ieire poz returneaz valoarea 0. Se observ c T i x care sunt iruri, se declar cu VAR, pentru a li se transmite (prin
stiv) doar adresa.

4.3.2. Cutarea de iruri direct

O manier frecvent ntlnit de cutare este aa numit cutare de iruri direct


("string search").

Formularea problemei: Se d un tablou s de n elemente i un tablou p de m elemente, unde 0<m<n


declarate astfel [4.3.2.a]: -----------------------------------------------------------VAR s: ARRAY[0..n-1] OF char; p: ARRAY[0..m-1] OF char; [4.3.2.a] ------------------------------------------------------------

Cutarea se iruri are drept scop stabilirea primei apariii a lui p n s. De regul, s poate fi privit ca un text, iar p ca un cuvnt model ("pattern") a crui
prim apariie se caut n textul s.

Aceasta este o operaie fundamental n toate sistemele de prelucrare a textelor i n


acest sens este de mare interes gsirea unor algoritmi ct mai eficieni.

Cea mai simpl metod de cutare este aa numita cutare de iruri direct. Rezultatul unei astfel de cutri este un indice i care precizeaz apariia unei
coincidene ntre model i ir.

Acest lucru este formalizat de predicatul P(i,j) [4.3.2.b].


-----------------------------------------------------------P(i,j)<= Ak:0 k < j:si+k = pk [4.3.2.b] ------------------------------------------------------------

Predicatul P(i,j) precizeaz faptul c exist o coinciden ntre: irul s (ncepnd cu indicele i) irul p (ncepnd cu indicele 0) pe o lungime de j caractere (fig. 4.3.2.a).
i 0 s i+j-1 n-1

p 0 1 2 j caractere j-1 m -1

Fig.4.3.2.a. Reprezentarea grafic a predicatului P(i,j)

Este evident c indexul i care rezult din cutarea direct de iruri trebuie s
satisfac predicatul P(i,m).

Aceast condiie nu este ns suficient. Deoarece cutarea trebuie s furnizeze prima apariie a modelului, P(k,m) trebuie
s fie fals pentru toi k < i .

Se noteaz aceast condiie cu Q(i)[4.3.2.c].


-----------------------------------------------------------Q(i) = Ak: 0 k < i: ~P(k,m) [4.3.2.c] ------------------------------------------------------------

Punerea problemei sugereaz formularea cutrii directe de iruri ca i o iteraie de


comparaii redactat n termenii predicatelor Q respectiv P.

Astfel implementarea lui Q(i) conduce la secvena [4.3.2.d]:


-----------------------------------------------------------{Implementarea predicatului Q(i)} i:= -1; REPEAT i:= 1+1; [4.3.2.d]

gasit:= P(i,m) UNTIL gasit OR (i=n-m); ------------------------------------------------------------

Calculul lui P rezult n continuare ca i o iteraie de comparaii de caractere


individuale.

Rafinarea secvenei anterioare conduce de fapt la implementarea cutrii directe de


iruri ca o repetiie ntr-o alt repetiie [4.3.2.e]. -----------------------------------------------------------{Implementarea cutrii directe de iruri} FUNCTION CautareDirecta(VAR poz: integer): boolean; VAR i,j: integer; {i parcurge caracterele din ir, j parcurge caracterele din model} BEGIN i:= -1; REPEAT [4.3.2.e] i:= i+1; j:= 0; {Q(i)} WHILE (j<m)AND(s[i+j]=p[j]) DO j:= j+1 {P(i,m)} UNTIL (j=m)OR(i=n-m); poz:= i; CautareDirecta:= j=m END; {CautareDirecta} -----------------------------------------------------------P(i,m).

Termenul j = m din condiia de terminare, corespunde lui gsit deoarece el implic Termenul i=n-m implic Q(n-m), deci non existena vreunei coincidene n
cadrul irului.

Analiza cutrii de iruri directe. Algoritmul lucreaz destul de eficient dac se presupune c nepotrivirea n

procesul de cutare apare cel mult dup cteva comparaii n cadrul buclei interioare. Astfel pentru un set de 128 de caractere se poate presupune c nepotrivirea apare dup inspectarea a 1 sau 2 caractere. Cu toate acestea n cazul cel mai nefavorabil, degradarea performanei este ngrijortoare. Astfel dac spre exemplu irul s este format din n-1 caractere 'A' urmate de un singur 'B', Iar modelul const din m-1 caractere 'A' urmate de un 'B', n acest caz este necesar un numr de comparaii de ordinul n m pentru a gsi coincidena la sfritul irului. Din fericire exist metode care mbuntesc radical comportarea algoritmului n aceast situaie. Tehnicile de cutare care sunt prezentate n continuare materializeaz acest deziderat.

4.3.3. Cutarea de iruri Knuth-Morris-Pratt

n anul 1970 Knuth, Morris i Pratt au inventat un algoritm de cutare n iruri de

caractere care necesit un numr de comparaii de ordinul n chiar n cel mai defavorabil caz. singur poziie la ntlnirea unei nepotriviri, aa cum se realizeaz n metoda anterioar, pe lng o eficien sczut, conduce la pierderea unor informaii utile.

Noul algoritm se bazeaz pe observaia c avansul modelului n cadrul irului cu o

Astfel dup o potrivire parial a nceputului modelului cu irul, ntruct se cunoate parial irul (pn n punctul baleat), Dac avem cunotine apriorice asupra modelului obinute prin
precompilare, Le putem folosi pentru a avansa mai rapid n ir.

Acest lucru este pus n eviden de exemplul urmtor n care: Se caut modelul MARGINE n textul surs; Caracterele care se compar sunt cele subliniate; Dup fiecare nepotrivire, modelul este deplasat de-a lungul ntregului drum
parcurs ntruct deplasri mai scurte nu ar putea conduce la o potrivire total [4.3.3.a]. -----------------------------------------------------------MAREA MARMARA SE MARGINESTE... MARGINE MARGINE MARGINE MARGINE MARGINE [4.3.3.a] MARGINE . . . MARGINE ------------------------------------------------------------

Utiliznd predicatele P i Q, algoritmul KMP poate fi formulat astfel [4.3.2.b]:


-----------------------------------------------------------{cutarea de iruri Knuth-Morris-Pratt} i:= 0; j:= 0; WHILE (j<m) AND (i<n) DO [4.3.3.b] BEGIN {Q(i-j) & P(i-j,j)} WHILE (j>=0) AND (s[i]<>p[j]) DO j:= d; i:= i+1; j:= j+1 END; {WHILE} ------------------------------------------------------------

Formularea algoritmului este aproape complet, cu excepia factorului d care


precizeaz valoarea deplasrii.

Se subliniaz faptul c n continuare Q(i-j) i P(i-j,j) sunt invariaii globali


la care se adaug relaiile 0<i<n i 0<j<m.

Este foarte important de subliniat faptul c de aici nainte nu i va fi indicele care


precizeaz poziia primului caracter al modelului n ir ci valoarea i-j.

Indicele i precizeaz poziia la care a ajuns procesul de cutare n irul s


(fig.4.3.3.a].
i-j s j n-1

Q(i-j)

m-1

P(i-j, j)
j caractere

Fig. 4.3.3.a. Reprezentarea predicatelor P i Q n contextul cutrii KMP

Dac algoritmul se termin deoarece j = m, termenul P(i-j,j) al invariantului


devine P(i-m,m), ceea ce conform relaiei care-l definete pe P potrivire ncepnd de la poziia i-m . potriviri. indic o

Dac la terminare i = n i j < m, invariantul Q(i) implic absena vreunei n vederea determinrii valorii lui d trebuie lmurit rolul atribuirii j:= d. Considernd prin definiie c d < j atunci atribuirea j:= d reprezint o deplasare
a modelului spre dreapta cu j - d poziii.

Este evident c este de dorit ca deplasarea s fie ct mai lung i n consecin d ct


mai mic posibil. Pentru a determina valoarea lui d se prezint dou exemple. -----------------------------------------------------------Exemplul 4.3.3.a. Se consider situaia din figura 4.3.3.b. .
i-j i i s p

.. ..

A B

C D

..

A B

C D .. A B C E ECcCCCeeeE 0 1 2 3

A B C E E 1 0 2 3
j=3 d=0

j=0

Fig. 4.3.3.b. Deplasarea modelului n cadrul irului s. Cazul 1.

Se observ c deplasarea se poate face peste 3 poziii ntruct nu mai pot apare alte
potriviri. n aceste condiii d = 0 , iar atribuirea j = d produce deplasarea cu jd = 3 poziii.

---------------------------------------------------------- Exemplul 4.3.3.b. Se consider situaia din figura 4.3.3.c.


i-j s p i

...

A A
0

B B
1

C C
2

A B

...

A B E
3 4 5 j=5 i-j i d=2 j=d=2

s p

...

A B A E 0 B 1

C C A 2
j=2

... B 4 5

d=2

Fig. 4.3.3.c. Deplasarea modelului n cadrul irului s. Cazul 2.

n aceast situaie deplasarea nu se poate face peste toat lungimea modelului ntruct
mai exist o posibilitate de potrivire ncepnd de la i-2. 'AB' de lungime 2.

Acest lucru se ntmpl ntruct n cadrul modelului exist 2 subsecvene identice Se noteaz cu d lungimea acestei subsecvene. Dup cum se observ din figura 4.3.3.c, deplasarea modelului la dreapta se poate face
numai peste j-d (5 - 2 = 3) poziii deoarece primele d poziii ale modelului coincid cu cele d poziii ale textului care preced indicele i (care indic neconcordana). ----------------------------------------------------------- n concluzie: a) Iniial exist o potrivire ntre irul s i modelul p ncepnd de la poziia i-j pe lungime j adic este valabil invariantul P(i-j,j) & Q(i-j). b) Dup efectuarea deplasrii avem din nou o potrivire la i-d de lungime d adic este valabil invariantul P(i-d,d) & Q(i-d).

Aceast situaie rezult din observaia c n model exist dou subsecvene identice
de lungime d una la nceputul modelului alta de la poziia j-d la j-1.

Formal acest lucru este precizat de relaia [4.3.3.c].

-----------------------------------------------------------p0... pd-1 = pj-d... pj [4.3.3.c] ----------------------------------------------------------- Pentru ca lucrurile s se desfoare corect este necesar ca lungimea lui d s fie maxim, adic s avem cea mai lung subsecven care ndeplinete condiiile precizate.

Rezultatul esenial este acela c valoarea lui d este determinat exclusiv din

structura modelului i nu depinde de textul propriu-zis. Din cele expuse rezult c pentru a-l determina pe d: Pentru fiecare valoare a lui j Se caut n model cel mai mare d, adic cea mai lung secven de caractere a modelului care precede imediat poziia j i care se potrivete ca un numr egal de caractere de la nceputul modelului. Se noteaz valoarea d pentru un anumit j cu dj. ntruct valorile dj depind numai i numai de model, naintea cutrii propriuzise poate fi construit un tablou d cu aceste valori printr-un proces de precompilare a modelului. Desigur acest efort merit s fie depus dac m << n. n continuare se prezint trei situaii particulare de determinare a valorii deplasamentului d. ----------------------------------------------------------- Exemplul 4.3.3.c. Se consider situaia din figura 4.3.3.d.
i-j s p i

...

A A B
0

A A A A A A
1 2 3

A A A

...

A A B
4 5 d5 = 4 j = d 5 = 4

A
0

A A
1 2

A A
3 4

B
5 j=4

Fig. 4.3.3.d. Determinarea deplasamentului d . Cazul 1. ----------------------------------------------------------- Exemplul 4.3.3.d. Se consider situaia din figura 4.3.3.e.
i-j s p i j=4 B C B 1 C 2 A A 3 B B 4 D C 5 A j=0 0 B 1 d5 = 0 j = d 5 = 0 C 2 A 3 B 4 C 5 A B

...

A A 0

...

Fig. 4.3.3.e. Determinarea deplasamentului d. Cazul 2.

------------------------------------------------------------

Exemplul 4.3.3.e. Se consider situaia din figura 4.3.3.f.


i-j s p i C C 2 D D 3 E E 4 F A 5 d5 = 0 j = d 5 = 0 A B

...

A B A 0 B 1

...

j=5
p A 0 j=0 p A 0 j = -1 B 1 C 2 D 3 E 4 A 5 B 1 C 2 D 3 E 4 A 5

d5 = -1

Fig. 4.3.3.f. Determinarea deplasamentului d. Cazul 3.

Ultimul exemplu sugereaz c se poate merge mai departe cu deplasrile i c n loc

s se realizeze deplasarea peste 5 poziii, n aceast situaie se poate face peste ntregul model deci d5 = -1 iar deplasarea se face peste 5 (1) = 6 poziii. ultimul su caracter.

Acest lucru este posibil deoarece primul caracter al modelului este identic cu ntruct s-a constatat necoincidena pentru ultimul caracter al modelului, rezult c
nu poate exista o coinciden nici n cazul primului caracter al acestuia (identic cu ultimul), deci deplasarea se poate realiza peste el.

n aceste condiii, calculul lui dj care presupune cutarea celor mai lungi secvene
care se potrivesc n baza relaiei [4.3.3.c], poate fi completat dup cum urmeaz.

Dac se constat c dj = 0 i p0 = pj , atunci se poate face dj = -1 indicnd


deplasarea integral a modelului fa de poziia sa curent n cadrul irului s. ------------------------------------------------------------

n figura 4.3.3.g. apare un tablou demonstrativ n care pentru mai multe iruri model

p se precizeaz structura tabloului d asociat adic valorile dj corespunztoare caracterelor modelului rezultate n urma precompilrii.

n stnga tabelului apare modelul p iar n dreapta tabelul d asociat.

p 0 A A A A A A A A M A A B B B B B A A C C C C C R A A A A D D G A B B B E E I B C C D F A N E D 1 2 3 4 5 6 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 1 2

d 3 4 5 6

-1 -1 -1 -1 0 0 0

-1 0 0 0 0 0 0

4 0 0 2 0 -1 0 0 3

Fig. 4.3.3.g. Exemple de precompilare a unor modele

Programul care implementeaz cutarea de iruri Knuth-Morrison-Pratt apare n


secvena [4.3.3.d]. -----------------------------------------------------------{Cutarea de iruri Knuth-Morrison-Pratt} CONST mmax={lungime maxim model}; nmax={lungime maxim ir surs}; VAR m{lungime model},n{lungime ir}: integer; p: ARRAY[0..mmax-1] OF char;{model} s: ARRAY[0..nmax-1] OF char;{ir} d: ARRAY[0..mmax-1] OF integer;{tabela de deplasri} FUNCTION CautareKMP(VAR poz: integer): boolean; VAR i,j,k: integer; BEGIN *citire ir n s {n = lungime curent ir} *citire model n p {m = lungime curent model} j:= 0; k:= -1; d[0]:= -1; {precompilare model} WHILE j<m-1 DO BEGIN WHILE (k>=0) AND (p[j]<>p[k]) DO k:= d[k]; j:= j+1; k:= k+1; IF p[j]=p[k] THEN d[j]:= d[k] ELSE d[j]:= k END;{WHILE} [4.3.3.d] i:= 0; j:= 0; {cutare model} WHILE (j<m) AND (i<n) DO BEGIN WHILE (j>=0) AND (s[i]<>p[j]) DO j:= d[j]; j:= j+1; i:= i+1; END;{WHILE} poz:= i-m; CautareKMP:= j=m END;

------------------------------------------------------------

Programul KMP const din 4 pri: Prima realizeaz citirea irului n care se face cutarea, A doua realizeaz citirea modelului, A treia precompileaz modelul i calculeaz valorile dj, A patra implementeaz cutarea propriu-zis. Se precizeaz c partea a treia fiind practic o cutare de iruri se implementeaz tot ca
i o cutare KMP.

Analiza cutrii KMP. Analiza exact a performanei cutrii de iruri Knuth-Morrison-Pratt este
asemeni algoritmului foarte sofisticat. Inventatorii demonstreaz c numrul de comparaii de caractere este de ordinul n + m , ceea ce reprezint o mbuntire substanial fa de m n . De asemenea se subliniaz faptul c pointerul i care baleeaz irul nu merge niciodat napoi spre deosebire de cutarea direct, unde dup o potrivire parial se reia cutarea cu modelul deplasat cu o poziie, chiar dac o parte din caracterele irului au fost deja parcurse. Acest avantaj permite aplicarea acestei metode i n cazul unor prelucrri secveniale.

4.3.4. Cutarea de iruri Boyer-Moore

Metoda ingenioas de cutare KMP conduce la beneficii numai dac nepotrivirea


dintre ir i model a fost precedat de o potrivire parial de o anumit lungime.

Numai n acest caz deplasarea modelului se realizeaz peste mai mult de o poziie. Din pcate aceast situaie n realitate este mai degrab excepia dect regula;
potrivirile apar mult mai rar ca i nepotrivirile. normale de texte.

n consecin, beneficiile acestei metode sunt reduse n marea majoritate a cutrilor Metoda de cutare inventat n 1975 de R.S. Bo yer i J.S. Moore mbuntete
performana att pentru situaia cea mai defavorabil ct i n general.

Cutarea BM, dup cum mai este numit, este bazat pe ideea neobinuit de a
ncepe compararea caracterelor de la sfritul modelului i nu de la nceput.

Ca i n cazul metodei KMP, modelul este precompilat anterior ntr-un tablou d. Astfel, pentru fiecare caracter x al setului de caractere, se noteaz cu dx distana
dintre cea mai din dreapta apariie a lui x n cadrul modelului i sfritul modelului (fig.4.3.4.a.).

modelul p: 0 1 2

dx m-2 m-1

Fig. 4.3.4.a. Determinarea valorii dx corespunztoare caracterului x al modelului

Valoarea dx se trece n tabloul d n poziia corespunztoare caracterului x. Pentru caracterele care nu sunt n model respectiv pentru ultimul caracter al
modelului, dx se face egal cu lungimea total a modelului.

n continuare, se presupune c n procesul de comparare al irului cu modelul a

aprut o nepotrivire ntre caracterele corespunztoare. n aceast situaie modelul poate fi imediat deplasat spre dreapta cu d[pm-1] poziii, valoare care este de regul mai mare ca 1. Se precizeaz faptul c pm-1 este caracterul din irul baleat s, corespunztor ultimului caracter al modelului la momentul considerat, indiferent de locul n care s-a constatat nepotrivirea (figura 4.3.4.b.).
nepotrivire s potrivire p 0 1 2 3 4 ... m -1 pm-1

Fig. 4.3.4.b. Comparare ir - model pentru determinarea lui pm-1


-----------------------------------------------------------------

Dac caracterul p m-1 nu apare n model, deplasarea este mai mare i anume cu ntreaga lungime a modelului.

Exemplul din secvena [4.3.4.a.] evideniaz acest proces.


6 5 6 MARGINE 6543217 MARGINE MARGINE
[4.3.4.a]

MARGINE

MAREA MARMARA SE MARGINESTE

-----------------------------------------------------------------

Deoarece compararea individual a caracterelor se realizeaz de la dreapta spre


stnga, este mai convenabil urmtoarea formulare a predicatelor P i Q [4.3.4.b]. -----------------------------------------------------------P(i,j)= Ak: j k < m:si-j+k = pk [4.3.4.b] Q(i) = Ak:0 k < i: ~P(k,0) ------------------------------------------------------------

Interpretarea grafic a a noii formulri a predicatelor n cauz apare n figura 4.3.4.c. Dup cum se observ, exist o potrivire ncepnd de la i spre stnga
cu ultimele m - j caractere ale modelului adic ncepnd de la poziia j (spre dreapta) a modelului, pn la sfritul acestuia.

Cnd j = 0 , avem o potrivire de la i - m cu m - 0 caractere, deci cu modelul


integral (de la poziia 0 a modelului).
m k s m-j i

p 0 1 2 3 j j m-j 4 .... m-1

Fig. 4.3.4.c. Interpretarea predicatelor P i Q n cutarea BM

Aceste predicate sunt utilizate n formularea urmtoare a algoritmului BM [4.3.4.c]:


-----------------------------------------------------------{cutare Boyer-Moore} i:= m; j:= m; WHILE (j>0) AND (i<n) DO BEGIN {Q(i-m)} [4.3.4.c] j:= m; k:= i; WHILE (j>0) AND (s[k-1]=p[j-1]) DO BEGIN {P(k-j,j)&(k-j=i-m)} k:= k-1; j:= j-1 END; i:= i+d[ORD(s[i-1])] END; {cutare Boyer-Moore} ------------------------------------------------------------

Indicii implicai satisfac urmtoarele relaii: 0 < j < m i 0 < i, k < n.

Astfel terminarea algoritmului cu j = 0 mpreun cu P(k-j,j), implic

P(k,0) adic o potrivire ncepnd de la poziia k spre dreapta, de m caractere, unde k = i - m. Terminarea algoritmului cu j > 0 implic i = n . n acest caz Q(i-m) devine Q(n-m) indicnd absena potrivirii.

Programul urmtor [4.3.4.d] implementeaz strategia Boyer-Moore ntr-un context


similar cutrii KMP. -----------------------------------------------------------{Cutarea Boyer-Moore} CONST mmax={lungime maxim model}; nmax={lungime maxim ir sursa}; [4.3.4.c] VAR m{lungime model},n{lungime ir}: integer; p: ARRAY[0..mmax-1] OF char;{model} s: ARRAY[0..nmax-1] OF char;{ir} d: ARRAY[0..255] OF integer;{tabela de deplasri} FUNCTION CautareBM(VAR poz: integer): boolean; VAR i,j,k: integer; BEGIN *citire ir {n este lungimea curent a irului} *citire model {m este lungimea curent a modelului} FOR i:=0 TO 255 DO d[CHR(i)]:= m; {compilare model} FOR j:=0 TO m-2 DO d[p[j]]:= m-j-1; i:= m; j:= m; {cautare model} WHILE (j>0) AND (i<=n) DO BEGIN j:= m; k:= i; WHILE (j>0) AND (s[k-1]=p[j-1]) DO BEGIN k:= k-1; j:= j-1 END; IF j>0 THEN i:= i+d[s[i-1]] END; {WHILE} poz:= i-m; {poz:=k} CautareBM:= j=0 END; {Cautare BM} ------------------------------------------------------------

Analiza cutrii Boyer-Moore, Autorii cutrii BM, au demonstrat proprietatea remarcabil c n toate cazurile, cu
excepia unora special construite, numrul de comparaii este substanial mai redus dect n. n ir peste un caracter diferit de cele ale modelului, numrul de comparaii este n/m .

n cazul cel mai favorabil cnd ultimul caracter al modelului nimerete ntotdeauna

Autorii indic anumite direcii de mbuntire a performanelor algoritmului.

Una din ele este aceea de a combina strategia BM care realizeaz o deplasare

substanial n prezena unei nepotriviri cu strategia KMP care permite o deplasare mai substanial dup detecia unei potriviri (pariale). Aceast metod necesit dou tablouri precalculate: d1 (tabloul specific cutrii BM) i d2 (tabloul corespunztor cutrii KMP). Toate acestea conduc la complicarea algoritmului i la creterea regiei sale prin precompilarea tablourilor.

De fapt n multe cazuri trebuie analizat oportunitatea implementrii unor extensii

sofisticate care dei rezolv anumite situaii punctuale, pot avea drept consecin deteriorarea performanelor de ansamblu prin creterea excesiv a regiei algoritmului implicat.

5. Recursivitate
5.1. Introducere
O definiie recursiv este acea definiie care se refer la un obiect care se definete ca parte a propriei sale definiri. Desigur o definiie de genul "o floare este o floare" care poate reprezenta n poezie un univers ntreg, n tiin n general i n matematic n special nu furnizeaz prea multe informaii despre floare. O caracteristic foarte important a recursivitii este aceea de a preciza o definiie ntr-un sens evolutiv, care evit circularitatea. o Spre exemplu o definiie recursiv este urmtoarea: "Un buchet de flori este fie (1) o floare, fie (2) o floare adugat buchetului". Afirmaia (1) servete ca i condiie iniial, indicnd maniera de amorsare a definiiei Afirmaia (2) precizeaz definirea recursiv (evolutiv) propriuzis. o Varianta iterativ a aceleai definiii este "Un buchet de flori const fie dintr-o floare, fie din dou, fie din 3 flori, fie ... etc. o Dup cum se observ, definiia recursiv este simpl i elegant dar oarecum indirect, n schimb definiia iterativ este direct dar greoaie. Despre un obiect se spune c este recursiv dac el const sau este definit prin el nsui. Prin definiie orice obiect recursiv implic recursivitatea ca i proprietate intrinsec a obiectului n cauz.

Recursivitatea este utilizat cu mult eficien n matematic, spre exemplu n definirea numerelor naturale, a structurilor de tip arbore sau a anumitor funcii [5.1.a]. ----------------------------------------------------------- Numerele naturale: (1) 1 este un numr natural; (2) succesorul unui numr natural este un numr natural.

Structuri de tip arbore:

(1) r este un arbore (numit arbore gol sau arbore cu un singur nod) (2) dac a1 i a2 sunt arbori, atunci structura
r a1 a2

este un arbore ( reprezentat rsturnat).

[5.1.a]

Funcia factorial n! (definit pentru ntregi pozitivi):

(1) 0! = 1 (2) Dac n > 0 , atunci n! = n*(n-1)! ------------------------------------------------------------

Puterea recursivitii rezid evident n posibilitatea de a defini un set infinit de


obiecte printr-o relaie sau un set de relaii finit, caracteristic evideniat foarte bine de exemplele furnizate mai sus.

Cunoscut ca i un concept fundamental n matematic, recursivitatea a devenit i o

puternic facilitate de programare odat cu apariia limbajelor de nivel superior care o implementeaz ca i caracteristic intrinsec (ALGOL, PASCAL, C, JAVA etc.). natere unor confuzii, se vor defini n continuare cele dou concepte din punctul de vedere al tehnicilor de programare. Iteraia este execuia repetat a unei poriuni de program, pn n momentul n care se ndeplinete o condiie respectiv atta timp ct condiia este ndeplinit. Fiecare execuie se duce pna la capt, se verific ndeplinirea condiiei i n caz de rspuns nesatisfctor se reia execuia de la nceput. Exemple clasice n acest sens sunt structurile repetitive WHILE, REPEAT i FOR . Recursivitatea presupune de asemenea execuia repetat a unei poriuni de program. n contrast cu iteraia ns, n cadrul recursivitii, condiia este verificat n cursul execuiei programului (nu la sfritul ei ca la iteraie) i n caz de rezultat nesatisfctor, ntreaga poriune de program este apelat din nou ca subprogram (procedur) a ei nsi, n particular ca procedur a poriunii de program originale care nc nu i-a terminat execuia. n momentul satisfacerii condiiei de revenire, se reia execuia programului apelant exact din punctul n care s-a apelat pe el nsui. Acest lucru este valabil pentru toate apelurile anterioare satisfacerii condiiei. (calcule, funcii sau structuri de date definite n termeni recursivi).

n contextul programrii, recursivitatea este strns legat de iteraie i pentru a nu da

Utilizarea algoritmilor recursivi este potrivit n situaiile care presupun recursivitate n general, un program recursiv P, poate fi exprimat prin mulimea P care const dintro serie de instruciuni fundamentale Si (care nu-l conin pe P) i P nsui. -----------------------------------------------------------P = P [Si,P] [5.1.b] ------------------------------------------------------------

Structurile de program necesare i suficiente pentru exprimarea recursivitii sunt


procedurile, subrutinele sau funciile care pot fi apelate prin nume. recursiv

Dac o procedur P conine o referin direct la ea nsi, se spune c este direct

Dac P conine o referin la o alt procedur Q care la rndul ei conine o referin


(direct sau indirect) la P, se spune c P este recursiv indirect (figura 5.1.a).

(a)

Procedura A . . . IF cond THEN A . . . END A

(b)

Procedure P . . . IF cond THEN Q . . . END P;

Procedure Q . . . IF cond THEN R . . . END Q;

Procedure R . . . IF cond THEN P . . . END R;

Fig.5.1.a. Recursivitate direct (a) i recursivitate indirect (b)

Procedure P . . . IF c THEN P . . . END P;

Procedure P . . . IF c THEN P . . . END P;

Procedure P . . . IF c THEN P . . . END P;

Procedure P . . . IF c THEN P . . . END;

Fig.5.1.b. Model de execuie al unei proceduri recursive

Intuitiv, execuia unei proceduri recursive este prezentat n figura 5.1.b. Fiecare reprezentare asociat procedurii reprezint o instan de apel recursiv
a acesteia. Instanele se apeleaz unele pe altele pn n momentul n care condiia c devine fals. n acest moment se revine pe lanul apelurilor n ordine invers, continund execuia fiecrei instane din punctul de ntrerupere pn la sfrit, dup cum indic sgeile. Suprapunnd imaginar toate aceste figuri peste una singur, se obine modelul de execuie al unei proceduri recursive.

De regul, unei proceduri i se asociaz un set de obiecte locale procedurii (variabile,

constante, tipuri i proceduri) care sunt definite local n procedur i care nu exist sau nu au neles n afara ei.

Mulimea acestor obiecte constituie aa numitul context al procedurii. De fiecare dat cnd o astfel de procedur este apelat recursiv, se creeaz un nou set
de astfel de variabile locale specifice apelului cu alte cuvinte un nou context specific apelului, care se salveaz n stiva sistem. anterioar a procedurii (n calitate de program apelant), ele au valori distincte i orice conflict de nume este evitat prin regula care stabilete domeniul de existen al identificatorilor.

Dei aceste variabile au acelai nume ca i cele corespunztoare lor din instana

Regula este urmtoarea: Identificatorii se refer ntotdeauna la setul cel mai


recent creat de variabile, adic la contextul aflat n vrful stivei. Aceai regul este valabil i pentru parametrii de apel ai procedurii care sunt asociai prin definiie setului de variabile.

Pe msur ce se realizeaz apeluri recursive, contextul fiecrui apel este salvat n

stiv, iar pe msur ce execuia unei instane s-a ncheiat, se restaureaz contextul instanei apelante prin eliminarea contextului aflat n vrful stivei.

Acest mecanism de implementare a recursivitii este ilustrat n figura 5.1.c.

PROCEDURE A(x: Tip1; y:Tip2); VAR u,w,z; BEGIN ... A(x,y); ... END A ;

... x y u w z retur x y u w z retur

etc...

apel 2

apel 1

Fig.5.1.c. Mecanism pentru implementarea recursivitii

n legtur cu acest mecanism se pot face cteva observaii:


1. n cadrul apelurilor recursive nu apar nici un fel de probleme referitoare la transmiterea parametrilor interinstane de apel, dac aceti parametri se transmit prin valoare, ei fiind salvai n stiv odat cu contextul. 2. n cazul parametrilor de tip VAR care se transmit prin adres, pentru a face posibil transmiterea valorilor acestor parametri ntre instanele de apel, este necesar declararea suplimentar a unor parametri locali, cte unul pentru fiecare parametru de tip VAR, care s asigure retransmiterea valorilor calculate interinstane. Aceti parametri trebuiesc reasignai cu valorile iniiale naintea revenirii din procedura recursiv [5.1.c].

-----------------------------------------------------------PROCEDURE A(x: Tip1; VAR y: Tip2; VAR z: Tip3); VAR u,w: TipX; y1: Tip2; {parametru local pentru y} z1: Tip3; {parametru local pentru z} BEGIN ... [5.1.c] A(x,y1,z1); ... y:= y1; {reasignare parametru iniial y} z:= z1; {reasignare parametru iniial z} END;{A} -----------------------------------------------------------3. n cazul parametrilor de tip referin trebuie inut cont de faptul c n urma apelului recursiv al unei proceduri se realizeaz automat i atribuirea parametrilor implicai n apel [5.1.d]. -----------------------------------------------------------PROCEDURE A(r: TipReferinta); BEGIN [5.1.d] ... A(r^.drept); {simultan cu apelul se realizeaz i atribuirea variabilei curente r} ... END;{A} ------------------------------------------------------------

Ca i n cazul structurilor repetitive, procedurile recursive necesit evaluarea unei

condiii de terminare, fr de care un apel recursiv conduce la o bucl de program infinit. care la un moment dat devine neadevrat.

Astfel, orice apel recursiv al procedurii P trebuie supus controlului unei condiii c n baza acestei observaii, un algoritm recursiv poate fi exprimat cu mai mare

acuratee prin una din formulele [5.1.e] sau [5.1.f]: -----------------------------------------------------------P IF c THEN P [Si,P] [5.1.e] -----------------------------------------------------------P P [Si,IF c THEN P] [5.1.f] ------------------------------------------------------------

Tehnica de baz n demonstrarea terminrii unei iteraii (repetiii) const n: (1) A defini o funcie f(x) (unde x este o variabil din program), astfel nct

condiia f(x)< 0 s implice satisfacerea condiiei de terminare a iteraiei (2) A demonstra c f(x) descrete pe parcursul execuiei iteraiei respective.

ntr-o manier similar, terminarea unui algoritm recursiv poate fi demonstrat,


dovedind c P descrete pe f(x). O modalitate particular uzual de a realiza acest lucru, este de a asocia lui P un parametru, spre exemplu n i de a apela recursiv pe P utiliznd pe n-1 ca i valoare de parametru. Dac se nlocuiete condiia c cu n > 0 , se garanteaz terminarea.

program [5.1.g,h]: ------------------------------------------------------------

Acest lucru se poate exprima formal cu ajutorul urmtoarelor scheme de

P(n) IF n > 0 THEN P [Si,P(n-1)] [5.1.g] -----------------------------------------------------------P(n) P [Si,IF n > 0 THEN P(n-1)] [5.1.h] ------------------------------------------------------------

De regul n aplicaiile practice trebuie demonstrat nu numai c adncimea


recursivitii este finit ci i faptul c ea este suficient de mic. memorie variabilelor sale curente (contextului procedurii).

Motivul este c fiecare apel recursiv al procedurii P, necesit alocarea unui volum de n plus, alturi de aceste variabile trebuie memorat i starea curent a programului, cu

scopul de a fi refcut, atunci cnd apelul curent al lui P se termin i urmeaz s fie reluat instana apelant. memorie alocat stivei sistem, greeal frecvent n contextul utilizrii procedurilor recursive.

Neglijarea acestor aspecte poate avea drept consecin depirea spaiului de

5.1.1. Exemple de programe recursive simple

Pentru exemplificare se furnizeaz n continuare trei exemple de proceduri recursive


simple.

Exemplul 5.1.1.a. Scopul programului furnizat n continuare ca exemplu, este acela


de a ilustra principiul de funcionare al unei proceduri recursive.

n cadrul programului se definete procedura recursiv Revers: Procedura Revers citete cte un caracter i l afieaz. n acest scop n cadrul procedurii se declar variabila local z:char. Procedura verific dac caracterul citit este blanc: n caz negativ procedura se autoapeleaz recursiv n caz afirmativ afieaz caracterul z [5.1.1.a].
-----------------------------------------------------------PROCEDURE Revers; VAR z: char; BEGIN [5.1.1.a] Read(z); IF z<>' ' THEN Revers; Write(z) END;{Revers} ------------------------------------------------------------

Execuia procedurii Revers conduce la afiarea caracterelor n ordinea n care sunt


citite pn la apriia primului blanc.

Fiecare autoapel presupune salvarea n stiva sistemului a contextului apelului care n


situaia de fa const doar din variabila local z.

Apariia primului caracter blanc presupune suprimarea apelurilor recursive ale

procedurii declannd continuarea execuiei procedurii, pn la terminarea sa pentru fiecare din apelurile anterioare.

n cazul de fa, aceasta presupune afiarea caracterului memorat n variabila z. Acest ir de reveniri va produce la nceput afiarea unui blanc, dup care sunt afiate
caracterele n ordinea invers a citirii lor.

Acest lucru se ntmpl deoarece fiecare terminare a execuiei procedurii determin


revenirea n apelul anterior, revenire care presupune reactualizarea contextului apelului. ordinii n care au fost memorate.

ntruct contextele sunt salvate n stiv, reactualizarea lor se face n sens invers De fapt pentru fiecare cuvnt introdus procedura Revers furnizeaz cuvntul n
cauz urmat de acelai cuvnt scris invers.

Exemplul 5.1.1.b. Se refer la procedura de traversare a unei liste simplu


nlnuite [5.1.1.b]. -----------------------------------------------------------TYPE TipLista = ^TipNod; TipNod = RECORD data: TipData; urm: TipLista END; PROCEDURE Traversare(p:TipLista); BEGIN IF p<>nil THEN BEGIN [1] Prelucrare(p^.data); [2] Traversare(p^.urm) END END;{Traversare} ------------------------------------------------------------

Algoritmul de traversare nu numai c este foarte simplu i foarte elegant, dar prin
simpla inversare a instruciunilor Prelucrare i Traversare ([1] respectiv [2]) se obine parcurgerea n sens invers a listei n cauz.

n acest ultim caz algoritmul se poate descrie astfel. Se traverseaz mai nti lista ncepnd cu poziia curent pn la sfritul listei; Dup ce s-a realizat aceast traversare se prelucreaz informaia din nodul curent; Consecina execuiei algoritmului: Informaia coninut ntr-o anumit poziie p este prelucrat numai dup ce
au fost prelucrate toate informaiile corespunztoare tuturor nodurilor care i urmeaz lui p.

Exemplul 5.1.1.c. Se refer la binecunoscuta problem a Turnurilor din Hanoi a


crei specificare este urmtoarea:

Se consider trei vergele A,B i C; Se consider de asemenea un set de n discuri gurite fiecare de alt
dimensiune, care sunt amplasate n ordine descresctoare, de jos n sus pe vergeaua A; Se cere s se mute discurile pe vergeaua C utiliznd ca i auxiliar vergeaua B; Se va respecta urmtoarea restricie: Nu se aeaz niciodat un disc mai mare peste un disc mai mic (figura 5.1.1.a).

..

Fig.5.1.1.a. Turnurile din Hanoi

Problema pare simpl, dar rezolvarea ei cere mult, rbdare, acuratee i un volum de
timp care crete exponenial odat cu n.

O abordare recursiv ns reprezint o soluie simpl i elegant. Rezolvarea recursiv a problemei presupune abordarea unui caz simplu, urmat de
generalizarea corespunztoare. Astfel pentru nceput se consider c exist doar dou discuri numerotate cu 1 (cel mai mic situat deasupra) respectiv cu 2 (cel mai mare), a cror mutare n condiiile impuse presupune urmtorii pai : Se mut discul 1 de pe A pe B; Se mut discul 2 de pe A pe C; Se mut discul 1 de pe B pe C. adic:

Prin generalizare se reia acelai algoritm pentru n discuri (figura 5.1.1.b),


Se mut n-1 discuri (cele de deasupra) de pe A pe B (prin C); Se mut discul n de pe A pe C; Se mut n-1 discuri de pe B pe C (prin A).

1
...

n-2 n -1 n

Fig.5.1.1.b. Generalizarea problemei Turnurilor din Hanoi

Pornind de la acest model, o variant imediat de implementare recursiv a rezolvrii


problemei este prezentat n secvena [5.1.1.a]. -----------------------------------------------------------PROCEDURE TurnuriHanoi(NrDiscuri:integer; A{dela}, B{la}, C{prin}: TipVergele); PROCEDURE MutaDisc(x{de la},y{la}: TipVergele); BEGIN * muta discul de la x la y END; {MutaDisc} BEGIN {TurnuriHanoi} IF NrDiscuri=1 THEN MutaDisc(A,C) [5.1.1.a] ELSE BEGIN TurnuriHanoi(NrDiscuri-1, A, B, C); MutaDisc(A,C); TurnuriHanoi(NrDiscuri-1, B, C, A) END {ELSE} END; {TurnuriHanoi} ------------------------------------------------------------

Procedura MutaDisc realizeaz efectiv mutarea unui disc de pe o vergea surs

pe o vergea destinaie. Procedura TurnuriHanoi realizeaz dou auto-apeluri recursive i un apel al procedurii MutaDisc conform modelului de generalizare prezentat mai sus.

5.2. Utilizarea recursi vitii


5.2.1. Cazul general de utilizare a recursivitii

Algoritmii recursivi sunt potrivii a fi utilizai atunci cnd problema care trebuie
rezolvat sau datele care trebuiesc prelucrate sunt definite n termeni recursivi. utilizarea unui algoritm recursiv reprezint cea mai bun alegere.

Cu toate acestea, un astfel de mod de definire nu justific ntotdeauna faptul c Mai mult, utilizarea recursivitii n anumite situaii nepotrivite, coroborat cu regia
relativ ridicat a implementrii i execuiei unor astfel de algoritmi, a generat n timp un curent de opinie potrivnic destul de vehement. un domeniu de aplicabilitate bine delimitat.

Cu toate acestea recursivitatea rmne o tehnic de programare fundamental cu Programele n care utilizarea recursivitii este recomandat se ncadreaz n modelul

[5.1.e,f]. Acest model este foarte potrivit n cazurile n care se cere calculul unor valori care se definesc cu ajutorul unor relaii recurente.

precizate n [5.2.1.a]. -----------------------------------------------------------i = 0,1,2,3,4,5,... fi=1,1,2,6,24,120,... [5.2.1.a] ------------------------------------------------------------

Un exemplu clasic n acest sens l reprezint numerele factoriale fi=i!

Elementul cu indicele 0 este furnizat n mod explicit ca fiind egal cu 1. Elementele urmtoare, sunt definite n mod recursiv, n termenii predecesorilor lor
[5.2.1.b]. -----------------------------------------------------------fi+1=(i+1)*fi [5.2.1.b] -----------------------------------------------------------respectiv valorile lui i i fi , n cel de-al i-lea nivel de recursivitate [5.2.1.c]. -----------------------------------------------------------i:= i+1; f:= i*f; [5.2.1.c] ------------------------------------------------------------

Pornind de la aceast formul, se introduc dou variabile i i f care precizeaz

nlocuind [5.2.1.c] n [5.1.e] se obine forma recursiv [5.2.1.d] creia i se poate


asocia programul principal [5.2.1.e]. -----------------------------------------------------------P IF i<n THEN (i:= i+1; f:= i*f; P [5.2.1.d] -----------------------------------------------------------i:= 0; f:= 1; P; [5.2.1.e] ------------------------------------------------------------

Varianta imediat de implementare a procedurii P apare n secvena [5.2.1.f].


-----------------------------------------------------------{Calculul factorialului Varianta 1} PROCEDURE P; BEGIN IF i<n THEN BEGIN [5.2.1.f] i:= i+1; f:= i*f; P END END;{P} ------------------------------------------------------------

Dup cum s-a precizat, algoritmii recursivi se recomand a fi utilizai cu precdere


pentru implementarea unor probleme care se pot defini n manier recursiv. nerecursiv.

Cu toate acestea, recursivitatea poate fi utilizat i n cazul unor probleme de natur Spre exemplu, n [5.2.1.g] se prezint un exemplu de implementare recursiv a unei
cutri ntr-un tablou liniar a. -----------------------------------------------------------{Cautare recursiva intr-un tablou liniar} VAR n: integer; x: TipElement; a: ARRAY[1..n] OF TipElement; FUNCTION Cauta(i: TipIndice): TipIndice; BEGIN

IF i>n THEN Cauta:= 0 ELSE [5.2.1.g] IF a[i]=x THEN Cauta:= i ELSE Cauta:= Cauta(i+1) END;{Cauta} {Apelul initial: Cauta(1)} ------------------------------------------------------------

Este evident faptul c o astfel de abordare este posibil, dar ea este oarecum forat
i ineficient, varianta iterativ fiind preferat n acest caz.

5.2.2. Algoritm recursiv pentru calculul factorialului

Procedura P prezentat n paragraful anterior este de regul nlocuit cu un

subprogram de tip funcie, Funcia poate fi utilizat direct ca i constituent al unei expresii Funciei i se poate asocia n mod explicit valoarea rezultat din calcule. Variabila f devine superflu, iar rolului lui i este preluat de ctre parametrul n de apel al funciei [5.2.2.a].

-----------------------------------------------------------{Funcie recursiv pentru calculul factorialului} FUNCTION Fact(n: integer): integer; BEGIN IF n=0 THEN Fact:= 1 [5.2.2.a] ELSE Fact:= n*Fact(n-1) END;{Fact} ------------------------------------------------------------

Secvena n cauz care a fost redactat pentru calculul factorialului ca i ntreg, este
limitat din punctul de vedere al dimensiunii maxime a lui n din raiuni de reprezentare n calculator a numerelor ntregi.

Trecerea la varianta real, eliberat de aceast constrngere este extrem de simpl. n figura 5.2.2.a apare reprezentarea intuitiv a apelurilor recursive ale funciei
Fact pentru n = 4. Fact(4) Fact(3) Fact(2) Fact(1) =4* =3* =2* =1* Fact(0) =1

Fig.5.2.2.a. Apelurile ale funciei recursive Fact pentru n = 4

n figura 5.2.2.b, se prezint intuitiv revenirile succesive din apelurile recursive ale
funciei Fact, care au drept urmare calculul efectiv al valorii factorialului.
Fact(4) Fact(3) Fact(2) =4* =3* =2* Fact(1) =1*1 =4* Fact(4) Fact(3) =3* Fact(2) =2*1

Fact(4) Fact(3) =3*2*1 Fact(4) =4*3*2*1

=4*

Fig.5.2.2.b. Rezolvarea apelurilor funciei recursive Fact pentru n = 4

n figura 5.2.2.c apare structura apelurilor respectiv a rezolvrii acestora n form de


arbore de apeluri.

Apeluri: 4! 4 * 3 3! * 2 2! * 1 1! *

Reveniri: 4! 4 * 3 3! * 2 0! 1 2! * 1 1! * 1 4 4! * 3 3! * 2 4 2! * 4! * 3 1 3! * 2 4 4! * 6 24

Fig.5.2.2.c. Arborele de apeluri al funciei Fact(4)

n acest caz fiind vorba despre despre o funcie cu un singur apel recursiv, arborele de
apeluri are o structur de list liniar.

Fiecare apel adaug un element la sfritul acestei liste, iar rezolvarea apelurilor are
drept consecin reducerea succesiv a listei de la sfrit spre nceput.

Dup cum se observ, pentru a calcula factorialul de ordinul n sunt necesare n+1
apeluri ale funciei recursive Fact.

Este ns evident c n cazul calculului factorialului, recursivitatea poate fi nlocuit


printr-o simpl iteraie [5.2.2.b]. -----------------------------------------------------------{Calculul factorialului - Varianta iterativ} VAR i,fact: integer; i:= 0; fact:= 1; WHILE i<n DO [5.2.2.b] BEGIN i:= i+1; fact:= fact*i END; ------------------------------------------------------------

n figura 5.2.2.d. apre reprezentarea grafic a profilului performanei algoritmului

de calcul al factorialului n variant recursiv respectiv iterativ. Sunt de fapt reprezentai n manier comparativ, timpii de execuie pe un acelai sistem de calcul, ai celor doi algoritmi funcie de valoarea lui n . Dup cum se observ, dei ambii algoritmi sunt liniari n raport cu n, adic au performana O(n) , algoritmul recursiv este de aproximativ 4 ori mai lent dect cel iterativ. Expresiile analitice ale celor dou reprezentri apar n [5.2.2.c][De84].
Timp [sec] 0.9 0.8 0.7 0.6 0.5 0.4 0.2 0.1 20 40 60 80 100 120 recursiv

iterativ

Fig.5.2.2.d. Profilul algoritmului factorial (variantele recursiv i iterativ)


----------------------------------------------------------------Ti(n)=0.0018*n+0.0033 [5.2.2.c] Tr(n)=0.0056*n+0.017 -------------------------------------------------------------------

5.2.3. Numerele lui Fibonacci

Exist i alte exemple de definiri recursive care se trateaz mult mai eficient cu
ajutorul iteraiei.

Un exemplu n acest sens l reprezint calculul numerelor lui Fibonacci de ordin 1


care sunt definite prin urmtoarea relaie recursiv [5.2.3.a]: -----------------------------------------------------------Fibn+1 = Fibn + Fibn-1 pentru n > 0 Fib1 = 1; Fib0 = 0 [5.2.3.a] ------------------------------------------------------------

Aceast definiie recursiv conduce imediat la urmtorul algoritm de calcul recursiv


[5.2.3.b]. -----------------------------------------------------------{Calculul numerelor lui Fibonacci - Varianta recursiv} FUNCTION Fib(n: integer): integer; BEGIN IF n=0 THEN Fib:= 0 ELSE [5.2.3.b] IF n=1 THEN Fib:= 1 ELSE Fib:= Fib(n-1) + Fib(n-2) END;{Fib} -----------------------------------------------------------de calcul, deoarece numrul de apeluri crete exponenial cu n .

Din pcate modelul recursiv utilizat n aceast situaie conduce la o manier ineficient n figura 5.2.3.a. apare reprezentarea arborelui de apeluri pentru n=5.

5 4 3 2 1 0 1 1 2 0 1 2 0 3 1

Fig.5.2.3.a. Arborele de apeluri al procedurii Fib pentru n = 5

Dup cum se observ: Este vorba despre un arbore binar (exist dou apeluri recursive ale
procedurii Fib), Parcurgerea arborelui de apeluri necesit 15 apeluri, fiecare nod semnificnd un apel al procedurii. Ineficiena acestei implementri crete odat cu creterea lui n.

Este evident faptul c numerele lui Fibonacci pot fi calculate cu ajutorul unei scheme

iterative care elimin recalcularea valorilor, utiliznd dou variabile auxiliare x = Fibi i y = Fibi-1 [5.2.3.c]. -------------------------------------------------------{Calculul numerelor lui Fibonacci - Varianta iterativ} i:= 1; x:= 1; y:= 0; WHILE i<n DO [5.2.3.c] BEGIN z:= x; i:= i+1; x:= x+y; y:= z END; --------------------------------------------------------

Variabila auxiliar z poate fi evitat, utiliznd atribuiri de forma x:= x + y i


y:= x- y .

5.2.4. Eliminarea recursivitii Recursivitatea reprezint o facilitate excelent de programare care contribuie la exprimarea simpl, concis i elegant a algoritmilor de natur recursiv. o Cu toate acestea, ori de cte ori problemele eficienei i performanei se pun cu preponderen, se recomand evitarea utilizrii recursivitii. o De asemenea se recomand evitarea recursivitii ori de cte ori st la dispoziie o rezolvare evident bazat pe iteraie. De fapt, implementarea recursivitii pe echipamente nerecursive dovedete faptul c orice algoritm recursiv poate fi transformat ntr-unul iterativ. n continuare se abordeaz teoretic problema conversiei unui algoritm recursiv ntrunul iterativ. n abordarea acestei chestiuni se disting dou cazuri: (1) Cazul n care apelul recursiv al procedurii apare la sfritul ei, drept ultim instruciune a procedurii ("tail recursion"). o n aceast situaie, recursivitatea poate fi nlocuit cu o bucl simpl de iteraie. o Acest lucru este posibil, deoarece n acest caz, revenirea dintr-un apel ncuibat presupune i terminarea instanei respective a procedurii, motiv pentru care contextul apelului nu mai trebuie restaurat. Astfel dac o procedur P(x) conine ca i ultim pas al su un apel la ea nsi de forma P(y), o Acest apel poate fi nlocuit cu o instruciune de atribuire x:=y , urmat de reluarea (salt la nceputul) codului lui P . o y poate fi chiar o expresie, o x ns n mod obligatoriu trebuie s fie o variabil transmisibil prin valoare, astfel nct valoarea ei s fie memorat ntr-o locaie specific apelului. o De asemenea x poate fi transmis prin referin dac y este chiar x . o Dac P are mai muli parametri, ei pot fi tratai fiecare n parte ca x i y. Aceast modificare este valabil deoarece reluarea execuiei lui P, cu noua valoare a lui x , are exact acelai efect ca i cnd s-ar apela P(y) i s-ar reveni din acest apel. n general programele recursive pot fi convertite formal n forme iterative dup cum urmeaz.

Forma recursiv [5.2.4.a] devine forma iterativ [5.2.4.b] iar [5.2.4.c] devine [5.2.4.d]. -----------------------------------------------------------P(x) P [Si,IF c THEN P(y)] [5.2.4.a] -----------------------------------------------------------P(x) P [Si,IF c THEN [x:=y; reluare P]] [5.2.4.b]

-----------------------------------------------------------P(n) P [Si,IF n>0 THEN P(n-1)] [5.2.4.c] -----------------------------------------------------------P(n) P [Si,IF n>0 THEN [n:=n-1; reluare P]] [5.2.4.d] ----------------------------------------------------------- Exemple concludente n acest sens sunt implementarea iterativ a calculului factorialului i a calculului irului numerelor lui Fibonacci prezentate anterior. (2) Cazul n care apelul sau apelurile recursive se realizeaz din interiorul procedurii [5.2.4.e]. o Varianta iterativ a acestei situaii presupune tratarea explicit de ctre programator a stivei apelurilor. o Fiecare apel necesit salvarea n stiva gestionat de ctre utilizator a contextului instanei de apel, o Acest context trebuie terminarea instanei. restaurat de ctre utilizaator din aceeai stiv la

-------------------------------------------------------P P [Si, IF c THEN P, Sj] [5.2.4.e] ------------------------------------------------------- Activitatea de conversie n acest caz are un nalt grad de specificitate funcie de algoritmul recursiv care se dorete a fi convertit i este n general dificil, laborioas i ngreuneaz mult nelegerea algoritmului. Un exemplu n acest sens l reprezint partiionarea nerecursiv prezentat n paragraful &3.2.6. Recursivitatea are ns domeniile ei bine definite n care se aplic cu succes. n general se apreciaz c algoritmii a cror natur este recursiv este indicat a fi formulai ca i proceduri recursive, lucru pus n eviden de algoritmii prezentai n cadrul acestui capitol i n capitolele urmtoare.

5.3. Exemple de algoritmi recursivi


5.3.1. Algoritmi care implementeaz definiii recursive

Exemplul 1. Algoritmul lui Euclid o Permite determinarea celui mai mare divizor comun a dou numere ntregi date. o Se definete n manier recursiv dup cum urmeaz:
Dac unul dintre numere este nul, c.m.m.d.c. al lor este cellalt numr; Dac nici unul din numere nu este nul, atunci c.m.m.d.c nu se modific

dac se nlocuiete unul din numere cu restul mpririi sale cu cellalt.

Pornind de la aceast definiie recursiv se poate concepe un subprogram simplu de tip funcie pentru calculul c.m.m.d.c. a dou numere ntregi [5.3.1.a]. -----------------------------------------------------------{Implementarea algoritmului lui Euclid} FUNCTION Cmmdc(m,n: integer): integer; BEGIN IF n=0 THEN Cmmdc:= m [5.3.1.a] ELSE Cmmdc:= Cmmdc(n, m MOD n) END;{Cmmdc} ----------------------------------------------------------- n figura 5.3.1.a apare urma execuiei acestui algoritm pentru valorile 18 i 27. Cmmdc(n, m MOD n) Cmmdc(27, 18 mod 27) Cmmdc(18, 27 mod 18) Cmmdc (9, 18 mod 9) n = 0 ; Cmmdc = 9

M 18 27 18 9

N 27 18 9 0

Fig.5.3.1.a. Urma execuiei algoritmului lui Euclid Exemplul 2. Algoritm pentru transformarea expresiilor aritmetice specificate n format infix n format postfix (notaie polonez). o Expresiile aritmetice uzuale (format infix) se definesc n mod recursiv cu ajutorul diagramelor sintactice prezentate n figura 5.3.1.b.
Expresie Termen + Termen Termen Factor * Factor / Factor Identificator ( Identificator litera Expresie )

Fig.5.3.1.b. Definirea recursiv a unei expresii aritmetice o Recursivitatea de natur indirect a acestei definiii const n faptul c definiia lui Factor se refer din nou la definiia lui Expresie.

o Se pune problema ca pornind de la o expresie aritmetic corect furnizat n format normal (infix) s se obin forma polonez (postfix) a acesteia. o Se precizeaz c notaia polonez presupune reprezentarea unei operaii aritmetice simple n forma "operand operand operator" i nu n forma "operand operator operand" presupus de notaia infix. o Spre exemplu a+b devine ab+ iar a+b*(c-d) devine abcd-*+ . o Avantajul notaiei poloneze este acela c indic ordinea corect a execuiei operaiilor fr a utiliza paranteze, iar expresia poate fi evaluat printr-o simpl baleere secvenial. Problema care trebuie rezolvat este aceea de a depista cei doi operanzi (care pot fi compleci), de a-i nregistra i de a trece operatorul dup ei. o Din acest motiv, pn la depistarea celui de-al doilea operand, operatorul aditiv din cadrul expresiei respectiv operatorul multiplicativ din cadrul unui termen, se memoreaz n variabilele locale op respectiv op1 . o Transformarea cerut se poate realiza construind cte o procedur individual de conversie pentru fiecare construcie sintactic (Expresie, Termen, Factor). o Deoarece fiecare dintre aceste construcii sintactice este definit recursiv, procedurile corespunztoare trebuiesc de asemenea activate recursiv. o Algoritmul de transformare apare n secvena [5.3.1.b]. -------------------------------------------------------{Transformarea unei expresii aritmetice din format infix n format postfix} TYPE linie = string[80]; VAR infix,post: linie; c: char; i: integer; {contor expresie curenta} j: integer; {contor postfix curent} PROCEDURE CitesteCar(ex: linie; VAR ch: char); {furnizeaz caracterul urmtor al expresiei infix, eliminnd blancurile} BEGIN REPEAT ch:= ex[i]; i:= i+1; UNTIL ch<>' ' END; {CitesteCar} PROCEDURE ScrieCar(VAR ex: linie, ch: char); {depune caracterul n zona precizat} BEGIN ex[j]:= ch; j:= j+1 END; {ScrieCar} PROCEDURE Expresie; VAR op: char; PROCEDURE Termen; VAR op1: char;

[5.3.1.b]

PROCEDURE Factor BEGIN {procedura Factor} IF c ='(' THEN BEGIN CitesteCar(infix,c); expresie {cnd se revine din expresie ch=')'} END ELSE BEGIN ScrieCar(post,c) END; CitesteCar(infix,c); END;{Factor} BEGIN {procedura Termen} Factor; WHILE (c ='*') OR (c ='/') DO BEGIN op1:= c; CitesteCar(infix,c); Factor; ScrieCar(post,op1) END {WHILE} END; {Termen} BEGIN {procedura Expresie} Termen; WHILE (c ='+') OR (c ='-') DO BEGIN op:= c; CitesteCar(infix,c); Termen; ScrieCar(post,op) END {WHILE} END; {Expresie} ------------------------------------------------------ n cadrul algoritmului se definesc: o Procedurile Expresie, Termen i Factor conform diagramelor din fig. 5.3.1.b o Variabile locale op i op1 utilizate pentru memorarea operatorilor aditivi respectiv multiplicativi. o Procedura CitesteCar care furnizeaz caracterul urmtor din textul de analizat eliminnd eventualele blancuri. o Variabila ch care n fiecare moment memoreaz caracterul furnizat de procedura CitesteCar . o Se consider c expresia n format infix ce urmeaz a fi convertit este depus ca ir de caractere n tabloul infix parcurs cu ajutorul indicelui i, o Forma postfix a expresiei se asambleaz n tabloul post utiliznd n acest scop indicele j. o Indicii i i j trebuiesc inializai n programul principal care apeleaz procedura Expresie . o Pentru funcionarea corect a procedurii de conversie, expresia de procesat trebuie precizat ntre paranteze.

5.3.2. Algoritmi de divizare Una dintre metodele fundamentale de proiectare a algoritmilor se bazeaz pe tehnica divizrii ("devide and conquer"). Principiul de baz al acestei tehnici este acela de a descompune (divide) o problem complex n mai multe subprobleme a cror rezolvare este mai simpl i din soluiile crora se poate asambla simplu soluia problemei iniiale. Acest mod de lucru se repet recursiv pn cnd subproblemele devin banale iar soluiile lor evidente. O aplicaie tipic a tehnicii divizrii are structura recursiv prezentat n [5.3.2.a].

-----------------------------------------------------------{Tehnica divizrii soluia recursiv} PROCEDURE Rezolva(x); BEGIN IF *x este divizibil n subprobleme THEN BEGIN [5.3.2.a] *divide pe x n dou sau mai multe pri: x1,x2,..,xk; Rezolva(x1); Rezolva(x2),...; Rezolva(xk); *combin cele k soluii pariale ntr-o soluie pentru x END{IF} ELSE *rezolv pe x direct END;{Rezolva} ----------------------------------------------------------- Dac recombinarea soluiilor pariale este substanial mai simpl dect rezolvarea ntregii probleme, aceast tehnic conduce la proiectarea unor algoritmi ntr-adevr eficieni. Datorit celor k apeluri recursive, arborele de apeluri asociat procedurii Rezolv este de ordinul k . Analiza algoritmilor de divizare. o Se presupune c timpul de execuie al rezolvrii problemei de dimensiune n este T(n) . o n condiiile n care prin divizri succesive problema de rezolvat devine suficient de redus ca dimensiune, se poate considera c pentru n c (c constant), determinarea soluiei necesit un timp de execuie constant, adic (1). o Se noteaz cu D(n) timpul necesar divizrii problemei n subprobleme i cu C(n) timpul necesar combinrii soluiilor pariale. o Dac problema iniial se divide n k subprobleme, fiecare dintre ele de dimensiune 1/b din dimensiunea problemei originale, se obine urmtoarea formul recurent [5.3.2.b].

-----------------------------------------------------------dac n c (1) T(n) = [5.3.2.b] k T(n/b) + D(n) + C(n) pentru n > c ----------------------------------------------------------- n continuare se prezint cteva aplicaii ale acestei tehnici. ----------------------------------------------------------- Exemplul 5.3.2.a. o Un prim exemplu de algoritm de divizare l constituie metoda de sortare Quicksort (&.3.2.6). o n acest caz problema se divide de fiecare dat n dou subprobleme, rezultnd un arbore binar de apeluri. o Combinarea soluiilor pariale nu este necesar deoarece scopul este atins prin modificrile care se realizeaz chiar de ctre rezolvrile pariale. ----------------------------------------------------------- Exemplul 5.3.2.b. Algoritm pentru determinarea extremelor valorilor componentelor unui vector. o n acest scop, poate fi proiectat o procedur Domeniu(a,i,j,mic,mare) care atribuie parametrilor mic i mare elementul minim respectiv maxim al vectorului a din domeniul delimitat de indicii i i j (a[i]..a[j]). o O implementare iterativ evident a procedurii apare n secvena [5.3.2.c] sub denumirea de DomeniuIt. -----------------------------------------------------------{Algoritm pentru deteterminarea extremelor unui vector soluia iterativ} PROCEDURE DomeniuIt(a: TipTablou; i,j: integer; VAR mic,mare: integer); VAR k:integer; BEGIN mic:= a[i]; mare:= a[i]; FOR k:=i+1 TO j DO [5.3.2.c] BEGIN IF a[k]>mare THEN mare:= a[k]; IF a[k]<mic THEN mic:= a[k] END END; {DomeniuIt} ----------------------------------------------------------- Procedura baleeaz ntregul vector comparnd fiecare element cu cel mai mare respectiv cel mai mic element pn la momentul curent. Este uor de vzut c costul procedurii Domeniu n termenii numrului de comparaii dintre elementele tabloului este 2*n-2 pentru un vector cu n elemente. Este de asemenea de observat faptul c fiecare element al lui a se va compara de dou ori; odat pentru aflarea maximului, iar a doua oar pentru aflarea minimului.

Acest algoritm nu ine cont de faptul c orice element luat n considerare drept candidat pentru minim (care apare n succesiunea de valori a lui mic) nu poate fi niciodat candidat pentru maxim, exceptnd condiia de iniializare i reciproc. Astfel algoritmul risipete un efort considerabil examinnd fiecare element de dou ori. Aceast risip se poate evita.

Soluia recursiv: o Aplicnd tehnica divizrii, se mparte tabloul n dou pri o Se compar valorile minime i maxime ale subtablourilor rezultate, stabilindu-se minimul i maximul absolut. o n continuare se procedeaz n aceeai manier reducnd de fiecare dat la jumtate dimensiunea subtablourilor. o Pentru dimensiuni 1 sau 2 ale subtablourilor soluia este evident.

o O ilustrare a acestei tehnici apare n procedura recursiv Domeniu [5.3.2.d]. -----------------------------------------------------------{Algoritm pentru deteterminarea extremelor unui vector soluia recursiv bazat pe tehnica divizrii} CONST n = ...; TYPE TipTablou = ARRAY[1..n] OF integer; VAR a: TipTablou; PROCEDURE Domeniu(a: TipTablou; i,j: integer; VAR mic,mare: integer); VAR mijloc,mic1,mare1,mic2,mare2: integer; BEGIN IF j<=i+1 THEN {tablou de dimensiune 1 sau 2} BEGIN IF a[i]<a[j] THEN BEGIN mic:= a[i]; mare:= a[j] END ELSE BEGIN [5.3.2.d] mic:= a[j]; mare:= a[i] END END{IF} ELSE BEGIN mijloc:= (i+j) DIV 2; Domeniu(a,i,mijloc,mic1,mare1); Domeniu(a,mijloc+1,j,mic2,mare2); IF mare1>mare2 THEN mare:= mare1 ELSE mare:= mare2; IF mic1<mic2 THEN mic:= mic1 ELSE mic:= mic2 END{ELSE} END;{Domeniu} ------------------------------------------------------------

mijloc mijloc + 1

mic1, mare1 mic i

mic2, mare2 mare j (j=i+1)

2 i j (j<i+1)

Fig.5.3.2.a. Funcionarea de principiu a procedurii Domeniu. Se constat analogia evident cu structura de principiu prezentat n secvena [5.3.2.a]. n figura 5.3.2.a. apare reprezentarea grafic a modului de lucru al procedurii Domeniu. Urma execuiei procedurii pentru n = 10 apare n figura 5.3.2.b, o n figur apar precizate limitele domeniilor pentru fiecare dintre apelurile procedurii. o n chenar sunt precizate domeniile de lungime 1 sau 2 care presupun comparaii directe. o Dup cum se observ, arborele de apeluri este un arbore binar deoarece se execut dou apeluri recursive din corpul procedurii. Dac se realizeaz o analiz mai aprofundat a modului de implementare se constat c: o Contextele apelurilor se salveaz n stiva sistem n ordinea rezultat de parcurgerea n preordine a arborelui de apeluri o Contextele se extrag din stiv conform parcurgerii n postordine a acestuia. o Restaurarea contextului fiecrui apel face posibil parcurgerea ordonat a tuturor posibilitilor evideniate de arborele de apeluri.
(1,10)

(1,5)

(6,10)

(1,3)

4,5

(6,8)

9,10

1,2

3,3

6,7

8,8

Fig.5.3.2.b. Arbore de apeluri al procedurii Domeniu pentru n = 10 Per ansamblu regia unui astfel de algoritm recursiv este mai ridicat dect a celui iterativ (sunt necesare 11 apeluri ale procedurii, pentru n = 10), Totui, autorii demonstreaz c din punctul de vedere al costului calculat relativ la numrul de comparaii ale elementelor tabloului, el este mai eficient dect algoritmul iterativ. o Pornind de la observaia c procedura include comparaii directe numai pentru subtablourile scurte (de lungime 1 sau 2) o i c pentru cele mai lungi ea procedeaz la divizarea intervalului comparnd numai extremele acestora, o n [AH85] se indic o valoare aproximativ pentru cost (numr de comparaii directe) egal cu (3/2)n-2 unde n este dimensiunea tabloului analizat. Analiza algoritmului Domeniu se poate realiza prin particularizarea relaiei [5.3.2.b]. Se observ imediat c k = 2, b = 2, iar D(n) i C(n) sunt ambele (1).

n consecin rezult ecuaia [5.3.2.e] a crei soluie apare n [5.2.3.f]. -----------------------------------------------------------daca n 2 (1) T(n) = [5.3.2.e] 2 T(n/2) + 2 daca n > 2 -----------------------------------------------------------[5.3.2.f] (T(n))= O(n) ------------------------------------------------------------

5.3.3. Algoritmi de reducere O alt categorie de algoritmi care se preteaz pentru o abordare recursiv o reprezint algoritmii de reducere. o Aceti algoritmi se bazeaz pe reducerea gradului de dificultate al problemei, pas cu pas, pn n momentul n care aceasta devine banal. o n continuare se revine n manier recursiv i se asambleaz soluia integral. n aceast categorie pot fi ncadrai algoritmul pentru calculul factorialului i algoritmul pentru rezolvarea problemei Turnurilor din Hanoi. Se atrage atenia c spre deosebire de algoritmii cu revenire (&5.4), n acest caz nu se pune problema revenirii n caz de nereuit, element care ncadreaz acest tip de algoritmi ntr-o categorie separat.

n continuare se prezint un exemplu de algoritm de reducere. ------------------------------------------------------------

Exemplul 5.3.3.a. Algoritm pentru determinarea permutrilor primelor n numere naturale. o Se d o secven de numere naturale, o Pentru determinarea permutrilor se poate utiliza tehnica reducerii. o Principiul este urmtorul: Pentru a obine permutrile de n elemente este suficient s se fixeze pe rnd cte un element i s se permute toate celelalte n -1 elemente. Procednd recursiv n aceast manier se ajunge la permutri de 1 element care sunt banale.

Aceast tehnic este implementat de procedura Permut(k) care realizeaz permutarea primelor k numere naturale. o Numerele se presupun memorate n tabloul a[i](i=1,2,...,n). o Dac k 1, atunci fiecare dintre elementele tabloului situate pe poziii inferioare lui k sunt aduse (fixate) pe poziia k prin interschimbarea poziiilor i i k ale tabloului a n cadrul unei bucle FOR pentru i=1,2,...,k. o Iniial k ia valoarea n pentru permutarea a n numere. o Pentru fiecare schimbare (fixare) se apeleaz recursiv rutina cu parametrul k-1, dup care se reface starea iniial relund n sens invers interschimbarea poziiilor i i k. o Acest lucru este necesar, deoarece fixarea elementului urmtor presupune refacerea strii iniiale n vederea parcurgerii n mod ordonat, pentru fiecare element n parte a tuturor posibilitilor. o n momentul n care k = 1, se consider terminat un apel i se afieaz tabloul a care conine o permutare. o Aceasta este condiia care limiteaz adncimea apelurilor recursive determinnd revenirea n apelurile anterioare.

Schia de principiu a acestui algoritm apare n [5.3.3.a.] iar o variant de rafinare Pascal apare n [5.3.3.b]. -----------------------------------------------------------{Schia de principiu a algoritmului pentru determinarea permutrilor a n numere naturale} Procedura Permuta(k:integer); dac k=1 atunci *afieaz tabloul (s-a finalizat o permutare) altfel pentru i=1 la k execut [5.3.3.a] *interschimba pe a[i] cu a[k]; Permuta(k-1); {apel recursiv} *interschimba pe a[i] cu a[k] {refacere situatie}

-----------------------------------------------------------{Exemplu de implementare a algoritmului pentru determinarea permutrilor a n numere naturale} PROCEDURE Permuta(k: integer); VAR i,x: integer; BEGIN

IF k=1 THEN afieaz ELSE BEGIN FOR i:=1 TO k DO BEGIN [5.3.3.b] x:= a[i]; a[i]:= a[k]; a[k]:= x; Permuta(k-1); x:= a[i]; a[i]:= a[k]; a[k]:= x END END END;{Permuta} ----------------------------------------------------------- n figura 5.3.3.a este reprezentat arborele de apeluri al procedurii Permuta(4) pentru permutrile obinute prin fixarea primului element
1 2 3 4

i=1
4 2

k=4
3 1

i=2 k=4
1 4 3 2

i=3 k=4
1 2 4 3

i=4 k=4
1 2 3 4

...
i=1 k=3 3 2 4 1 i=2 k=3 4 3 2 1 4

i=3 k=3 2 3 1

i=1 k=2 2 3 4 1 3

i=2 k=2 2 4 1

i=1 k=2 3 4 2 1

i=2 k=2 4 3 2 1 2

i=1 k=2 4 3 1

i=2 k=2 4 2 3 1

k=1 2 3 4 1 3 2

k=1 4 1 3 4

k=1 2 1 4

k=1 3 2 1 2

k=1 4 3 1 4

k=1 2 3 1

Fig.5.3.3.a. Arborele de apeluri pentru Permuta(4) Structura arborelui apelurilor este mai complicat datorit faptului c apelurile recursive se realizeaz dintr-o bucl FOR cu o limit (k) care descrete odat cu creterea nivelului arborelui. nlimea arborelui de apeluri este egal cu n.

5.3.4. Algoritmi recursivi pentru determinarea tuturor soluiilor unor probleme Algoritmii recursivi au proprietatea de a putea evidenia n mod ordonat toate posibilitile referitoare la o situaie dat. Se prezint n acest sens dou exemple o Primul exemplu reliefeaz proprietatea (exemplul 5.3.4.a). o Cel de-al doilea exemplu selecteaz din mulimea acestor posibiliti, cele care prezint interes (exemplul 5.3.4.b).

----------------------------------------------------------- Exemplul 5.3.4.a. Algoritm pentru evidenierea tuturor posibilitilor de secionare a unui fir de lungime ntreag dat (n) n pri de lungime 1 sau 2.

Acest algoritm se bazeaz pe urmtoarea tehnic de lucru:


modurile posibile;

pentru lungimea n > 1 exist dou posibiliti: se taie o parte de lungime 1 i restul lungimii (n-1) se secioneaz n toate se taie o parte de lungime 2 i restul lungimii (n-2)se secioneaz n toate

modurile posibile. pentru n = 1 avem cazul banal al unei tieturi de lungime 1. pentru n = 0 nu exist nici o posibilitate de tietur.

Schia de principiu a acestui algoritm apare n secvena [5.3.4.a].


-----------------------------------------------------------{Schita de principiu a algoritmului de tiere a firului} Procedura Taie(lungimeFir); dac lungimeFir>1 atunci *se taie o bucat de lungime 1; Taie(lungimeFir-1); *se taie o bucat de lungime 2; Taie(lungimeFir-2) *se anuleaz tietura altfel dac lungimeFir=1 atunci *se taie bucata de lungime 1; *afiare ------------------------------------------------------------

[5.3.4.a]

n secvena [5.3.4.b] se prezint o variant de implementare Pascal. Procedura Taie implementeaz tehnica de lucru anterior prezentat cu
precizarea c la atingerea dimensiunii n = 1 sau n = 0 se consider procedura terminat i se afieaz secvena de tieturi generat. Tieturile sunt reprezentate grafic utiliznd caracterul '.' pentru segmentul de lungime 1 i caracterul '_' pentru segmentul de lungime 2. Pentru memorarea tieturilor se utilizeaz un tablou de caractere z, parcurs cu ajutorul indicelui k, n care se depun caracterele corespunztoare tieturilor.

-----------------------------------------------------------{Variant de implementare a algoritmului de tiere a firului} VAR i,k,x: integer; z: ARRAY[1..9] OF char; PROCEDURE Taie(lungimeFir: integer); BEGIN IF lungimeFir>1 THEN BEGIN k:= k+1; z[k]:= '.'; Taie(lungimeFir-1); z[k]:= '_'; Taie(lungimeFir-2); k:= k-1{anulare taietura} END{IF} ELSE BEGIN [5.3.4.b] Write(' '); FOR i:= 1 TO k DO Write(z[i]); IF lungimeFir=1 THEN Write('.'); Writeln END{ELSE} END;{Taie} ------------------------------------------------------------

n figura 5.3.4.a apare rezultatul apelului procedurii Taie pentru n = 4 i n = 5. Din aceast figur se poate deduce uor maniera de execuie a procedurii
(urma execuiei) lund n considerare faptul c n reprezentarea grafic: Caracterul '.' reprezint apelul procedurii Taie pentru n-1, Cracterul '_' reprezint apelul procedurii pentru n - 2. lungimeFir = 4 .... .._ ._. _.. _ _ lungimeFir = 5 ..... ..._ .._. ._.. ._ _ _... _._ _ _.

Fig.5.3.4.a. Execuia procedurii Taie pentru n = 4 i n = 5

n figura5.3.4.b se prezint arborele de apeluri al procedurii Taie pentru n = 4 . Se observ c n fiecare nod al acestui arbore sunt precizate: Succesiunea bucilor tiate; Apelul recursiv care se realizeaz;

Valoarea curent a lui k; n chenar ngroat sunt ncadrate secvenele care se afieaz.
Taie(4) (1) (2)

.
k=1 Taie(3) (1) (2) (1)

k=2 Taie(2) (2)

. .
(1) k=2 Taie(2) (2) . . -

- .
k=2 Taie(1)

- k=2 Taie(0)

k=2 Taie(1)

. . .

- .

- .

- -

k=3 Taie(1) k=3 Taie(0)

. .

Fig.5.3.4.b. Arborele de apeluri al procedurii Taie(4) ----------------------------------------------------------- Exemplul 5.3.4.b. Algoritm pentru determinarea tuturor soluiilor de ieire dintrun labirint.

Algoritmul care va fi prezentat n continuare presupune un labirint Labirintul este descris cu ajutorul unui tablou bidimensional de caractere de
dimensiuni (n+1)*(n+1) n tablou, cu ajutorul caracterului '*' sunt reprezentai pereii, Cu ajutorul caracterului ' ' sunt reprezentate culoarele. Punctul de start este centrul labirintului,

Drumul de ieire se cut cu ajutorul unei proceduri recursive Cauta(x,y) unde x

i y sunt coordonatele locului curent n labirint i n acelai timp indici ai tabloului care materializeaz labirintul.

Cutarea se execut astfel: Dac valoarea locului curent este ' ' Se intr pe un posibil traseu de ieire Se marcheaz locul cu caracterul '#'. Dac s-a ajuns la margine rezult c s-a gsit un drum de ieire din labirint
i se execut afiarea tabloului bidimensional (labirintul i drumul gsit).

Dac valoarea locului curent nu este ' '

Se apeleaz recursiv procedura Cauta pentru cele patru puncte din

vecintatea imediat a locului curent, dup direciile axelor de coordonate (dreapta, sus, stnga, jos).

Pentru fiecare cutare reuit traseul apare marcat cu '# '. Marcarea asigur nu numai memorarea drumului de ieire dar n acelai timp nltur
reparcurgerea unui drum deja ales.

Reluarea parcurgerii aceluiai drum poate conduce la un ciclu infinit. Este important sublinierea faptului c marcajul de drum se terge de ndat ce s-a
ajuns ntr-o fundtur sau dac s-a gsit o ieire, n ambele situaii revenindu-se pe drumul parcurs pn la proximul punct care permite selecia unui nou drum. prsirea procedurii. Aceasta corespunde practic nfurrii "firului parcurgerii" conform metodei "firului Ariadnei". [5.3.4.d] o variant de implementare Pascal.

tergerea se execut prin generarea unui caracter ' ' pe poziia curent nainte de

n secvena [5.3.4.c] este prezentat schia de principiu a procedurii Caut iar n


-----------------------------------------------------------{Schita de principiu a algoritmului de cutare a drumului de ieire dintr-un labirint} Procedura Cauta(x,y: coordonate); dac *locul este liber atunci [5.3.4.c] *marcheaza locul dac *s-a ajuns la ieire atunci *afieaz drumul altfel Cauta(x+1,y); {dreapta} Cauta(x,y+1); {sus} Cauta(x-1,y); {stnga} Cauta(x,y-1) {jos} *terge marcajul -----------------------------------------------------------{Variant de implementare a algoritmului de cutare a drumului de ieire dintr-un labirint} PROCEDURE Cauta(x,y: integer); BEGIN IF m[x,y]=' ' THEN BEGIN [5.3.4.d] m[x,y]:= '#'; IF ((x MOD n)=0)OR((y MOD n)=0) THEN *afieaz ELSE BEGIN Cauta(x+1,y); Cauta(x,y+1); Cauta(x-1,y); Cauta(x,y-1) END; m[x,y]:= ' ' END

END;{Cauta} --------------------------------------------------------

Procedura recursiv Cauta materializeaz metoda expus anterior. Dac n timpul cutrii se atinge una din extremele valorilor abscisei sau ordonatei (0
sau n), s-a gsit o ieire din labirint i se realizeaz afiarea.

n figura 5.3.4.c se prezint un exemplu de execuie al acestei proceduri.


******* ******* * * * * * ** ** * *** * * * * * * * * ******* * * * * * *** * * * * *** * * * * * * * *** ***** * * * * * * * * ********* * * * * * * * * ******* * * * * * * ******* ******* ******* ******* *####* * * *#**#** * *** * *#*## * * * *#*#******* * * *#*#*#####*** * *#*#*#***#* * * ##*#*###*# * ***#*****#* * * * #######* * * * ********* * * * * * * * * ******* * * * * * * ******* ******* ******* ******* * * * * * ** ** * *** * * * * * * * * ******* * * * * *#####*** * * * *#***#* * * * *###*###* *** ***** *#* * * *#* * * *********#* * * *#########* * * *#******* * * * *#####* * *******#*******

Fig.5.3.4.c. Exemplu de execuie al procedurii Cauta ------------------------------------------------------------

5.3.5. Algoritmi pentru trasarea unor curbe recursive

Curbele recursive reprezint un alt domeniu de aplicaie al algoritmilor recursivi. Astfel de curbe pot fi generate cu ajutorul unui sistem de calcul, ntruct ele presupun
un numr redus de module care se mbin succesiv, conform unor reguli simple, bine precizate. generarea unor astfel de curbe.

Scopul acestui paragraf este acela de a prezenta doi algoritmi recursivi care permit Se face precizarea c studiul curbelor recursive care aparent nu are o aplicabilitate

practic imediat, contribuie n mod esenial la fundamentarea, consolidarea i nelegerea conceptului de recursivitate.

----------------------------------------------------------- Exemplul 5.3.5.a. Algoritm recursiv pentru generarea curbelor lui Hilbert.

n figura 5.3.5.a apar reprezentate patru curbe notate H0 , H1 , H2 i H3 care


reprezint curbele lui Hilbert de ordin 0, 1, 2 i 3.

Din figur se observ c o curb Hilbert Hi+1 Se obine prin compunerea a patru instane de curbe Hi , Cele 4 instane au dimensiunea njumtit, Ele sunt rotite n mod corespunztor, Instanele sunt legate ntre ele prin trei linii de legtur (prezentate mai
ngroat).

(A 0)

(A 1)

(A 2)

(A 3)

H0

H1

H2

H3

Fig.5.3.5.a. Curbele lui Hilbert de ordin 0, 1, 2 i 3

Se observ c curba H1 poate fi considerat ca i constnd din 4 instane al curbei vide


H0 , unite prin aceleai linii de legtur.

Acestea sunt curbele Hilbert datnd din 1891. Se presupune c exist disponibil o bibliotec grafic care dispune de procedurile: MoveTo(x,y) care poziioneaz cursorul grafic n punctul de coordonate x,y; LineTo(x,y) care traseaz o linie dreapt ntre poziia curent i cea indicat
prin x,y.

n continuare ne propunem s concepem un program simplu pentru reprezentarea

grafic a curbelor Hilbert de diferite ordine. Deoarece fiecare curb Hi const din patru copii njumtite Hi-1 , este normal ca procedura care-l deseneaz pe Hi s fie compus din patru pri, fiecare desennd cte o curb Hi-1 rotit corespunztor i la dimensiunea cerut. Cele patru pri sunt denumite A, B, C i D, iar rutinele care traseaz liniile sunt reprezentate prin sgei indicnd direcia de trasare. Prin generalizare se deduc cele patru modele recursive care sunt prezentate n figura 5.3.5.b.

A B C A B C D D

A A

D B

B C

B A

D B

C C

C D

A D

Fig.5.3.5.b. Modele recursive pentru curbele lui Hilbert

Pornind de la aceste modele recursive se deduc imediat schemele recursive care

permit generarea curbelor lui Hilbert [5.3.5.a]. -----------------------------------------------------------A:D A A B B:C B B A [5.3.5.a] C:B C C D D:A D D C ----------------------------------------------------------- Dac lungimea unitii de linie se noteaz cu h , procedura care corespunde modelului A poate fi redactat utiliznd apeluri recursive la ea nsi precum i la procedurile D i B realizate n aceeai manier [5.3.5.b]. -----------------------------------------------------------{Procedura pentru trasarea recursiv a modelului A al unei curbe Hilbert} PROCEDURE A(i: integer); BEGIN IF i>0 THEN BEGIN D(i-1); x:= x-h; LineTo(x,y); A(i-1); y:= y-h; LineTo(x,y); [5.3.5.b] A(i-1); x:= x+h; LineTo(x,y); B(i-1) END END;{A} ----------------------------------------------------------- Procedura este iniializat n programul principal pentru fiecare curb Hilbert care va fi supraimprimat.

Se consider c pe acelai desen se suprapun curbe Hilbert de mai multe ordine


succesive.

Programul principal determin punctul iniial de start al fiecrei curbe i incrementul


specific h .

Variabila h0 care precizeaz dimensiunea total a paginii trebuie s aib valoarea 2k


unde k > n , n fiind ordinul maxim al curbelor care supraimprim. [5.3.5.c].

Programul integral care traseaz curbele Hilbert H1 , H2 , ..., Hn apare n secvena


-----------------------------------------------------------{Program pentru trasarea recursiv a unor curbe Hilbert de diferite ordine} PROGRAM Hilbert; {deseneaz curbele hilbert de ordin 1,2,...,n} CONST n=4; h0=512; VAR i,h,x,y,x0,y0: integer; PROCEDURE B(i: integer); FORWARD; PROCEDURE C(i: integer); FORWARD; PROCEDURE D(i: integer); FORWARD; PROCEDURE A(i: integer); BEGIN IF i>0 THEN D(i-1); x:= x-h; LineTo(x,y); A(i-1); y:= y-h; LineTo(x,y); A(i-1); x:= x+h; LineTo(x,y); B(i-1) END END;{A} PROCEDURE B(i: integer); BEGIN IF i>0 THEN BEGIN C(i-1); y:= y+h; LineTo(x,y); B(i-1); x:= x+h; LineTo(x,y); B(i-1); y:= y-h; LineTo(x,y); A(i-1) END END;{B} PROCEDURE C(i: integer); BEGIN IF i>0 THEN BEGIN B(i-1); x:= x+h; LineTo(x,y); C(i-1); y:= y+h; LineTo(x,y); C(i-1); x:= x-h; LineTo(x,y); D(i-1) END END;{C} PROCEDURE D(i: integer); BEGIN IF i>0 THEN BEGIN A(i-1); y:= y-h; LineTo(x,y);

[5.3.5.c]

D(i-1); x:= x-h; LineTo(x,y); D(i-1); y:= y+h; LineTo(x,y); C(i-1) END END;{D} BEGIN {programul pricipal} i:= 0; h:= h0; x0:= h DIV 2; y0:= x0; REPEAT {traseaz curba hilbert de ordin i} i:= i+1; h:= h DIV 2; x0:= x0+(h DIV 2); y0:= y0+(h DIV 2); x:= x0; y:= y0; MoveTo(x,y); A(i) UNTIL i=n; END. ----------------------------------------------------------- Exemplul 5.3.5.b. Algoritm recursiv pentru trasarea curbelor lui Sierpinski.

Un exemplu mai complicat l reprezint curbele lui Sierpinski care apar n figura
5.3.5.c (S1 i S2).
A1

A2

D1

B1

C1 (S1)

D2

B2

C2
(S2)

Fig.5.3.5.c. Modele recursive pentru curbele lui Sierpinski de ordin 1 i 2

Diferena dintre curbele lui Sierpinski i cele ale lui Hilbert este aceea c primele sunt

curbe nchise. Aceasta implic faptul c modelul recursiv de baz trebuie s fie o curb deschis Cele patru pri ale sale sunt conectate prin linii de legtur care nu aparin modelului recursiv propriu-zis. Aceste legturi constau n patru linii drepte situate n colurile figurii i care sunt reprezentate ngroat n desen. Din aceast reprezentare rezult clar modul de asamblare al curbelor de ordin inferior pentru a obine o curb de ordin superior. curbelor lui Sierpinski.

Pornind de la aceste observaii, pot fi construite cele patru modelele generice ale

Ele sunt denumite A, B, C i D iar liniile de legtur sunt precizate explicit prin sgei
(fig.5.3.5.d).
----------------------------------------------------------------S: A B C D [5.3.5.d] ----------------------------------------------------------------A D C B: B A: A B D A C C: C A B D B D A A D: D C B

Fig.5.3.5.d. Modelele recursive ale curbelor lui Sierpinski

Cele patru modele grafice recursive sunt identice exceptnd faptul c sunt rotite cu
90 fiecare.

Este foarte important de subliniat faptul c cele patru modele se asambleaz prin
intermediul unui model de baz reprezentat n figur deasupra.

Din aceste modele pot fi deduse simplu schemele recursive care permit generarea
curbelor.

n secvena [5.3.5.e] apar schemele recursive ale curbelor componente, unde o sgeat
dubl indic o linie de lungime dubl.
----------------------------------------------------------------A : A B D A B : B C A B [5.3.5.e] C : C D B C D : D A C D -----------------------------------------------------------------

Considernd aceleai faciliti de reprezentare ca i n cazul anterior pot fi redactate cu


uurin rutinele recursive care traseaz curbele lui Sierpinski. precizat n [5.3.5.e] la fel i procedurile B,C i D.

Astfel, procedura A poate fi construit pornind de la prima linie a modelului recursiv n secvena [5.3.5.f ] apare un model de implementare pentru procedura A .
-----------------------------------------------------------{Exemplu de implementare a modelului A pentru o curba Sierpinski}

PROCEDURE A(i: integer); BEGIN IF i>0 THEN [5.3.5.f] BEGIN A(i-1); x:= x+h; y:= y-h; LineTo(x,y); B(i-1); x:= x+2*h; LineTo(x,y); D(i-1); x:= x+h; y:= y+h; LineTo(x,y); A(i-1) END
END;{A} -----------------------------------------------------------------

Programul principal este conceput conform modelului [5.3.5.d]. Sarcina sa este aceea de a fixa valorile coordonatelor iniiale conform cu dimensiunea
zonei de afiare.

Programul integral apare n [5.3.5.g]. Elegana i oportunitatea utilizrii recursivitii n aceast situaie este evident. Corectitudinea programului poate fi uor dedus din structura sa i din forma
modelelor. Limitarea adncimii recursivitii s-a realizat cu ajutorul parametrului i, garantnd n acest mod terminarea programului. Utilizarea iteraiei n aceste situaii conduce la programe complicate i greoaie.

-----------------------------------------------------------------

{Program pentru trasarea curbelor Sierpinski de diferite ordine}


PROGRAM Sierpinski; {traseaz curbele Sierpinski de ordin CONST n=4; h0=512; VAR i,h,x,y,xo,yo: integer; PROCEDURE B(i: integer); FORWARD; PROCEDURE C(i: integer); FORWARD; PROCEDURE D(i: integer]; FORWARD; PROCEDURE A(i: integer); BEGIN IF i>O THEN BEGIN A(i-1); x:= x+h; y:= y-h; LineTo(x,y); B(i-1); x:= x+2*h; LineTo(x,y); D(i-1); x:= x+h; y:= y+h; LineTo(x,y); A (i-1) END END;{A} PROCEDURE B; BEGIN IF i>0 THEN BEGIN B(i-1); x:= x-h; y:= y-h; LineTo(x,y); C(i-1); y:= y-2*h; LineTo(x,y); A(i-1); x:= x+h; y:= y-h; LineTo(x,y); B(i-1) 1,2,....,n}

END END;{B} PROCEDURE C; BEGIN {. . .} END;{C} PROCEDURE D; BEGIN {. . .} END;{D} BEGIN {programul principal} i:= 0; h:= ho DIV 4; xo:= 2*h; yo:= 3*h; REPEAT i:= i+1; xo:= xo-h; h:= h DIV 2; yo:= yo+h; x:= xo; y:= yo; MoveTo(x,y); A(i); x:= x+h; y:= y-h; LineTo(x,y); B(i); x:= x-h; y:= y-h; LineTo(x,y); C(i); x:= x-h; y:= y+h; LineTo(x,y); D(i); x:= x+h; y:= y+h; LineTo(x,y) UNTIL i=n; END. ----------------------------------------------------------------[5.3.5.g]

5.4. Algoritmi cu revenire (backtracking) Unul din subiectele de mare interes ale programrii se refer la rezolvarea unor
probleme cu caracter general.

Ideea este de a concepe algoritmi generali pentru gsirea soluiilor unor probleme
specifice, care s nu se bazeze pe un set fix de reguli de calcul, ci pe ncercri repetate i reveniri n caz de nereuit. obiectivului (taskului) n obiective pariale (taskuri pariale).

Modalitatea comun de realizare a acestei tehnici const n descompunerea De regul aceast descompunere este exprimat n mod natural n termeni
recursivi i const n explorarea unui numr finit de subtaskuri.

n general, ntregul proces poate fi privit ca un proces de ncercare sau

cutare care construiete n mod gradat i parcurge n acelai timp un arbore de subprobleme. recursiv n cadrul procesului de cutare i reluarea acestuia pn la obinerea soluiei dorite.

Obinerea unor soluii pariale sau finale care nu satisfac, provoac revenirea

Din acest motiv, astfel de algoritmi se numesc algoritmi cu revenire


("backtracking algorithms").

n multe cazuri arborii de cutare cresc foarte rapid, de obicei exponenial, iar
efortul de cutare crete n aceeai msur.

n mod obinuit, arborii de cutare pot fi simplificai numai cu ajutorul


euristicilor, simplificare care se reflect de fapt n restrngerea volumului de calcul i ncadrarea sa n limite acceptabile.

Scopul acestui paragraf: Nu este acela de a discuta regulile generale ale euristicii. Mai degrab, se vor aborda principiile separrii unei probleme n subproblemele
care o rezolv;

i utilizarea recursivitii n acest context.


5.4.1. Turneul calului

Specificarea problemei: Se consider o tabl de ah (eicher) cu n2 cmpuri. Un cal cruia i este permis a se mica conform regulilor ahului, este plasat n
cmpul cu coordonatele iniiale x0 i y0 .

Se cere s se gseasc acel parcurs al calului - dac exist vreunul - care


acoper toate cmpurile tablei, trecnd o singur dat prin fiecare.

Primul pas n abordarea problemei parcurgerii celor n2 cmpuri este acela de a


considera problema urmtoare:

"La un moment dat se ncearc execuia unei micri urmtoare sau se


constat c nu mai este posibil nici una i atunci se revine n pasul anterior".

Pentru nceput se va defini algoritmul care ncearc s execute micarea urmtoare a


calului. -----------------------------------------------------------{Schia algoritmului pentru realizarea micrii urmtoare a calului} PROCEDURE IncearcaMiscareaUrmatoare; BEGIN *iniializeaz lista micrilor REPEAT *selecteaz posibilitatea urmtoare din lista micrilor IF *este acceptabil THEN BEGIN [5.4.1.a] *nregistreaz micarea curent; IF *tabela nu e plin THEN BEGIN IncearcaMiscareaUrmatoare; IF NOT *micare reusit THEN *terge nregistrarea curent END ELSE *micare reuit END UNTIL (*micare reuit) OR (*nu mai exist posibiliti n lista micrilor) END; {IncearcaMiscareaUrmatoare} -------------------------------------------------------

n continuare se vor preciza cteva dintre structurile de date care vor fi utilizate. n primul rnd tabela de ah va fi reprezentat prin matricea t pentru care se
introduce tipul TipIndice [5.4.1.b]. -----------------------------------------------------------{Definirea structurilor de date: tabela de ah} TYPE TipIndice = 1..n; [5.4.1.b] TipTabela = ARRAY[TipIndice,TipIndice] OF integer; ------------------------------------------------------------

Dup cum se observ, n cmpurile tabelei t se memoreaz valori ntregi i nu valori


booleene care s indice ocuparea.

Acest lucru se face cu scopul de a pstra urma traseului parcurs conform urmtoarei
convenii [5.4.1.c]. ------------------------------------------------------------

{Convenia de marcare a traseului parcurs de cal pe tabela de ah} t[x,y] = 0; t[x,y] = i; {cmpul (x,y) nu a fost nc vizitat} {cmpul (x,y) a fost vizitat n pasul i (1 i n2)} [5.4.1.c] ----------------------------------------------------------- n continuare se vor stabili parametrii specifici de apel ai procedurii. Acetia trebuie s precizeze:

(1) Condiiile de start ale noii micri (parametri de intrare) (2) S precizeze dac micarea respectiv este reuit sau nu (parametru
de ieire).

Pentru prima cerin (1) sunt suficiente: Coordonatele x,y de la care va porni micarea i valoarea i care precizeaz numrul mutrii curente (din raiuni de
nregistrare).

Pentru cea de-a doua cerin (2) se introduce parametrul de ieire q = true desemnnd

micare reuit respectiv q = false nereuit, din punctul de vedere al acceptrii micrii. caracterul "*" n secvena [5.4.1.a].

Problema care se pune n continuare este aceea a rafinrii propoziiilor precedate de n primul rnd faptul c *tabela nu este plin se exprim prin i < n2. Dac se introduc variabilele locale u i v pentru a preciza coordonatele unei
posibile destinaii a micrii conform regulilor dup care se efectueaz saltul calului,

atunci propoziia *este acceptabil poate fi exprimat ca i o Aseriunea *nregistreaz micarea devine t[u,v] = i; Aseriunea *terge
t[u,v] = 0. nregistrarea

combinaie logic a condiiilor 1 u n i 1 v n (adic noul cmp este pe tabel) i c el nu a fost vizitat anterior (t[u,v] = 0).

curent, se exprim prin

Se mai introduce variabila boolean local q1 utilizat ca i parametru rezultat


pentru apelul recursiv al procedurii. Variabila q1 substituie de fapt condiia *micare reuit .

Se ajunge n definitiv la urmtoarea formulare a procedurii [5.4.1.d]. -----------------------------------------------------------{Rafinarea procedurii ncearc - pasul 1 de rafinare}
PROCEDURE Incearca(i: integer; x,y: indice; VAR q: boolean); VAR u,v: integer; q1: boolean; BEGIN

*iniializeaz lista micrilor REPEAT *fie u,v coordonatele micrii urmtoare conform regulilor sahului IF (1<=u<=n) AND (1<=v<=n) AND (t[u,v]=0) THEN BEGIN t[u,v]:= i; [5.4.1.d] IF i<n*n THEN BEGIN Incearca(i+1,u,v,q1); IF NOT q1 THEN t[u,v]:= 0 END ELSE q1:= true END UNTIL q1 OR (nu mai exist posibiliti n lista micrilor); q:= q1 END; {Incearc} ------------------------------------------------------- Relativ la aceast secven se fac urmtoarele precizri.

Tehnica utilizat este cea cunoscut n literatura de specialitate sub denumirea de


"look ahead" (tehnica scrutrii). n baza acestei tehnici:

(1) Se apeleaz procedura cu coordonatele curente x i y; (2) Se selecteaz o nou micare de coordonate u i v (urmtoarea din cel 8
posibile din lista de micri);

(3) Se ncearc realizarea micrii urmtoare plecnd de la poziia u,v. Dac micarea nu este reuit, respectiv s-au parcurs fr succes toate cele
8 posibiliti ale listei de micri, se anuleaz micarea curent (u,v), ea fiind lipsit de perspectiv (bucla REPEAT).

Privind n perspectiva evoluiei cutrii, fiecare dintre cele 8 micri este tratat n
manier similar: poate,

Respectiv pornind de la fiecare dintre micri se merge att de departe ct se n caz de nereuit se ncerc micarea urmtoare din lista de micri pn la
epuizarea tuturor posibilitilor,

Dac s-au epuizat toate posibilitile, se anuleaz micarea curent. Dup cum se observ, procedura se extinde de fapt peste trei niveluri de cutare, Arborele de apeluri asociat cutrii: Este de ordinul 8 (din fiecare punct se pot selecta 8 posibiliti de micare) Are nlimea n2 (numrul de pai necesari pentru soluia final), element care
explic complexitatea procesului de determinare a soluiei problemei. element care i permite revenirea n caz de eec n vederea selectrii unui nou parcurs.

n pasul urmtor i ultimul de rafinare mai rmn cteva puncte de specificat. Precizarea saltului calului. Fiind dat o poziie iniial <x,y> exist opt posibiliti pentru generarea
coordonatelor destinaiei micrii urmtoare <u,v>, care sunt numerotate de la 1 la 8 n fig.5.4.1.a.
3 4 X 5 6 7 8 2 1

Fig.5.4.1.a. Micrile posibile ale calului pe eicher

O metod simpl de obinere a lui u i v din x i y este de a aduna la acestea

din urm diferenele specifice de coordonate memorate n dou tablouri, unul pentru x notat cu a i unul pentru y notat cu b. Indicele k precizeaz numrul urmtorului candidat din lista micrilor (1 k 8).

Detaliile de implementare apar n programul [5.4.1.e]. Procedura recursiv este iniiat printr-un apel cu coordonatele x0,y0 de la care
pornete parcursul turneului calului. (valoare nul).

Acestui cmp i se atribuie valoarea 1, restul cmpurilor se marcheaz ca fiind libere Se face urmtoarea precizare: o variabil t[u,v] exist numai dac u i v sunt n
domeniul 1..n.

n program aceast cerin a fost implementat cu ajutorul operatorului IN,


----------------------------------------------------------------{Determinarea Turneului Calului - varianta final} PROGRAM TurneulCalului; CONST n=5; TYPE TipIndice = 1..n; VAR i,j: TipIndice; q: boolean; S: SET OF integer; a,b: ARRAY[1..8] OF integer; t: ARRAY[TipIndice,TipIndice] OF integer;

utiliznd mulimea S, implementare care pentru valori mici ale lui u respectiv v este foarte eficient.

PROCEDURE Incearca(i: integer; x,y: TipIndice; VAR q: boolean); VAR k,u,v: integer;

k: integer; q1: boolean; BEGIN k:= 0; REPEAT k:= k+1; q1:= false; u:= x+a[k]; v:= y+b[k]; IF (u IN S) AND (v IN S) THEN IF t[u,v]=0 THEN BEGIN t[u,v]:= i; [5.4.1.e] IF i<n*n THEN BEGIN Incearca(i+1,u,v,q1); IF NOT q1 THEN t[u,v]:= 0 END ELSE q1:= true END UNTIL q1 OR (k=8); q:= q1 END;{Incearca} BEGIN {programul principal} S:= [1,2,3,4,5]; a[1]:= 2; b[1]:= 1; a[2]:= 1; b[2]:= 2; a[3]:=-1; b[3]:= 2; a[4]:=-2; b[4]:= 1; a[5]:=-2; b[5]:=-1; a[6]:=-1; b[6]:=-2; a[7]:= 1; b[7]:=-2; a[8]:= 2; b[8]:=-1; FOR i:=1 TO n DO FOR j:= 1 TO n DO t[i,j]:= 0; t[1,1]:= 1; Incearca(2,1,1,q); IF q THEN FOR i:= 1 TO n DO BEGIN FOR j:= 1 TO n DO Write(' ',t[i,j]); Writeln END ELSE Writeln(' nu exista solutie ') END. -----------------------------------------------------------------

n figura 5.4.1.b se prezint rezultatele execuiei programului TurneulCalului


pentru poziiile iniiale (1,1),(3,3) cu n = 5 i (1,1) cu n = 6.
1 14 19 8 25 6 9 2 13 18 15 20 7 24 3 10 5 22 17 12 21 16 11 4 23 23 16 11 6 21 10 5 22 17 12 15 24 1 20 7 4 9 18 13 2 25 14 3 8 19

1 16 34 25 17 2 32 35 23 18 36 31

7 26 11 14 12 15 6 27 33 8 13 10 24 21 28 5 3 30 9 20 22 19 4 29

Fig.5.4.1.b. Exemple de execuie ale programului TurneulCalului

Caracteristica esenial a acestui algoritm: nainteaz spre soluia final pas cu pas, tatonnd i nregistrnd drumul
parcurs. Dac la un moment dat constat c drumul ales nu conduce la soluia dorit ci la o fundtur, revine, tergnd nregistrrile pailor pn la proximul punct care permite o nou alternativ de drum. Aceast activitate se numete revenire (backtraking).

5.4.2. Probleme (n,m). Determinarea unei soluii

Modelul principal general al unui algoritm de revenire se preteaz foarte bine pentru
rezolvarea problemelor pentru care: Soluia final presupune parcurgerea a n pai succesivi, Fiecare pas putnd fi selectat dintre m posibiliti.

O astfel de problem se numete problem de tip (n,m). n secvena [5.4.2.a] apare modelul principial de rezolvare a unei astfel de probleme
n forma procedurii ncearc . Modelul ofer o singur soluie a problemei. -----------------------------------------------------------{Model pricipial de rezolvare a unei probleme de tip (n,m). Determinarea unei soluii} Procedura Incerca; BEGIN *iniializeaz selecia posibilitilor; REPEAT *selecteaz posibilitatea urmtoare; IF acceptabil THEN [5.4.2.a] BEGIN *nregistreaz-o ca i curent; IF *soluie incomplet THEN BEGIN Incerca *pasul urmator; IF *nu este reuit THEN *terge nregistrarea curent END ELSE *pas reuit (soluie complet) END{IF} UNTIL (*pas reusit) OR (*nu mai sunt posibiliti) END;{Incearca} ------------------------------------------------------------

Acest model principial poate fi concretizat n diverse forme. n continuare se prezint


dou variante.

(1) n prima variant, procedura Incearca1 are drept parametru de apel numrul
pasului curent

Explorarea posibilitilor se realizeaz n bucla interioar REPEAT [5.4.2.b].


-----------------------------------------------------------{Rezolvarea unei probleme de tip (n,m). Determinarea unei soluii - Varianta 1} Procedura Incerca1(i: TipPas); VAR posibilitate: TipPosibilitate; BEGIN posibilitate:= 0;{iniializeaz selecia posibilitilor} REPEAT posibilitate:= posibilitate+1; {selecie posibilitate urmtoare} IF *acceptabila THEN BEGIN [5.4.2.b] *nregistreaz-o ca i curent; IF I<n THEN {soluie incomplet} BEGIN Incerca1(I+1); {ncerca pasul urmtor} IF *nereuit THEN *terge nregistrarea curent END ELSE *soluie complet (afiare) END UNTIL *soluie complet OR (posib=m) END;{Incerca1} --------------------------------------------------------

(2) n cea de-a doua variant, procedura Incearca2 are drept parametru de apel o

posibilitate de selecie Construcia soluiei se realizeaz apelnd recursiv procedura pe rnd pentru fiecare posibilitate n parte [5.4.2.c].

Din punctul de vedere al finalitii cele dou variante sunt identice, ele difer doar ca
form. -----------------------------------------------------------{Rezolvarea unei probleme de tip (n,m). Determinarea unei soluii - Varianta 2} Procedura Incearca2(posibilitate: TipPosibilitate); BEGIN IF *acceptabil THEN BEGIN *nregistreaz-o ca i curent; IF *soluie incomplet THEN BEGIN [5.4.2.c] Incearca2(Posibilitate1); Incearca2(Posibilitate2); ... Incearca2(Posibilitatem);

END END;{Incearca2} --------------------------------------------------------

*terge nregistrarea curent END ELSE *soluie complet (afiare)

Se presupune c la fiecare pas numrul posibilitilor de examinat este fix (m) i c


procedura este apelat iniial prin Incearca2(1).

n continuare n cadrul acestui paragraf vor fi prezentate cteva aplicaii ale


algoritmilor cu revenire, care se preteaz deosebit de bine unei abordri recursive.

5.4.3. Problema celor 8 regine

Problema celor 8 regine reprezint un exemplu bine cunoscut de utilizare a


algoritmilor cu revenire.

Aceast problem a fost investigat de K.F. Gauss n 1850, care ns nu a rezolvat-o


complet, ntruct pn n prezent nu a fost gsit o soluie analitic complet.

n schimb ea poate fi rezolvat prin ncercri, necesitnd o mare cantitate de munc,

rbdare, exactitate i acuratee, atribute n care maina de calcul exceleaz asupra omului chiar atunci cnd acesta este un geniu.

Specificarea Problemei celor 8 regine: Pe o tabl de ah trebuiesc plasate 8 regine astfel nct nici una dintre ele s nu
le amenine pe celelalte.

Se observ imediat c aceasta este o problem de tip(n,m): Deoarece exist 8 regine care trebuiesc plasate, deci soluia necesit 8 pai, Pentru fiecare din cele 8 regine existnd, dup cum se va vedea, 8 posibiliti
de a fi aezate pe tabla de ah.

Pornind de la modelul [5.4.2.a] se obine imediat urmtoarea formulare primar a


algoritmului [5.4.3.a]. -----------------------------------------------------------{Rezolvarea Problemei celor 8 regine - schia de principiu} PROCEDURE Incerca(i: regina); BEGIN *iniializeaz selecia locului de plasare pentru a i-a regin REPEAT *selecteaz locul urmtor IF *loc sigur THEN

BEGIN [5.4.3.a] *plaseaz regina i IF i<8 THEN BEGIN Incearca(i+1); IF *ncercare nereuit THEN *ia regina END ELSE *ncercare reuit (i=8) END UNTIL *ncercare reuit OR (*nu mai exista locuri) END;{Incerca} ------------------------------------------------------------

Sunt necesare cteva precizri. Deoarece din regulile ahului se tie c regina amenin toate cmpurile
situate pe aceeai coloan, rnd sau diagonal n raport cu cmpul pe care ea se afl, rezult c fiecare coloan a tablei de ah va putea conine o singur regin.

Astfel alegerea poziiei celei de-a i-a regine poate fi restrns numai la
coloana i.

n consecin: Parametrul i din cadrul algoritmului devine indexul coloanei n care va fi


plasat regina i,

Procesul de selecie se restrnge la una din cele 8 valori posibile ale indicelui
j care precizeaz rndul n cadrul coloanei.

n concluzie: Avem o problem tipic (8,8), Soluionarea ei necesit 8 pai (aezarea celor 8 regine), Fiecare ntr-una din cele 8 poziii ale coloanei proprii (8 posibiliti). Arborele de apeluri recursive este de ordinul 8 i are nlimea 8. n continuare se impune alegerea modalitii de reprezentare a poziiei celor 8 regine
pe tabla de ah. Soluia imediat este aceea a reprezentrii tablei cu ajutorul unei matrice t de dimensiuni 8x8 , dar o astfel de reprezentare conduce la operaii greoaie i complicate de determinare a cmpurilor disponibile.

Pornind de la principiul c de fiecare dat trebuiesc utilizate reprezentrile

directe cele mai relevante i mai eficiente ale informaiei, n cazul de fa nu se vor reprezenta poziiile reginelor pe tabla de ah ci faptul c o regin a fost sau nu plasat pe un anumit rnd sau pe o anumit diagonal.

urmtoarea reprezentare a datelor [5.4.3.b]. -----------------------------------------------------------{Problema celor 9 regine - definirea structurilor de date} VAR x: ARRAY[1..8] OF integer; a: ARRAY[1..8] OF boolean; b: ARRAY[b1..b2] OF boolean; [5.4.3.b] c: ARRAY[c1..c2] OF boolean; ------------------------------------------------------------

tiind c pe fiecare coloan este plasat o singur regin, se poate alege

Presupunnd c regina i se plaseaz n poziia (i,j) pe tabla de ah, semnificaia


acestei reprezentri apare n secvena [5.4.3.c]. -----------------------------------------------------------x[i]:= j precizeaz locul j al reginei n coloana i a[j]:= true nici o regin nu amenin rndul j [5.4.3.c] b[k]:= true nici o regin nu amenin diagonala / k c[k]:= true nici o regin nu amenin diagonala \ k ------------------------------------------------------------

Se precizeaz c pe tabela de ah exist 15 diagonale / (nclinate spre dreapta) i 15


diagonale \ (nclinate spre stnga).

Caracteristica unei diagonale / este aceea c suma coordonatelor i i j pentru oricare


cmp care i aparine este o constant, oricare cmp este o constant.

Pentru diagonalele \ este caracteristic faptul c diferena coordonatelor i i j pentru n figura 5.4.3.a apar reprezentate aceste dou tipuri de diagonale. Dup cum se observ, pentru diagonalele / sumele i+j sunt cuprinse n domeniul
[2,16]

Iar pentru diagonalele \ diferenele aparin domeniului [-7,7].


1 1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

2 i + j 16

1 1 2 3 4 5 6 7 8
15

7
3 4

8
1 2

5 6 7 8 9 10 11 12 13 14

-7 i - j 7 Fig.5.4.3.a. Categorii de diagonale n problema celor 8 regine

Aceste considerente fac posibil alegerea valorilor limitelor b1,b2,c1,c2 pentru


indicii tabelelor b i c [5.4.3.b].

Una din posibiliti este cea utilizat n continuare pentru care s-au ales
b1 = 2, b2 = 16, c1 = 0, c2 = 14 . iau valori,

Pentru b1 i b2 s-au ales chiar limitele intervalului n care sumele indicilor Intervalul n care iau valori diferenele indicilor a fost translatat cu valoarea 7
spre dreapta pentru a obine valori pozitive pentru indicele de acces n tabela c. realizeaz prin b[i+j]

Cu alte cuvinte, accesul n tabloul b destinat evidenei diagonalelor / se Iar accesul n tabloul c destinat evidenei diagonalelor \ prin c[i-j+7]. Iniial, locaiile tablourilor a,b i c se poziioneaz pe true.
(i,j), i fiind coloana proprie, devine [5.4.3.d]: -----------------------------------------------------------{*plaseaz regina i pe poziia (i,j)} x[i]:= j; a[j]:= false; b[i+j]:= false; [5.4.3.d] c[i-j+7]:= false ----------------------------------------------------------- n acelai context, afirmaia *ia regina apare rafinat n secvena [5.4.3.e]: -----------------------------------------------------------{*ia regina} a[j]:= true; b[i+j]:= true; c[i-j+7]:= true [5.4.3.e] ------------------------------------------------------------

Cu ajutorul acestor reprezentri afirmaia *plaseaz regina pe poziia

Condiia *sigur este ndeplinit dac cmpul (i,j) destinaie aparine unui rnd i
unor diagonale care sunt libere (true), situaie ce poate fi exprimat de urmtoarea expresie logic [5.4.3.f]: -----------------------------------------------------------{*sigur} a[j] AND b[i+j] AND c[i-j+7] [5.4.3.f] ------------------------------------------------------------

Programul care materializeaz aceste considerente apare n secvena [5.4.3.g].


-----------------------------------------------------------{Problema celor 8 regine - determinarea unei soluii Varianta final} PROGRAM Regine1; {gsete o soluie a problemei celor 8 regine} VAR i: a: b: c: x: integer; q: boolean; ARRAY[1..8] OF boolean; ARRAY[2..16] OF boolean; ARRAY[0..14] OF boolean; ARRAY[1..8] OF integer;

PROCEDURE Incearca(i: integer; VAR q: boolean); VAR j: integer; BEGIN j:= 0; REPEAT j:= j+1; q:= false; IF a[j] AND b[i+j] AND c[i-j+7] THEN BEGIN x[i]:= j; [5.4.3.g] a[j]:= false; b[i+j]:= false; c[i-j+7]:= false; IF i<8 THEN BEGIN Incearca(i+1,q); IF NOT q THEN BEGIN a[j]:= true; b[i+j]:= true; c[i-j+7]:= true END END ELSE q:= true END UNTIL q OR (j=8) END;{Incearca} BEGIN {programul principal} FOR i:=1 TO 8 DO a[i]:= true; FOR i:=2 TO 16 DO b[i]:= true; FOR i:=0 TO 14 DO c[i]:= true; Incearca(1,q); IF q THEN FOR i:=1 TO 8 DO Write(x[i]); Writeln END.

------------------------------------------------------------

Soluia determinat de program este x =(1,5,8,6,3,7,2,4) i apare reprezentat


grafic n figura 5.4.3.b.

1 2 3 4 5 6 7 8

1 R

7 R

R R R R R R

Fig.5.4.3.b. Soluia problemei celor 8 regine 5.4.4. Determinarea tuturor soluiilor unei probleme (n,m). Generalizarea problemei celor 8 regine

Modelul de determinare a unei soluii pentru o problem de tip (n,m) poate fi uor
extins pentru a determina toate soluiile unei astfel de probleme.

Pentru aceasta este necesar ca generarea pailor care construiesc soluia s se fac
ntr-o manier ordonat ceea ce garanteaz c un anumit pas nu poate fi generat dect o singur dat. sistematic, astfel nct fiecare nod s fie vizitat o singur dat.

Aceast proprietate corespunde cutrii n arborele de apeluri, ntr-o manier De ndat ce o soluie a fost gsit i nregistrat, se trece la determinarea soluiei
urmtoare pe baza unui proces de selecie sistematic pn a la epuizarea tuturor posibilitilor.

Schema general de principiu derivat din [5.4.2.a], care rezolv aceast problem
apare n [5.4.4.a].

n mod surprinztor, gsirea tuturor soluiilor unei probleme de tip (n,m) presupune
un algoritm mai simplu dect gsirea unei singure soluii. -----------------------------------------------------------{Model pentru rezolvarea unei probleme de tip (n,m) determinarea tuturor soluiilor} Procedura Incearca; BEGIN FOR *toate posibilitile de selecie DO IF *selecie acceptabil THEN BEGIN *nregistreaz-o ca i curent IF *soluia incomplet THEN

[5.4.4.a]

Incearca *pasul urmator ELSE *evideniaz soluia; *terge nregistrarea curent END END;{Incearca} ------------------------------------------------------------

Ca i n cazul anterior, acest model principial va fi concretizat n dou variante. (1) n prima variant, procedura Incearca1 are drept parametru de apel numrul
pasului curent i realizeaz explorarea posibilitilor n bucla interioar FOR [5.4.4.b].

parcurse toate valorile lui k=[1,m], ciclul REPEAT a fost nlocuit cu unul FOR. -----------------------------------------------------------{Rezolvarea unei probleme de tip (n,m). Determinarea tuturor soluiilor - Varianta 1} PROCEDURE Incearca1(i: TipPas); VAR posib: TipPosibilitate; BEGIN FOR posib:= 1 TO m DO IF acceptabil THEN BEGIN *nregistreaz-o ca i curent; IF i<n THEN [5.4.4.b] Incearca1(i+1) ELSE *afieaz soluia; *terge nregistrarea END END;{Incearca1} ------------------------------------------------------------

Deoarece pentru evidenierea tuturor posibilitilor n fiecare pas trebuiesc

(2) n cea de-a doua variant, procedura Incearca2 are drept parametru de apel o

posibilitate de selecie Construcia soluiei se realizeaz apelnd recursiv procedura pe rnd pentru fiecare posibilitate n parte. [5.4.4.c]. -----------------------------------------------------------{Rezolvarea unei probleme de tip (n,m). Determinarea tuturor soluiilor - Varianta 2} Procedura Incearca2(posib: TipPosibilitate); BEGIN IF *acceptabil THEN BEGIN *nregistreaz-o ca i curent; IF *soluie incomplet THEN BEGIN Incearca2(Posibilitate1); Incearca2(Posibilitate2); ... Incearca2(Posibilitatem); END ELSE

[5.4.4.c]

*afieaz soluia *terge nregistrarea curent END END;{Incearca2} ------------------------------------------------------------

Pentru exemplificare, se prezint generalizarea problemei celor 8 regine n vederea

determinrii tuturor soluiilor [5.4.4.d]. -----------------------------------------------------------{Problema celor 8 regine - determinarea tuturor soluiilor} PROGRAM OptRegine; VAR i: integer; a: ARRAY[1..8] OF boolean; b: ARRAY[2..16] OF boolean; c: ARRAY[0..14] OF boolean; x: ARRAY[1..8] OF integer; PROCEDURE Afisare; VAR k: integer; BEGIN FOR k:=1 TO 8 DO Writeln END;{Afisare}

Write(x[k]);

[5.4.4.d]

PROCEDURE Incearca(i: integer); VAR j: integer; BEGIN FOR j:=1 TO 8 DO IF a[j] AND b[i+j] AND c[i-j+7] THEN BEGIN x[i]:= j; a[j]:= false; b[i+j]:= false; c[i-j+7]:= false; IF i<8 THEN Incearca(i+1) ELSE Afisare; a[j]:= true; b[i+j]:= true; c[i-j+7]:= true END{IF} END;{Incearca} BEGIN {programul principal} FOR i:=1 TO 8 DO a[i]:= true; FOR i:=2 TO 16 DO b[i]:= true; FOR i:=0 TO 14 DO c[i]:= true; Incearca(1) END. --------------------------------------------------------

Algoritmul prezentat anterior determin cele 92 de soluii ale problemei celor 8


regine.

De fapt, din cauza simetriei exist doar 12 soluii distincte care apar evideniate n figura
5.4.4.a.

R1 R2 R3 R4 R5 R6 R7 R8 ------------------------------------1 5 8 6 3 7 2 4 1 6 8 3 7 4 2 5 1 7 4 6 8 2 5 3 1 7 5 8 2 4 6 3 2 4 6 8 3 1 7 5 2 5 7 1 3 8 6 4 2 5 7 4 1` 8 6 3 2 6 1 7 4 8 3 5 2 6 8 3 1 4 7 5 2 7 3 6 8 5 1 4 2 7 5 8 1 4 6 3 2 8 6 1 3 5 7 4 -------------------------------------

Fig.5.4.4.a. Soluiile problemei celor 8 regine

5.4.5. Problema relaiilor stabile

Specificarea problemei: Se dau dou mulimi disjuncte A i B de cardinalitate egal n. Se cere s se gseasc o mulime de n perechi <a,b> astfel nct a A i
b B , perechi care s satisfac anumite constrngeri.

Exist multe criterii de alctuire a perechilor, unul dintre ele este cel cunoscut sub
denumirea de "regula relaiei stabile" ("stable marriage rule").

Se presupune c ia fiin un ansamblu de dansuri pentru care s-au nscris

biei i fete n numr egal (n). n general fiecare biat i fiecare fat are preferine distincte fa de partenerul su. n baza acestor preferine se constituie n perechi de dansatori. Dac n aceast repartizare exist un biat i o fat care nu formeaz o pereche, dar care se prefer reciproc fa de actualii lor parteneri, se spune c repartizarea este instabil. Dac nu exist astfel de perechi, repartizarea se numete stabil.

Aceast situaie se refer la o serie de probleme asemntoare din viaa de toate zilele
n care atribuirile trebuiesc executate n raport cu anumite preferine: cstorie, alegerea unei faculti de ctre un candidat, alegerea unei meserii etc. preferine este un invariant i c ea nu se modific dup o atribuire particular. de dansatori pe rnd, pn la epuizarea celor dou grupuri.

n cazul de fa problema va fi oarecum simplificat ntruct se presupune c lista de O modalitate de rezolvare a problemei este aceea de a se ncerca alctuirea perechilor

Pentru aflarea tuturor repartizrilor stabile se poate realiza rapid schia algoritmului

de rezolvare ntruct aceasta este evident o problem de tip (n,m) creia i trebuiesc determinate toate soluiile. algoritmului care determin perechile.

Pornind de la modelul [5.4.4.a], procedura Incearca reprezint prima form a Ideea este de a lua pe rnd bieii i de a le distribui partenere, cutarea

desfurndu-se conform listei de preferine a bieilor [5.4.5.a]. -----------------------------------------------------------{Problema relaiilor stabile - rafinarea 1} PROCEDURE Incearca (b: TipBaiat); VAR o: TipOrdin; BEGIN FOR o:=1 TO n DO BEGIN *alege cea de-a o-a preferin a biatului b IF *acceptabil THEN BEGIN [5.4.5.a] *nregistreaz perechea; IF b nu este ultimul biat THEN Incearca(Succ(b)) ELSE *nregistreaz setul stabil ; *terge perechea END{IF} END{FOR} END;{Incearca} -------------------------------------------------------

n continuare se vor preciza tipurile de date. Din raiuni de claritate a programului se introduc trei tipuri scalare identice ca
definiie, dar cu nume diferite [5.4.5.b]. respectiv preferinele fetelor.

Datele iniiale sunt reprezentate prin dou matrice care indic preferinele bieilor PrefBaieti[b] reprezint lista de preferine a biatului b, respectiv
PrefBaieti[b,o] este fata care ocup poziia o n lista biatului b.

PrefFete[f,o] este cea de-a o-a alegere a sa (fig.5.4.5.a). -----------------------------------------------------------{Problema relaiilor stabile - definirea structurilor de date} TYPE TipBaiat = 1..n; TipFata = 1..n; [5.4.5.b] TipOrdin = 1..n; VAR PrefBaieti: ARRAY[TipBaiat,TipOrdin] OF TipFata; PrefFete: ARRAY[TipFata,TipOrdin] OF TipBaiat;

Similar PrefFete[f] este lista de preferine a fetei f, iar

------------------------------------------------------------

Un exemplu de date de intrare apare n figura 5.4.5.a


ordin biatul 1 selecteaz fata 2 3 4 5 6 7 8 fata 1 selecteaz biatul 2 3 4 5 6 7 8 1 7 4 3 3 8 8 2 6 4 8 6 3 6 2 3 7 2 2 3 2 8 3 7 4 1 6 5 8 2 3 1 5 2 3 6 2 4 4 4 5 6 4 2 3 1 4 1 3 7 8 4 5 6 1 2 5 2 3 2 5 1 2 7 4 8 2 4 5 1 8 8 5 6 4 1 7 8 6 3 6 5 7 4 5 6 3 1 5 6 1 3 7 5 1 7 4 8 7 4 1 6 7 8 7 7 7 7 1 5 3 3 4 7 5 2 6 8 3 8 4 5 6 1 2 6 8 8 7 2 5 1 8 5 6 1

Fig.5.4.5.a. Date de intrare pentru determinarea relaiei stabile

Rezultatul execuiei algoritmului va fi prezentat n forma unui tablou de fete x, astfel


nct x[b] reprezint partenera biatului b.

Pentru egalitatea dintre ntre biei i fete se mai introduce un tablou de biei y,
astfel nct y[f] precizeaz partenerul fetei f [5.4.5.c]. -----------------------------------------------------------{Problema relaiilor stabile - definirea structurilor de date de ieire} VAR x: ARRAY[TipBaiat] OF TipFata; [5.4.5.c] y: ARRAY[TipFata] OF TipBaiat; ------------------------------------------------------------

Este evident c nu sunt necesare ambele tablouri, unul putndu-se determina din

cellalt conform relaiilor: x[y[f]]=f i y[x[b]]=b care sunt valabile pentru perechile constituite. Dei valorile lui y[f] pot fi determinate printr-o simpl baleere a lui x , totui y va fi pstrat, pe de o parte din raiuni de echitate iar pe de alt parte pentru claritatea i eficiena programului generat. de dansatori care se construiete.

Informaiile cuprinse n x i y servesc la determinarea stabilitii setului de perechi Deoarece acest set este construit pas cu pas, genernd cte o pereche creia i se
verific imediat stabilitatea, x i y sunt necesari chiar nainte ca toate componentele lor s fie definite.

Pentru a pstra evidena componentelor definite se utilizeaz dou tablouri booleene


auxiliare: Bneales respectiv FNealeasa [5.4.5.d].

-----------------------------------------------------------{Problema relaiilor stabile - definirea unor structuri de date auxiliare} BNeales: ARRAY[baiat] OF boolean; [5.4.5.d] FNealeasa: ARRAY[fata] OF bolean; BNeales[b]= false {baiatul b are pereche} FNealeasa[f]= false {fata f are pereche} ------------------------------------------------------------

Semnificaia cmpurilor celor dou tablouri apare n aceeai secven. Dup cum se observ, BNeales[b]= false implic c x[b] este definit,
deci biatul b are pereche pereche.

Iar FNealeasa = false implic c y[f] este definit, deci fata f are Deoarece alctuirea perechilor se face pornind de la biei care sunt parcuri n ordine

(bucla FOR din [5.4.5a]), faptul c un biat k a fost selectat sau nu, se poate determina din valoarea lui b conform relaiei [5.4.5.e]: -----------------------------------------------------------NOT BNeales[k] k < b [5.4.5.e] ------------------------------------------------------------

Aceasta sugereaz faptul c tabloul BNeales nu este necesar i n consecin se va


utiliza un singur tablou FNealeasa destinat fetelor.

Condiia *acceptabil va fi rafinat prin intermediul condiiilor FNealeasa[f]


i *stabil, unde condiia *stabil va fi rafinat ulterior.

Aplicnd aceste dezvoltri se obine [5.4.5.f] .


-----------------------------------------------------------{Problema relaiilor stabile - rafinarea 2} PROCEDURE Incearca(b: TipBaiat); VAR o: TipOrdin; f: TipFata; BEGIN FOR o:=1 TO n DO BEGIN f:= PrefB[b,o]; IF FNealeasa[f] AND *stabil THEN [5.4.5.f] BEGIN x[b]:= f; y[f]:= b; FNealeasa[f]:= false IF b<n THEN Incearca(Succ(b)) ELSE *nregistreaz setul stabil; FNealeasa[f]:= true END

END; {Incearca} ------------------------------------------------------------

Elementul esenial n acest context este determinarea stabilitii. O prim observaie se refer la faptul c stabilitatea se stabilete prin definiie
din compararea ordinelor de preferine.

Ordinul unui biat sau al unei fete nu st direct la dispoziie din datele

utilizate pn n acest moment: el poate fi determinat printr-o cutare costisitoare n tablourile PrefBaieti respectiv PrefFete. operaie foarte frecvent, pentru a o face ct mai eficient se introduc dou noi structuri de date [5.4.5.g].

Deoarece n determinarea stabilitii stabilirea ordinului de preferin este o

Astfel, OFB[b,f] reprezint ordinul de preferin pe care l are fata f


n lista biatului b,

Iar OBF[f,b] reprezint prioritatea biatului b n lista fetei f.


determinate din matricele iniiale PrefBaieti i PrefFete. -----------------------------------------------------------{Problema relaiilor stabile - definirea unor structuri de date auxiliare}

Este evident c cele dou matrice sunt invariante i pot fi uor

OFB: ARRAY[TipBaiat,TipFata] OF TipOrdin; {ordinul de preferin pe care l au fetele n lista bieilor} [5.4.5.g] OBF: ARRAY[TipFata,TipBaiat] OF TipOrdin; {ordinul de preferin pe care l au bieii n lista fetelor} ------------------------------------------------------------

n continuare stabilitatea se determin n raport strict cu definiia sa iniial. Se reamintete faptul c n cadrul algoritmului se ncearc alctuirea perechilor,
selectnd pe b [1,n] i pe f unde f = PrefBaieti[b,o] adic f este cea de-a o-a clasat n lista de preferine a lui b. ncearc gsirea surselor de perturbaie posibile. n acest sens exist dou posibiliti:

n general, se presupune c stabilitatea este predominant i n continuare se

(1) Presupunnd c biatului b i-a fost repartizat fata f, ar putea exista o


fat fp, preferat lui f de ctre b (f fiind perechea repartizat lui b), care la rndul ei prefer pe b perechii sale actuale b1 [5.4.5.h (1)]. biat bp preferat lui b de ctre f (f fiind perechea repartizat lui b), biat care la rndul su prefer pe f perechii f1 repartizate lui [5.4.5.h (2)].

(2) Presupunnd ca fata f a fost repartizat biatului b, ar putea exista un

-----------------------------------------------------------(1) b ----> f b1 ----> fp y[fp] (2) f ----> b [5.4.5.h]

f1 ----> bp x[bp] ------------------------------------------------------------

Iniial se urmrete sursa (1) de perturbaie n vederea determinrii instabilitii: Pentru toate fetele fp care au ordinul de preferin mai mic n lista lui b (adic

sunt mai preferate) dect fata f repartizat lui, se verific dac nu cumva vreuna l prefer mai mult pe b dect perechea ce i-a fost repartizat. Pentru aceasta se compar ordinele OBF[fp,b] i OBF[fp,y[fp]] pentru toate fetele fp preferate lui f de ctre b , adic pentru toate fp = PrefBaieti[b,i] astfel nct 1 i < o , unde o este ordinul de preferin al fetei f repartizate lui b. De fapt, toate aceste fete sunt deja distribuite n perechi, deoarece oricare dintre ele ar fi fost nedistribuit, ar fi fost repartizat lui b anterior. Se face precizarea c un ordin de preferin cu valoare mai mare nseamn o preferin mai slab. Rezultatul comparaiei este variabila boolean s. Atta vreme ct s rmne true relaia este stabil. Procesul descris poate fi formulat printr-o simpl cutare liniar [5.4.5.i]. -----------------------------------------------------------{Verificarea sursei 1 de perturbaie} s:= true; i:= 1; WHILE (i<o) AND s DO [5.4.5.i] BEGIN fp:= PrefBaieti[b,i]; i:= i+1; IF NOT FNealeasa[fp] THEN s:= OBF[fp,b]>OBF[fp,y[fp]) END; ----------------------------------------------------------- n continuare se urmrete sursa (2) de perturbaie: Pentru toi bieii bp care au ordinul de preferin n lista fetei f mai mic ca i ordinul biatului b cruia i-a fost repartizat, se verific dac nu cumva vreunul o prefer mai mult pe f dect perechea lui actual.

pentru 1 i < OBF[f,b]. n mod analog ca i la cealalt surs de perturbaie, este necesar compararea ordinelor OFB[bp,f] i OFB[bp,x[bp]]. Vor trebui ns evitate comparaiile care se refer la bieii bp crora nc nu leau fost repartizate fete. Testul care realizeaz acest lucru este bp<b, deoarece tuturor bieilor care-l preced pe b li s-au repartizat deja perechi. Rezultatul comparaiei este tot variabila boolean s care atta vreme ct rmne true relaia este stabil. Secvena de cod care implementeaz verificarea celei de-a doua surse de perturbaie apare n [5.4.5.j]. -----------------------------------------------------------{Verificarea sursei 2 de perturbaie} i:= 1; lim:= OBF[f,b]; WHILE (i<lim) AND s DO [5.4.5.j] BEGIN bp:= PrefFete[f,i]; i:= i+1; IF bp<b THEN s:= OFB[bp,f]>OFB[bp,x[bp]] END; ------------------------------------------------------------

n acest scop sunt investigai toi bieii preferai bp = PrefFete[f,i]

Algoritmul complet apare n [5.4.5.k].


-----------------------------------------------------------{Probleme relaiilor stabile - varianta final} PROGRAM RelatieStabila; CONST n=8; TYPE TipBaiat = 1..n; TipFata = 1..n; TipOrdin = 1..n; VAR b: TipBaiat; f: TipFata; o: TipOrdin; PrefBaieti: ARRAY[TipBaiat,TipOrdin] OF TipFata; PrefFete: ARRAY[TipFata,TipOrdin] OF TipBaiat; OFB: ARRAY[TipBaiat,TipFata] OF TipOrdin; OBF: ARRAY[TipFata,TipBaiat] OF TipOrdin; x: ARRAY[TipBaiat] OF TipFata; y: ARRAY[TipFata] OF TipBaiat; FNealeasa: ARRAY[TipFata] OF boolean; PROCEDURE Afiseaz; VAR b1: TipBaiat; ob,of:integer; BEGIN [5.4.5.k] ob:= 0; of:= 0; FOR b1:=1 TO n DO BEGIN Write(x[b1]); ob:= ob+OFB[b1,x[b1]]; of:= of+OBF[x[b1],b1] END; Writeln(ob,of) END;{Afiseaza} PROCEDURE Incearca(b:baiat); VAR o: TipOrdin; f: TipFata; FUNCTION Stabila: boolean;

VAR bp: TipBaiat; fp: TipFata; i,lim: TipOrdin; s: boolean; BEGIN s:= true; i:= 1; {sursa 1)} WHILE (i<o) AND s DO BEGIN fp:= PrefBaieti[b,i]; i:= i+1; IF NOT FNealeasa[fp] THEN s:= OBF[fp,b]>OBF[fp,y[fp]] END; i:= 1; lim:= OBF[f,b]; {sursa 2)} WHILE (i<lim) AND s DO BEGIN bp:= PrefF[f,i]; i:= i+1; IF bp<b THEN s:= OFB[bp,f]>OFB[bp,x[bp]] END; Stabila:= s END;{Stabil} BEGIN {Incearc} FOR o:=1 TO n DO BEGIN f:= PrefBaieti[b,o]; IF FNealeasa[f] THEN IF Stabila THEN BEGIN x[b]:= f; y[f]:= b; FNealeasa[f]:= false; IF b<n THEN Incearca(Succ(b)) ELSE Afisare; FNealeasa[f]:= true END END END;{Incearc} BEGIN {programul principal} FOR b:=1 TO n DO FOR o:=1 TO n DO BEGIN Read(PrefBaieti[b,o]); OFB[b,PrefBaieti[b,o]]:=o END; FOR f:=1 TO n DO FOR o:=1 TO n DO BEGIN Read(PrefFete[f,o]); OBF[f,PrefFete[f,o]]:=o END; FOR f:=1 TO n DO FNealeasa[f]:= true; Incearca(1) END. ------------------------------------------------------------

Pentru datele de intrare din fig.5.4.5.a se obin soluiile stabile din figura 5.4.5.b.

Programul are la baz un algoritm cu revenire din categoria celor tratai n acest
paragraf.

Eficiena sa depinde n primul rnd de gradul de sofisticare al soluiei de reducere a

parcurgerii arborelui cutrilor. Exist i alte metode mai eficiente dect cea propus [MW71]. probleme sub incidena anumitor constrngeri, sunt adesea utilizai n selectarea uneia sau mai multor soluii optime ntr-un sens precizat. pentru fete, sau pentru toi.

Algoritmii de genul celor prezentai care genereaz toate soluiile posibile ale unei

n cazul de fa poate interesa spre exemplu soluia cea mai favorabil pentru biei, n tabela din fig.5.4.5.b apare suma ordinelor tuturor fetelor alese din listele de

preferine ale perechilor lor biei (ob)i suma ordinelor tuturor bieilor alei din listele de preferine ale pe fetelor (of) pentru fiecare soluie n parte.

Acestea sume se calculeaz conform relaiilor [5.4.5.l].


-----------------------------------------------------------ob:= OFB [b, x [b]]
b =1 n

of:=

b =1

OFB [ x [b],b]

[5.4.5.l]

----------------------------------------------------------- Soluia cu cea mai mic valoare pentru ob se numete soluia stabil optim pentru biei iar soluia cu cea mai mic valoare pentru of soluia stabil optim pentru fete.

Datorit strategiei de lucru adoptate n algoritm este evident c se obine la nceput Acest lucru poate fi ns uor evitat dac se schimb rolul bieilor i al fetelor
respectiv se interschimb PrefFete cu PrefBaieti i OFB cu OBF.

soluia optim pentru biei, iar cea pentru fete la sfrit. n acest sens algoritmul favorizeaz bieii.

soluia 1 2 3 4 5 6 7 8 9 x1 7 2 2 6 6 6 6 3 3 x2 4 4 4 4 4 3 3 6 6 x3 3 3 3 3 3 4 4 4 4 x4 8 8 1 8 1 8 1 8 1 x5 1 1 7 1 7 1 7 1 7 x6 5 5 5 5 5 5 5 5 5 x7 2 7 8 7 8 7 8 7 8 x8 6 6 6 2 2 2 2 2 2 ob 16 22 31 26 35 29 38 34 43 of 32 27 20 22 15 20 13 18 11

Fig.5.4.5.b. Soluii ale programului RelatieStabila

5.4.6. Problema seleciei optime

Urmtorul exemplu de algoritm cu revenire reprezint o extensie ale celor anterioare. Astfel, ntr-o prim etap utiliznd principiul revenirii s-a determinat o singur
soluie pentru o anumit problem de tip (n,m) (turneul calului sau problema plasrii celor 8 regine). (n,m) (generalizarea plasrii celor 8 regine i relaia stabil).

n a doua etap s-a abordat problema aflrii tuturor soluiilor unei probleme tip n continuare se propune aflarea soluiei optime. n acest scop este necesar s fie generate toate soluiile posibile i pe parcursul
procesului de generare s fie reinut cea optim ntr-un anumit sens.

Presupunnd c optimul este definit n termenii unei funcii cu valori pozitive f(s),
algoritmul cutat deriv din modelul destinat aflrii tuturor soluiilor unei probleme de tip (n,m) [5.4.4.a] n care afirmaia *evidentiaz soluia se nlocuiete cu secvena [5.4.6.a]. -----------------------------------------------------------IF f(solutie)>f(optim) THEN optim:= solutie [5.4.6.a] ------------------------------------------------------------

Variabila optim nregistreaz cea mai bun soluie ntlnit pn la momentul curent
i ea trebuie iniializat n mod corespunztor. regul ntr-o variabil auxiliar.

Pentru a nu fi recalculat de fiecare dat, valoarea lui f(optim) se pstreaz de Un exemplu cu caracter general al aflrii soluiei optime este urmtorul: Se cere s se gseasc maniera optim de selecie a unei mulimi date de
obiecte, supuse unor constrngeri.

Selecionrile care constituie soluii acceptabile, se construiesc n mod gradat,


examinnd obiectele individuale ale mulimii de baz. vederea seleciei, potrivit soluiei.

Procedura Incearca [5.4.6.b], descrie procesul de investigare al obiectelor mulimii n Procedura se apeleaz recursiv (pentru a investiga obiectul urmtor) pn la
parcurgerea tuturor obiectelor.

Se precizeaz c examinarea unui obiect (numit candidat) se poate termina n


dou moduri: Fie cu includerea obiectului investigat n soluia curent, Fie cu excluderea sa.

are sensul, cele dou situaii specificndu-se n mod explicit [5.4.6.b]. -----------------------------------------------------------{Determinarea seleciei optime - schi de algoritm} PROCEDURE Incearca(i: TipObiect); BEGIN IF *incluziunea este acceptabil THEN BEGIN *include cel de-al i-lea obiect; IF i<n THEN Incearca(i+1) ELSE [5.4.6.b] *verific optimalitatea; *elimin al i-lea obiect END; IF *excluziunea este acceptabil THEN IF i<n THEN Incearca(i+1) ELSE *verific optimalitatea END;{Incearca} --------------------------------------------------------

Din acest motiv utilizarea instruciunilor repetitive REPEAT sau FOR nu-i

Se presupune c obiectele sunt numerotate cu 1,2,...,n. Pornind de la acest model se obin 2n mulimi selectate posibile. Este necesar ns s fie introduse criterii corespunztoare care s reduc n mod
drastic numrul candidailor investigai avnd n vedere faptul c se caut selecia optim.

Pentru lmurirea acestor aspecte se va particulariza n continuare exemplul. Specidicarea problemei: Se consider un set de n obiecte A1,A2,...,An , fiecare fiind caracterizat
prin greutatea sa g i prin valoarea v.

Se cere s se genereze setul optim de obiecte constnd dintr-o selecie a celor


n obiecte, selecie care este afectat de una sau mai multe constrngeri.

Se definete drept set optim acel set care are cea mai mare sum a valorilor
componentelor

Se precizeaz c constrngerea se refer la limitarea sumei greutilor


componentelor.

Aceasta este bine cunoscut problem a cltorilor (knapsack problem), care i


pregtesc bagajele selectnd lucruri dintr-o mulime n, astfel nct:

Valoarea lor (de ntrebuinare) s fie maxim Iar greutatea lor total s nu depeasc o anumit limit.

Rezolvarea problemei ncepe cu etapa stabilirii structurilor de date [5.4.6.c].


-----------------------------------------------------------{Determinarea seleciei optime - definirea structurilor de date} TYPE TipIndice = 1..n; TipObiect = RECORD g,v: integer END; VAR obiecte: ARRAY[TipIndice] OF TipObiect; [5.4.6.c] limg,vtot,vmax: integer; s,sopt: SET OF TipIndice; ------------------------------------------------------------

Variabilele limg i vtot precizeaz greutatea limit respectiv valoarea total a


celor n obiecte, ele fiind constante pe parcursul ntregului proces de selecie. reprezentat prin numele su (indice).

Variabila s reprezint selecia curent a obiectelor, n care fiecare obiect este sopt este selecia optim iar vmax valoarea sa. n continuare se vor preciza criteriile de acceptare ale unui obiect n selecia curent. (1) n ceea ce privete includerea: Un obiect este selectabil dac el se ncadreaz n tolerana de greutate. Dac nu se ncadreaz, se poate opri cutarea altor obiecte pe ramura
respectiv a arborelui de cutare.

(2) n ceea ce privete excluderea: Criteriul de acceptare al excluderii (cel care permite continuarea

construirii seleciei curente), este acela conform cruia valoarea potenial final la care s-ar putea ajunge fr a include candidatul curent n selecie, nu este mai mic dect optimul ntlnit pn n prezent. Neincluderea candidatului curent reprezint practic excluderea sa din selecie. n cazul n care aceast valoare e mai mic dect optimul curent, procesul de cutare poate conduce i la alte soluii, singur ns nu la una optim, deci cutarea n pasul curent nu este fructificabil, ca atare este abandonat.

n baza acestor dou condiii se precizeaz elementele relevante care urmeaz s


fie determinate n fiecare pas al procesului de selecie:

(1) Greutatea total gt a seleciilor executate pn n prezent. Variabila gt se iniializeaz cu valoarea 0, creia i se adaug de fiecare

dat greutatea obiectului inclus n selecia curent. Condiia *incluziunea este acceptabil este ca noua greutate total gt s nu depeasc limita de greutate limg [5.4.6.d].

(2) Valoarea potenial vp care mai poate fi atins de ctre selecia curent
s.

Iniial vp ia valoarea maxim posibil vtot obinut prin nsumarea

valorilor tuturor celor n obiecte ale mulimii. La fiecare excludere din vp se scade valoarea obiectului exclus. Condiia *excluziunea este acceptabil este adevrat dac noua valoare a lui vp rmne mai mare ca vmax unde vmax este valoarea corespunztoare soluiei optime determinate pn n prezent [5.4.6.e]. Explicaia este urmtoarea: chiar eliminnd din selecie obiectul curent, valoarea potenial la care s-ar mai putea ajunge este mai mare dect cea a soluiei optime curente, adic selecia curent poate conduce la o soluie optim.

Aceste entiti (gt,vp) sunt declarate ca parametri ai procedurii Incearca.

-----------------------------------------------------------{rafinarea afirmaiei *incluziunea este acceptabila} gt + obiecte[i].g <= limg [5.4.6.d] -----------------------------------------------------------{rafinarea afirmaiei *excluziunea este acceptabila} vp - obiecte[i].v > vmax [5.4.6.e] ------------------------------------------------------------

Spre a evita reevaluarea periodic a diferenei vp-obiecte[i].v, aceasta se


noteaz cu vp1.

Verificarea optimalitii i nregistrarea soluiei optime apare n secvena [5.4.6.f].


-----------------------------------------------------------{verifica optimalitatea} IF vp>vmax THEN BEGIN {avem un nou optim care se nregistreaz} sopt:= s; vmax:= vp [5.4.6.f] END; ------------------------------------------------------------

Ultima secven este bazat pe raionamentul c valoarea potenial devine valoare


efectiv, de ndat ce au fost tratate toate cele n obiecte.

Programul integral apare n [5.4.6.g]. Exprimarea simpl a incluziunii i excluziunii


se datoreaz utilizrii operatorilor specifici ai tipului mulime SET. -------------------------------------------------------PROGRAM SelectieOptima; CONST n=10; TYPE TipIndice = 1..n; TipObiect = RECORD g,v: integer END; VAR i: TipIndice; obiecte: ARRAY[TipIndice] OF TipObiect;

limg,vtot,vmax: integer; g1,g2,ratia: integer; s,sopt: SET OF TipIndice; z: ARRAY[boolean] OF char; b: boolean;

[5.4.6.g]

PROCEDURE Incearca(i: TipIndice; gt,vp: integer); VAR vp1: integer; BEGIN {se incearca incluziunea obiectului i} IF gt+obiect[i].g<=limg THEN BEGIN s:= s+[i]; IF i<n THEN Incearca(i+1,gt+obiecte[i].g,vp) ELSE IF vp>vmax THEN BEGIN vmax:= vp; sopt:= s END; s:= s-[i] END; {se incearca excluderea obiectului} vp1:= vp-obiecte[i].v; IF vp1>vmax THEN BEGIN IF i<n THEN Incearca(i+1,gt,vp1) ELSE BEGIN vmax:= vp1; sopt:= s END END END;{Incearca} BEGIN {programul principal} vtot:= 0; FOR i:=1 TO n DO WITH obiecte[i] DO BEGIN Read(g,v); vtot:= vtot+v END; Read(g1,g2,ratia); z[true]:= '*'; z[false]:= ' '; Write(' Greutate '); FOR i:=1 TO n DO Write(' ' ,obiecte[i].g); Writeln; Write(' Valoare '); FOR i:=1 TO n DO Write(' ' ,obiecte[i].v); Writeln; REPEAT limg:= g1; vmax:= 0; s:= []; sopt:= []; Incearca(1,0,vtot);

Write(' ',limg); FOR i:=1 TO n DO BEGIN b:= i IN sopt; Write(' ',z[b]) END; Writeln; g1:= g1+ratia UNTIL g1>g2 END. --------------------------------------------------------

n fig.5.4.6.c sunt listate rezultatele execuiei programului cu limita de greutate cuprins


n intervalul 10120.
Greutate Valoare 10 20 30 40 50 60 70 80 90 100 110 120 10 11 12 13 14 15 16 17 18 19 18 20 17 19 25 21 27 23 25 24 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Fig.5.4.6.c. Rezultatele execuiei programului SelectieOptima

Algoritmii care se bazeaz pe tehnica revenirii i care utilizeaz un factor de limitare care
restrnge creterea arborelui potenial de cutare, se mai numesc i algoritmi de tip "nlnuie i limiteaz" ("branch and bound algorithms").

5.5. Structuri de date recursive


5.5.1. Structuri de date statice i dinamice

n cadrul capitolului 1 au fost prezentate structurile fundamentale de date: tabloul,


articolul i mulimea.

Ele se numesc fundamentale deoarece: Constituie elementele de baz cu ajutorul crora se pot forma structuri mai
complexe, Apar foarte frecvent n practic.

Scopul definirii tipurilor de date urmat de specificarea faptului c anumite variabile


concrete sunt de un anumit tip, este:

De a fixa domeniul valorilor pe care le pot lua aceste variabile, De a preciza structura, dimensiunea i amplasarea zonelor de memorie care
le sunt asociate.

ntruct toate aceste elemente sunt fixate de la nceput de ctre compilator, astfel de
variabile i structurile de date aferente lor se numesc statice.

n practica programrii ns exist multe probleme care presupun structuri ale

informaiei mai complicate, a cror caracteristic principal este aceea c se modific structural n timpul execuiei programului, motiv pentru care se numesc structuri dinamice.

Este ns evident faptul, c la un anumit nivel de detaliere, componentele unor astfel de


structuri sunt tipuri de date fundamentale.

n general, indiferent de limbajul de programare utilizat, o structur de date static

este aceea care ocup pe toat durata execuiei programului cruia i aparine, o zon de memorie fix, de volum constant. Memoria necesar unei structuri statice se rezerv n faza de compilare deci nainte de lansarea programului. Se consider statice acele structuri care au volumul efectiv constant Se consider tot statice acele structuri de date cu volum variabil pentru care programatorul poate aprecia o margine superioar a volumului i pentru care se aloc volumul de memorie corespunztor cazului maxim n faza de compilare. memorie nu poate fi cunoscut n faza de compilare deoarece el este funcie de maniera de execuie a algoritmului. Cu alte cuvinte acest volum poate s creasc sau s descreasc n dependen de anumii factori cunoscui numai n timpul rulrii. n consecin alocarea memoriei la o structur dinamic are loc n timpul execuiei programului. celor de tip secven, reprezint o structur static, pentru care se rezerv o zon de memorie constant. Prin declararea unei variabile statice se precizeaz numele i tipul ei, numele fiind un identificator prin intermediul cruia programul respectiv programatorul poate face referire la aceast variabil. secven. Datorit faptului c variabilele corespunztoare lor se presupun nregistrate n memorii externe, ele se consider structuri dinamice speciale i nu fac obiectul prezentului paragraf.

n contrast, o structur de date dinamic este aceea structur al crei volum de

n general, orice variabil declarat n partea de declarare a variabilelor, cu excepia

Pn n momentul de fa, singurele structuri dinamice abordate au fost cele de tip

De fapt att structurile dinamice ct i cele statice sunt formate n ultim instan din

componente de volum constant care se ncadreaz ntr-unul din tipurile cunoscute i care sunt nregistrate integral n memoria central.

n cadrul structurilor dinamice aceste componente se numesc de regul noduri. Natura dinamic a acestor structuri rezult din faptul c numrul nodurilor se poate
modifica pe durata rulrii.

Att structurile dinamice ct i nodurile lor individuale se deosebesc de structurile

statice prin faptul c ele nu se declar i n consecin nu se poate face referire la ele utiliznd numele lor, deoarece practic nu au nume. special n acest scop i care se numesc variabile indicator (pointer).

Referirea unor astfel de structuri se face cu ajutorul unor variabile statice, definite Variabilele referite n aceast manier se numesc variabile indicate.

5.5.2. Tipul de date abstract indicator


5.5.2.1. Definirea TDA Indicator

Utilizarea structurilor de date dinamice alturi de alte aplicaii speciale impun


definirea unui tip special de variabile numite variabile indicator. memorie care memoreaz date efective.

Valorile acestor variabile nu sunt date efective ci ele precizeaz (indic) locaii de Cu alte cuvinte, valoarea unei astfel de variabile indicator reprezint o referin la o
variabil de un anumit tip precizat, numit variabil indicat. abstract i anume tipul de date abstract indicator.

Pentru a preciza natura unor variabile indicator, s-a introdus un nou tip de date Descrierea de principiu a unui astfel de tip apare n [5.5.2.a].
-----------------------------------------------------------TDA Indicator Modelul matematic: Const dintr-o mulime de valori care indic adresele de memorie ale unor variabile numite indicate, aparinnd unui tip specificat. Aceast mulime de valori cuprinde i indicatorul vid care nu indic nici o variabil. Notaii: p,q - variabile de TipIndicator; e - variabila de TipIndicat; b - valoare booleana.

[5.5.2.a]

Operatori: New(p) - procedur care aloc memorie pentru o variabil indicat i plaseaz valoarea adresei de memorie (indicatorul) n variabila p; Dispose(p) - procedur care elibereaz zona de

memorie (locaia) corespunztoare variabilei indicate p; MemoreazIndicator(p,q) - copiaz indicatorul p n q; MemoreazValoareIndicat(p,e) - copiaz valoarea lui e n zona (locaia) indicat de p; e:=FurnizeazValoareIndicat(p) - funcie care returneaz valoarea memorat n zona (locaia) indicat de p; b:=IndicatorIdentic(p,q) - funcie boolean ce returneaz valoarea true dac p q, adic cele dou variabile indicator indic aceeai locaie de memorie. ------------------------------------------------------------

Tipul de date abstract indicator poate fi implementat n mai multe moduri. n continuare se prezint dou astfel de modaliti una bazat pe pointeri i o a doua
bazat pe cursori.

5.5.2.2. Implementarea TDA Indicator cu ajutorul tipului pointer

Dup cum se cunoate, variabilele pointer, sunt variabile statice obinuite care se
declar ca orice alt variabil.

Elementul particular al acestor variabile este faptul c ele se declar de tip pointer. nainte de a preciza acest tip, se va clarifica semnificaia variabilelor pointer. Valoarea unei astfel de variabile este de fapt o adres de memorie care poate
fi adresa unei structuri dinamice sau a unei componente a ei. n consecin, n limbaj de asamblare accesul la variabila indicat de o variabil pointer se realizeaz prin adresarea indirect a celei din urm. n limbajele de nivel superior acelai lucru se realizeaz prin ataarea unor caractere speciale numelui variabilei pointer n dependen de limbajul utilizat [5.5.2.2.a]. -----------------------------------------------------------{Precizarea unei variabile indicate - varianta Pascal} VariabilaIndicata VariabilaPointer^ -----------------------------------------------------------//Precizarea unei variabile indicate - varianta C VariabilaIndicata *VariabilaPointer (n C) [5.5.2.2.a] ------------------------------------------------------------

n plus, n limbajul C s-a definit operatorul & care se permite determinarea adresei
unei variabile indicate i a fost dezvoltat o aritmetic special a pointerilor.

O regul deosebit de important stabilete c, spre deosebire de un limbaj de


asamblare, n limbajele de nivel superior se stabilete o legtur fix ntre orice variabil pointer i tipul variabilei indicate.

O variabil pointer dat se refer n mod obligatoriu la o variabil indicat de


un anumit tip. Aceast restricie mrete sigurana n programare i constituie o deosebire net ntre variabilele pointer definite n limbajele de nivel superior i adresele obinuite din limbajele de asamblare.

Un tip pointer se definete preciznd tipul variabilei indicate precedat de caracterul


"^" n Pascal respectiv caracterul "*" n limbajul C [5.5.2.2.b]. -----------------------------------------------------------{Definirea unui tip pointer - varianta Pascal} TYPE TipPointer = ^TipIndicat; (Pascal) -----------------------------------------------------------//Definirea unui tip pointer - varianta C tip_indicat *variabila_pointer; (C) [5.5.2.2.b] ------------------------------------------------------------

n limbajul C aceast restricie poate fi evitat utiliznd tipul generic void care
permite declararea unui pointer generic care nu are asociat un tip de date precis. -----------------------------------------------------------void *variabila_pointer; [5.5.2.2.c] ------------------------------------------------------------

Alocarea sau eliberarea memoriei unei structuri dinamice n timpul rulrii cade n
competena programatorului i se realizeaz cu ajutorul unor operatori standard specifici dependeni de limbaj.

Pentru exemplificare n secvenele urmtoare se prezint implementarea TDA


Indicator n limbajele Pascal respectiv C, Implementri sunt realizate prin intermediul unor operatori definii la nivelul limbajului. -----------------------------------------------------------{TDA Indicator - implementare Pascal} TYPE TipPointer = ^TipIndicat; VAR p,q: TipPointer; e: TipIndicator; b: boolean;

[5.5.2.2.d]

New(p); {New(p)} Dispose(p); {Dispose(p)} p:= q; {MemoreazaIndicator(p,q)} p^:= e; {MemoreazaValoareIndicata(p,e)} e:= p^; {e:=FurnizeazaValoareIndicata(p)} b:= p=q; {b:=IndicatorIdentic(p,q)} -----------------------------------------------------------//TDA Indicator - implementare C tip_indicat int b; *p,*q,e; [5.5.2.2.e]

p=malloc(sizeof(tip_indicat)); {New(p)} free(p); {Dispose(p)} p:= q; {MemoreazaIndicator(p,q)}

*p:= e; {MemoreazaValoareIndicata(p,e)} e:= *p; {e:=FurnizeazaValoareIndicata(p)} b=(p==q); {b:=IndicatorIdentic(p,q)} -----------------------------------------------------------5.5.2.3. Implementarea TDA Indicator cu ajutorul cursorilor

Dac n limbajele care definesc tipul pointer (referin), implementarea TDA


Indicator este imediat i realizat chiar la nivelul limbajului, n limbajele care nu dispun de aceast facilitate sau n situaii speciale, implementarea acestui tip de date se poate realiza cu ajutorul cursorilor.

Un cursor este o variabil ntreag utilizat pentru a indica o locaie ntr-un tablou
de variabile indicate. Ca i metod de conectare, un cursor este perfect echivalent cu un pointer cu deosebirea c el poate fi utilizat n plus i n limbajele care nu definesc tipul referin. Interpretnd valoarea unei variabile ntregi ca i un indice ntr-un tablou, n mod efectiv acea variabil va indica locaia respectiv din tablou.

Cu ajutorul acestei tehnici, pot fi implementate practic toate structruile de date care

presupun nlnuiri, dup cum se va vedea n capitolele urmtoare. Este ns evident faptul c sarcina gestionrii zonei care se aloc dinamic revine exclusiv programatorului, cu alte cuvinte utilizatorul trebuie s-i dezvolte proprii operatori de tip New respectiv Dispose .

5.5.2.4. Implementarea tipului indicator cu ajutorul referinelor

Programarea orientat spre obiecte a impus noi dezvoltri ale posibilitilor de acces
la elementele cu care programatorul opereaz n cadrul unui program.

Astfel limbajele orientate spre obiecte cum ar fi C++ sau JAVA definesc aa numitele
referine care de fapt sunt o form de implementare a tipului indicator.

O referin apare ca fiind similar n multe privine cu un pointer, dar de fapt nu este
un pointer.

O referin este un alias (un alt nume) pentru o variabil precizat. Ea are un nume care poate fi folosit n locul variabilei originale. Deoarece este un alias i nu un pointer, variabila pentru care este declarat

referina trebuie s fie specificat n momentul declarrii referinei. n plus spre deosebire de un pointer o referin nu poate fi modificat pentru a indica o alt variabil. ---------------------------------------------------------- Exemplul 5.5.2.3. Considernd n limbajul C++ o variabil declarat dup cum urmeaz: -----------------------------------------------------------long numar = 0; ------------------------------------------------------------

Se poate declara o referin pentru ea utiliznd urmtoarea declaraie:


----------------------------------------------------------long& ref_numar = numar; // Declarare referin ------------------------------------------------------------

n acest caz se poate spune c de fapt ref_numar este de tip "referin la long". Referina poate fi n continuare utilizat n locul variabilei originale. Spre exemplu
atribuirea -----------------------------------------------------------ref_numar += 13; ------------------------------------------------------------

Are ca efect incrementarea variabilei numar cu 13. Spre deosebire de referina anterior precizat, un pointer la aceeai variabil poate fi
declarat astfel: -----------------------------------------------------------long* pointer_numar = &numar; ------------------------------------------------------------

Ceea ce permite incrementarea variabilei numar dup cum urmeaz:


-----------------------------------------------------------*pointer_numar += 13; ------------------------------------------------------------

Exist o distincie evident ntre utilizarea unui pointer i utilizarea unei referine. Pointerul este evaluat la fiecare utilizare i n conformitate cu adresa pe care o
conine la momentul respectiv este accesat variabila indicat. Referina nu este necesar s fie evaluat la fiecare utilizare; odat definit ea nu mai poate fi modificat, lucru care nu este valabil i pentru pointer. Referina este complet echivalent cu variabila la care se refer. ------------------------------------------------------------

La o analiz sumar referina nu pare a fi altceva dect o notaie alternativ pentru o


anumit variabil deoarece ea se comport n acest sens.

n realitate adevratele valene ale acestui concept sunt puse n eviden n cazul

utilizrii obiectelor. Astfel, n limbajele orientate spre programarea pe obiecte, n momentul n care se asigneaz obiecte variabilelor sau se transmit obiecte ca argumente pentru metode, se transmit de fapt referinele acelor obiecte i nu obiectele ca atare sau copii ale lor. Acest lucru are o importan cu totul particular n dezvoltarea de aplicaii orientate spre obiecte. Spre exemplu n limbajul JAVA nu exist pointeri explicii nici aa numita "aritmetic a pointerilor" ci doar referine, fr ca acest lucru s restrng libertatea de programare ci dimpotriv.

5.5.3. Structuri de date recursive

n cadrul paragrafelor acestui capitol, pn n prezent, recursivitatea a fost prezentat


ca o proprietate specific algoritmilor, implementat n forma unor proceduri care se apeleaz pe ele nsele. n cadrul acestui paragraf se va prezenta extinderea acestei proprieti i asupra tipurilor de date n forma aa-numitelor "structuri de date recursive". care are cel puin o component de acelai tip ca i structura nsi. sau n mod indirect (vezi &5.1).

Prin analogie cu algoritmii, prin structur de date recursiv se nelege o structur i n acest caz, definiiile unor astfel de tipuri de date pot fi recursive n mod direct Cel mai simplu exemplu de structur recursiv l reprezint lista nlnuit simpl a
crei definire formal apare n [5.5.3.a]. -----------------------------------------------------------{Exemplu 1 de structur de date recursiv - structura list nlnuit} TYPE TipLista = RECORD info: TipInfo; [5.5.3.a] urm: TipLista (incorect) END; ------------------------------------------------------------

Un alt exemplu de structur recursiv este arborele genealogic al unei persoane,


adic structura care precizeaz toate rudele directe ascendente ale persoanei n cauz. reprezentnd numele persoanei, celelalte dou fiind arborii genealogici ai prinilor.

O astfel de structur poate fi definit ca i un articol cu trei componente, prima Aceasta se exprim formal astfel [5.5.3.b]:
-----------------------------------------------------------{Exemplu 2 de structur de date recursiv - arborele genealogic al unei persoane} TYPE TipGenealogie = RECORD nume: string; [5.5.3.b] tata, mama: TipGenealogie (incorect) END; ------------------------------------------------------------

Datorit cmpurilor urm respectiv tata i mama, care au acelai tip ca i structura
nsi, tipurile definite sunt recursive.

Se face precizarea c definiiile de mai sus nu sunt corecte, deoarece se utilizeaz

identificatorii TipLista repectiv TipGenealogie nainte de a fi complet definii (utilizarea unui tip n curs de definire). Acest neajuns va fi remediat ulterior.

Se constat uor c cele dou tipuri definesc structuri infinite. Aceasta este o consecin direct a recursivitii respectiv a faptului c exist
cmpuri de tip identic cu cel al structurii complete.

Este ns evident c n practic nu se pot prelucra structuri infinite. n realitate nici listele nici arborii genealogici nu sunt infinii. n cazul arborilor genealogici spre exemplu, urmrind suficient de departe
ascendena oricrei persoane se ajunge la strmoi despre care lipsete informaia. astfel:

innd cont de aceasta, se va modifica definiia tipului TipGenealogie Dac se ajunge la o persoan necunoscut atunci structura va conine
un singur cmp notat cu 'XX', anterioare.

n orice alt caz structura conine trei cmpuri conform definiiei n figura 5.5.3.a se poate urmri o structur de TipGenalogie
conform definiiei modificate.

TEFAN PETRU

ADAM XX XX

XX

MARIA

EVA

XX

Fig.5.5.3.a. Exemplu de structur de date recursiv

Cu privire la tipurile de date recursive se precizeaz n general c pentru a evita


structuri infinite, definiia tipului trebuie s conin o condiie, de care depinde prezena efectiv a componentei (sau a componentelor) recursive.

Ca i n exemplul de mai sus, n anumite condiii, componentele avnd acelai tip ca i

structura complet, pot s lipseasc. n accepiunea acestor condiionri pot exista structuri recursive finite. n forma unor structuri dinamice.

Structurile recursive pot fi implementate n limbajele de programare avansate numai Prin definiie, orice component care are tipul identic cu structura complet, se
nlocuiete cu un pointer care indic acea component.

n secvenele [5.5.3.c,d] apar pentru exemplificare definiri ale structurii recursive

TipGenealogie n variant Pascal respectiv C. -----------------------------------------------------------{Structur de date recursiv - arborele genealogic al unei persoane - varianta Pascal} TYPE PointerGenealogie = ^TipGenealogie; TipGenealogie = RECORD nume: string; [5.5.3.c] tata,mama:PointerGenealogie END; -----------------------------------------------------------// Structur de date recursiv - arborele genealogic al unei persoane - varianta C} struct genealogie { char *nume; [5.5.3.d] struct genealogie *tata, *mama; } ------------------------------------------------------------

Dup cum s-a precizat, n anumite condiii, o component poate s lipseasc dintr-o
structur recursiv.

n acest scop, mulimea valorilor oricrui tip referin se extinde cu referina vid

(pointer nul) care nu se refer la nici o variabil indicat. Acest pointer se noteaz cu NIL respectiv NULL, el aparinnd prin definiie oricrui tip referin. Absena unei componente recursive se va indica asignnd pointerul referitor la aceast component cu referina vid. n figura 5.5.3.b.

Cu aceste precizri, structura recursiv din figura 5.5.3.a poate fi reprezentat ca n i

PETRU

TEFAN

NIL

MARIA

NIL

ADAM

NIL

NIL

EVA

NIL

NIL

Fig.5.5.3.b. Implementarea unei structuri de date recursive

Un alt exemplu de structur recursiv este cel al expresiilor aritmetice. n acest caz recursivitatea rezult din faptul c orice expresie aritmetic poate
conine un operand care este o alt expresie aritmetic nchis ntre paranteze.

n exemplul care urmeaz se va considera cazul simplu n care prin expresia

aritmetic se nelege: Fie un operand simplu (care va fi reprezentat ca un identificator cu o singur liter) Fie o expresie aritmetic urmat de un operator urmat de o expresie aritmetic.

Definirea unei astfel de structuri se poate realiza dup cum se prezint n secvena
[5.5.3.e]. -----------------------------------------------------------{Structur de date recursiv - expresie aritmetic varianta Pascal} TYPE PointerExpresie= ^TipExpresie; TipExpresie = RECORD [5.5.3.e] Operator: CHAR; Operand1,Operand2: PointerExpresie END; ------------------------------------------------------------

n cadrul acestei definiii se consider c: Dac n cmpul operator se gsete unul din caracterele ' + ',' - ', ' * ' sau ' / ',

atunci celelalte dou cmpuri conin cte un pointer la o alt expresie, Dac n cmpul operator se gsete o liter, atunci celelalte dou cmpuri vor conine NIL.

S-a artat n &5.2.4, c orice algoritm recursiv poate fi nlocuit cu un algoritm


iterativ echivalent.

Aceast proprietate se pstreaz i pentru structurile recursive de date i anume orice


structur recursiv poate fi nlocuit cu o structur de tip secven.

Astfel, o definiie de tip recursiv de forma [5.5.3.f]:


-----------------------------------------------------------{Structur de date recursiv} TYPE IndicatorStructura = ^TipStructura TipStructura = RECORD continut: TipContinut; [5.5.3.f] legatura: IndicatorStructura END; ------------------------------------------------------------

Este echivalent i nlocuibil imediat cu tipul secvenial [5.5.3.g]:


-----------------------------------------------------------{Varianta iterativ a structurii de date recursive [5.5.3.f]} TYPE TipStuctura = FILE OF TipContinut [5.5.3.g] ------------------------------------------------------------

Se observ c o structur recursiv se poate nlocui imediat cu una secvenial dac i

numai dac numele tipului recursiv apare n propria definiie recursiv o singur dat la sfritul (sau la nceputul) acesteia.

Aceast observaie este valabil i pentru procedurile recursive (vezi &5.2.4). n cazul altor structuri de date recursive cu un grad mai ridicat de complexitate (de
regul bazate pe arbori de diferite ordine) se poate realiza transformarea ntr-o structur secvenial prin traversarea arborelui asociat structurii ntr-o manier ordonat specific (spre exemplu n in, pre sau post ordine).

6. Liste
6.1. Structura de date list n cadrul structurilor avansate de date, structura list ocup un loc important. O list, este o structur dinamic, care se definete pornind de la noiunea de
vector.

Elementele unei liste sunt toate de acelai tip i sunt nregistrate n memoria

central a sistemului de calcul. Spre deosebire de structura static tablou la care se impune ca numrul componentelor s fie constant, n cazul listelor acest numr poate fi variabil, chiar nul. Listele sunt structuri flexibile particulare, care funcie de necesiti pot crete sau descrete i ale cror elemente pot fi referite, inserate sau terse n orice poziie din cadrul listei. Dou sau mai multe liste pot fi concatenate sau scindate n subliste.

n practica programrii listele apar n mod obinuit n aplicaii referitoare la regsirea


informaiei, implementarea translatoarelor de programe, simulare etc.

6.2. TDA List Din punct de vedere matematic, o list este o secven de zero sau mai multe

elemente numite noduri aparinnd unui anumit tip numit tip de baz, care se reprezint de regul astfel [6.1.a]: -----------------------------------------------------------a1, a2,...,an [6.1.a] ----------------------------------------------------------- Unde n 0 i fiecare ai aparine tipului de baz.

Numrul n al nodurilor se numete lungimea listei. Presupunnd c n 1, se spune c a1 este primul nod al listei iar an este ultimul
nod.

Dac n = 0 avem de-a face cu o list vid. O proprietate important a unei liste este aceea c nodurile sale pot fi ordonate liniar
funcie de poziia lor n cadrul listei. Se spune c ai precede pe ai+1 pentru i=1,2,...,n-1 i c ai succede (urmeaz) lui ai-1 pentru i=2,3,4, ...,n. De regul se spune c nodul ai se afl pe poziia i. element al listei. n aceast idee se introduce funcia FIN(L):TipPozitie care returneaz poziia urmtoare poziiei n n lista L avnd n elemente.

Este de asemenea convenabil s se postuleze existena poziiei urmtoare ultimului

Se observ c FIN(L) are o distan variabil fa de nceputul listei, funcie

de faptul c lista crete sau se reduce, n timp ce alte poziii au o distan fix fa de nceputul listei.

Pentru a defini un tip de date abstract, n cazul de fa TDA List, Pe lng definirea din punct de vedere matematic a modelului asociat (mai Este necesar s se defineasc i un set de operatori aplicabili obiectelor de tip
list. sus precizat)

Din pcate, pe de o parte este relativ greu de definit un set de operatori valabil n toate
aplicaiile, iar pe de alt parte natura setului depinde esenial de maniera de implementare a listelor. listelor, unul restrns i altul extins.

n continuare se prezint dou seturi reprezentative de operatori care acioneaz asupra

6.2.1. Set de operatori restrns

Pentru a defini setul restrns de operatori: Se noteaz cu L o list ale crei noduri aparin tipului de baz TipNod. x:TipNod este un obiect al acestui tip (deci un nod al listei). p este o variabil de TipPozitie. Tipul poziie este dependent de
implementare (indice, pointer, cursor, etc.) [AH85].

n termenii formalismului utilizat n cadrul acestui manual, TDA List - varianta


restrns apare n [6.2.1.a]. -----------------------------------------------------------TDA List (varianta restrns) Modelul matematic: o secven format din zero sau mai multe elemente numite noduri toate ncadrate ntr-un anumit tip numit tip de baz. Notaii: L: TipLista; p: TipPozitie; x: TipNod.

[6.2.1.a]

Operatori: 1.Fin(L:TipLista):TipPozitie; - operator care returneaz poziia urmtoare ultimului nod al listei, adic poziia urmtoare sfritului ei. n cazul listei vide Fin(L)=0; 2.Insereaza(L:TipLista,x:TipNod,p:TipPozitie); insereaz n lista L, nodul x n poziia p. Toate nodurile care urmeaz acestei poziii se mut cu un pas spre poziiile superioare, astfel nct a1,a2,...,an devine a1,a2,...,ap-1, x,ap,...an. Dac p este Fin(L) atunci lista

devine a1, a2,...an,x. Dac p>Fin(L) rezultatul este nedefinit; 3.Cauta(x:TipNod,L:TipLista):TipPozitie; - caut nodul x n lista L i returneaz poziia nodului. Dac x apare de mai multe ori, se furnizeaz poziia primei apariii. Dac x nu apare de loc se returneaz valoarea Fin(L); 4.Furnizeaza(p:TipPozite,L:TipLista):TipNod; operator care returneaz nodul situat pe poziia p n lista L. Rezultatul este nedefinit dac p=Fin(L) sau dac n L nu exist poziia p. Se precizeaz c tipul operatorului Furnizeaza trebuie s fie identic cu tipul de baz al listei; 5.Suprima(p:TipPozite,L:TipLista); - suprim elementul aflat pe poziia p n lista L. Dac L este a1,a2,...,an atunci dup suprimare L devine a1,a2,...ap-1,ap+1,...,an. Rezultatul este nedefinit dac L nu are poziia p sau dac p=Fin(L); 6.Urmator(p:TipPozite,L:TipLista):TipPozitie; Anterior(p:TipPozite,L:TipLista):TipPozitie; operatori care returneaz poziia urmtoare, respectiv poziia anterioar poziiei p n cadrul listei. Dac p este ultima poziie n L atunci Urmator(p,l)=Fin(L). Urmator nu este definit pentru p=Fin(L), iar Anterior pentru p=1. 7.Initializeaza(L:TipLista):TipPozitie;- operator care face lista L vid i returneaz poziia Fin(L)=0. 8.Primul(L:TipLista):TipPozitie; - returneaz valoarea primei poziii n lista L. Dac L este vid, poziia returnat este Fin(L)=0. 9.TraverseazaLista(L:TipLista,ProcesareNod(...): PROCEDURE); - parcurge nodurile listei L n ordinea n care apar ele n list i aplic fiecruia procedura ProcesareNod. ------------------------------------------------------------

Exemplul 6.2.1. Pentru a ilustra utilitatea acestui set de operatori se consider un

exemplu tipic de aplicaie. Fiind dat o list de adrese de persoane, se cere s se elimine duplicatele. Conceptual acest lucru este simplu: pentru fiecare nod al listei se elimin nodurile echivalente care-i urmeaz. Definind o structur de date specific, n termenii operatorilor anterior definii, algoritmul de eliminare a adreselor duble din list poate fi formulat astfel [6.2.1.b]. -----------------------------------------------------------{Exemplu - Eliminarea nodurilor duplicat din cadrul unei liste} TYPE TipNod=RECORD NrCurent: integer; Nume: string[20]; Adresa: string[40]

END;

[6.2.1.b]

PROCEDURE Elimina(VAR L: TipLista); {procedura suprim duplicatele nodurilor din list} VAR p,q: TipPozitie; {p este poziia curent} {q este utilizat n cutare} BEGIN p:= Primul(L); WHILE p<>Fin(L) DO BEGIN q:= Urmator(p,L); WHILE q<>Fin(L) DO IF Furnizeaza(p,L)=Furnizeaza(q,L) THEN Suprima(q,L) ELSE q:= Urmator(q,L); p:= Urmator(p,L) END; END;{Elimina} ------------------------------------------------------------

n legtur cu cea de-a doua bucl WHILE, se poate face o observaie important
referitoare la variabila q. Dac se suprim din list elementul situat pe poziia q, elementele aflate pe poziiile q+1,q+2, etc, retrogradeaz cu o poziie n list. Dac n mod ntmpltor q este ultimul element al listei, atunci valoarea sa devine Fin(L). Dac n continuare s-ar executa instrucia q:=Urmator(q,L), lucru dictat de logica algoritmului, s-ar obine o valoare nedeterminat pentru q. Din acest motiv, s-a prevzut ca trecerea la elementul urmtor s se fac numai dup o nou verificare a condiiei, respectiv dac condiia instruciei IF este adevrat se execut numai suprimarea iar n caz contrar numai avansul. -----------------------------------------------------------6.2.2. Set de operatori extins

n acelai context, n continuare se prezint un al doilea set de operatori referitori la liste


avnd o complexitate mai ridicat [6.2.2.a].

n principiu structura listei avute n vedere se bazeaz pe nlnuiri [SH90].


-----------------------------------------------------------TDAList (varianta extins) [6.2.2.a] Modelul matematic: o secven finit de noduri. Toate nodurile aparin unui acelai tip numit tip de baz. Fiecare nod const din dou pri: o parte de informaii i o a doua parte coninnd legtura la nodul urmtor. O variabil special indic primul nod al listei. Notaii: - TipNod - tipul de baz; - TipLista - tip indicator la tipul de baz;

- TipInfo - partea de informaii a lui TipNod; - TipIndicatorNod - tip indicator la tipul de baz (identic cu TipLista); - incLista: TipLista - variabil care indic nceputul listei; - curent,p,pnod: TipIndicatorNod - indic noduri n lista; - element: TipNod; - info: TipInfo; parte de informaii a unui nod; - b - valoare boolean; - nil - indicatorul vid. Operatori: 1.CreazaListaVida(incLista: TipLista); - variabila incLista devine nil. 2.ListaVida(incLista:TipLista):boolean; - operator care returneaz TRUE dac lista este vid respectiv FALSE altfel. 3.Primul(incLista:TipLista,curent:TipIndicatorNod; operator care face ca variabila curent s indice primul nod al listei precizat de incLista. 4.Ultimul(curent: TipIndicatorNod): boolean; operator care returneaz TRUE dac curent indic ultimul element al listei. 5.InserInceput(incLista: TipLista,pnod: TipIndicatorNod); - insereaz la inceputul listei incLista nodul indicat de pnod. 6.InserDupa(curent,pnod: TipIndicatorNod); insereaz nodul indicat de pnod dup nodul indicat de curent. Se presupune c curent indic un nod din list. 7.InserInFatza(curent,pnod: TipIndicatorNod); insertie n faa nodului curent. 8.SuprimaPrimul(incLista: TipLista); - suprim primul nod al listei incLista. 9.SuprimaUrm(curent: TipIndicatorNod); - suprim nodul urmtor celui indicat de curent. 10.SuprimaCurent(curent: TipIndicatorNod); 11.Urmatorul(curent: TipIndicatorNod); - curent se poziioneaz pe urmtorul nod al listei. Dac curent indic ultimul nod al listei el va deveni nil. 12.Anterior(curent: TipIndicatorNod); - curent se poziioneaz pe nodul anterior celui curent. 13.MemoreazaInfo(curent: TipIndicatorNod,info: TipInfo); - atribuie nodului indicat de curent informaia info. 14.MemoreazaLeg(curent,p: TipIndicatorNod); atribuie cmpului urm (de legatur) al nodului indicat de curent valoarea p. 15.FurnizeazaInfo(curent:TipIndicatorNod):TipInfo; - returneaz partea de informaie a nodului indicat de curent. 16.FurnizeazaUrm(curent: TipIndicatorNod): TipIndicator Nod; - returneaz legatura nodului curent (valoarea cmpului urm).

17.TraverseazaLista(incLista:TipLista,ProcesareNod (...):PROCEDURE); - parcurge nodurile listei L n ordinea n care apar ele n list i aplic fiecruia procedura ProcesareNod. ------------------------------------------------------------

6.3. Tehnici de implementare a listelor De regul pentru structurile de date fundamentale exist construcii de limbaj care le
reprezint, construcii care i gsesc un anumit corespondent n particularitile hardware ale sistemelor care le implementeaz. nalt de abstractizare, acest lucru nu mai este valabil.

Pentru structurile de date avansate ns, care se caracterizeaz printr-un nivel mai De regul, reprezentarea acestor structuri se realizeaz cu ajutorul structurilor de date
fundamentale, observaie valabil i pentru structura list.

Din acest motiv, n cadrul acestui paragraf: Vor fi prezentate cteva dintre structurile de date fundamentale care pot servi
la reprezentarea listelor, Iar procedurile i funciile care implementeaz operatorii specifici prelucrrii listelor vor fi descrii n termenii acestor structuri.

6.3.1. Implementarea listelor cu ajutorul structurii tablou

n cazul implementrii listelor cu ajutorul structurii tablou: O list se asimileaz cu un tablou, Nodurile listei sunt memorate ntr-o zon contigu n locaii succesive de
memorie.

n aceast reprezentare: O list poate fi uor traversat Noile noduri pot fi adugate n mod simplu la sfritul listei. Inseria unui nod n mijlocul listei presupune ns deplasarea tuturor

nodurilor urmtoare cu o poziie spre sfritul listei pentru a face loc noului nod. Suprimarea oricrui nod cu excepia ultimului, presupune de asemenea deplasarea tuturor celorlalte n vederea eliminrii spaiului creat. Inseria i suprimarea unui nod necesit un efort de execuie O(n).

n implementarea bazat pe tablouri, TipLista se definete ca un articol cu dou


cmpuri. Primul cmp este un tablou numit noduri, cu elemente de TipNod, a crui lungime este astfel aleas de ctre programator nct s fie suficient pentru a putea pstra cea mai mare dimensiune de list ce poate apare n respectiva aplicaie.

Cel de-al doilea cmp este un ntreg (ultim) care indic n tablou poziia

ultimului nod al listei. Cel de-al i-lea nod al listei se gsete n cel de-al i-lea element al tabloului, pentru 1 i ultim (fig.6.3.1). Poziia n cadrul listei se reprezint prin valori ntregi, respectiv cea de-a i-a poziie prin valoarea i. Funcia Fin(L) returneaz valoarea ultim+1.
noduri
Primul nod Al doilea nod

ultim

Ultimul nod

lungMax

Fig.6.3.1. Implementarea listelor cu ajutorul structurii tablou.

O variant a unei astfel de implementri apare n secvena [6.3.1.a].


-----------------------------------------------------------{Implementarea listelor cu ajutorul structurii tablou} CONST lungMax=...; TYPE TipLista=RECORD [6.3.1.a] noduri:ARRAY[1..lungMax] OF TipNod; ultim:TipIndice END; TipPozitie=TipIndice; ------------------------------------------------------------

Secvena de program [6.3.1.b] prezint modul n care se pot implementa operaiile


specifice setului restrns de operatori: Fin, Insereaza, Suprima i Cauta utiliznd implementarea bazat pe tablouri a listelor.

Se fac urmtoarele precizri: Dac se ncearc inseria unui nod ntr-o list care deja a utilizat n ntregime

tabloul asociat se semnaleaz un mesaj de eroare Dac n cursul procesului de cutare nu se gsete elementul cutat, Cauta returneaz poziia ultim+1.

valoarea adevrat i care poate fi utilizat pentru tratarea erorii sau pentru ntreruperea execuiei programului. -----------------------------------------------------------{Implementarea setului restrns de operatori referitori la liste: FIN, INSEREAZA, SUPRIMA, CAUTA cu ajutorul structurii tablou} FUNCTION Fin(VAR L: TipLista): TipPozitie; BEGIN Fin:= L.ultim+1 {performana O(1)} END; {fin} PROCEDURE Insereaza(VAR L: TipLista; x: TipNod; p: TipPozitie; VAR er: boolean); {plaseaz pe x n poziia p a listei; performana O(n)} VAR q: TipPozitie; BEGIN [6.3.1.b] er:= false; IF L.ultim>=lungMax THEN BEGIN er:= true; mesaj('lista este plin') END ELSE IF (p>L.ultim+1) OR (p<1) THEN BEGIN er:= true; Write('poziia nu exist') END ELSE BEGIN FOR q:= L.ultim DOWNTO p DO L.noduri[q+1]:= L.noduri[q]; L.ultim:= L.ultim+1; L.noduri[p]:= x END END;{Insereaza} PROCEDURE Suprima(p: TipPozitie; VAR L: TipLista, er: boolean); {extrage elementul situat pe poziia p a listei} VAR q: TipPozitie; BEGIN {performana O(n)} er:= false; IF (p>L.ultim) OR (p<1) THEN BEGIN er:= true; mesaj('poziia nu exist') END ELSE BEGIN L.ultim:= L.ultim-1; FOR q:=p TO l.ultim DO L.noduri[q]:= L.noduri[q+1] END END;{Suprima}

S-a prevzut parametrul boolean er, care n caz de eroare se returneaz cu

FUNCTION Cauta(x: TipNod; L: TipLista): TipPozitie; {returneaz poziia lui x in lista} VAR q: TipPozitie; gasit: boolean; BEGIN q:= 1; gasit:= false; {performana O(n)} REPEAT IF L.noduri[q]=x THEN BEGIN Cauta:= q; gasit:= true END; q:= q+1 UNTIL gasit OR (q=L.ultim+1); IF NOT gasit THEN Cauta:= L.ultim+1 END;{Cauta} --------------------------------------------------------

n acest context, implementarea celorlali operatori nu ridic probleme deosebite: Operatorul Primul returneaz ntotdeauna valoarea 1; Operatorul Urmator returneaz valoarea argumentului incrementat cu 1; Operatorul Anterior returneaz valoarea argumentului diminuat cu 1 dup
ce n prealabil s-au fcut verificrile de limite; Operatorul Initializare face pe L.ultim egal cu 0.

La prima vedere pare tendenioas redactarea unor proceduri care s guverneze toate
accesele la o anumit structur de date.

Cu toate acestea acest lucru are o importan cu totul remarcabil, fiind legat de

utilizarea conceptului de "obiect" n exploatarea structurilor de date. Astfel, dac programatorul va redacta programele n termenii operatorilor care manipuleaz tipurile abstracte de date n loc de a face n mod direct uz de detaliile lor de implementare, Atunci pe de-o parte crete elegana i sigurana n funcionare a programului Pe de alt parte modificarea programului sau a structurii de date propriu-zise se poate realiza doar prin modificarea procedurilor care reglementeaz accesele la ea, fr a mai fi necesar cutarea i modificarea n program a locurilor din care se fac accese la respectiva structur. Aceast flexibilitate poate s joace de asemenea un rol esenial n cazul efortului necesar dezvoltrii unor produse software de mari dimensiuni.

6.3.2. Implementarea listelor cu ajutorul pointerilor

Listele liniare se pot implementa i cu ajutorul tipului de date pointer. Deoarece o list liniar este o structur dinamic ea poate fi definit n termeni
recursivi dup cum urmeaz [6.3.2.a]: -----------------------------------------------------------{Implementarea listelor cu ajutorul pointerilor implementarea ca structur de date recursiv}

TYPE TipPointerNod = ^TipNod; [6.3.2.a] TipNod = RECORD cheie: integer; urm: TipPointerNod; info: TipInfo END; TipLista = TipPointerNod; ------------------------------------------------------------

Dup cum se observ, n cazul definirii s-au pus n eviden trei cmpuri: O cheie care servete la identificarea nodului, Un pointer de nlnuire la nodul urmtor Un cmp info coninnd informaia util. n figura 6.3.2.a apare reprezentarea unei astfel de liste liniare mpreun cu o variabil
pointer inceput care indic primul nod. Lista liniar din figur are particularitatea c valoarea cheii fiecrui nod este egal cu numrul de ordine al nodului.
nceput

n nil

Fig.6.3.2.a. Exemplu de list liniar

n secvena [6.3.2.a] se observ c o list liniar poate fi definit ca i o structur


recursiv avnd o component de tip identic cu cel al structurii complete.

Caracteristica unei astfel de structuri rezid n prezena unei singure nlnuiri. n continuare, n cadrul acestui paragraf se prezint cteva tehnici referitoare la
implementarea listelor liniare ca i structuri recursive.

(1) Exist posibilitatea de a nlocui pointerul inceput care indic nceputul listei, cu o
variabil de tipNod avnd cmpurile cheie i info neasignate, iar cmpul urm indicnd primul nod al listei. simplific n anumite situaii prelucrarea listelor nlnuite (fig.6.3.2.b).
nod fictiv nceput 1 2

Utilizarea acestui nod de nceput, cunoscut sub denumirea de tehnica nodului fictiv

Fig.6.3.2.b. Implementarea listelor prin tehnica nodului fictiv

(2) Exist de asemenea posibilitatea utilizrii unui nod fictiv final pe post de fanion
avnd nlnuirea nil sau care se nlnuie cu el nsui [Se88]. fanion (fig.6.3.2.c).

Aceast tehnic de implementare este cunoscut sub denumirea de tehnica nodului


nod fanion nceput

Fig.6.3.2.c. Implementarea listelor prin tehnica nodului fanion

(3) O alt posibilitate de implementare o reprezint utilizarea a dou noduri fictive, unul
iniial i un altul final - tehnica celor dou noduri fictive (fig.6.3.2.d).
nod fictiv 1 nceput nod fictiv 2

Fig.6.3.2.d. Implementarea listelor cu ajutorul tehnicii celor dou noduri fictive

Fiecare dintre modalitile de implementare prezentate au avantaje specifice care vor


fi evideniate pe parcursul capitolului.

6.3.2.1. Tehnici de inserie a nodurilor i de creare a listelor nlnuite

Presupunnd c este dat o structur de date list, n continuare se prezint o secven


de program pentru inseria unui nod nou n list.

Iniial inseria se execut la nceputul listei. Se consider c inceput este o variabil pointer care indic primul nod al
----------------------------------------------------------------{Inserie la nceputul listei} [6.3.2.1.a] [1] new(auxiliar); [2] auxiliar^.urm:= inceput; {performanta O(1)} [3] inceput:= auxiliar; [4] inceput^.info= ....; -----------------------------------------------------------------

listei, Variabila auxiliar este o variabil pointer ajuttoare [6.3.2.1.a].

n figura 6.3.2.1.a se prezint grafic maniera n care se desfoar o astfel de inserie.

nceput

nceput

auxiliar

[1]

[2]

[3]

[4]

Fig.6.3.2.1.a. Inseria unui nod la nceputul unei liste nlnuite

Pe baza acestui fragment de program se prezint n continuare, crearea unei liste

nlnuite. Se pornete cu o list vid n care se nsereaz cte un nod la nceputul listei pn cnd numrul nodurilor devine egal cu un numr dat n. n secven s-a omis asignarea cmpurilor de informaie [6.3.2.1.b]:

fictiv 2

----------------------------------------------------------------{Crearea unei liste nlnuite} [6.3.2.1.b] inceput:= nil; {se pornete cu lista vid} WHILE n>0 DO BEGIN new(auxiliar); auxiliar^.urm:= inceput; inceput:= auxiliar; auxiliar^.cheie:= n; n:= n-1 END; -----------------------------------------------------------------

Datorit faptului c inseria noului nod are loc de fiecare dat la nceputul listei,
secvena de mai sus creeaz lista n ordinea invers a furnizrii cheilor. insereaz un nod la sfritul unei liste.

Dac se dorete crearea listei n ordine natural, atunci este nevoie de o secven care Aceast secven de program se redacteaz mai simplu dac se cunoate locaia

ultimului nod al listei. Teoretic lucrul acesta nu prezint nici o dificultate, deoarece se poate parcurge lista de la nceputul ei (indicat prin inceput) pn la detectarea nodului care are cmpul urm = nil. n practic aceast soluie nu este convenabil, deoarece parcurgerea ntregii liste este ineficient. Se prefer s se lucreze cu o variabil pointer ajuttoare ultim care indic mereu ultimul nod al listei, dup cum inceput indic mereu primul nod. liste liniare i concomitent l actualizeaz pe ultim este urmtoarea [6.3.2.1.c]:

n prezena lui ultim, secvena de program care insereaz un nod la sfritul unei

----------------------------------------------------------------{Inserie la sfritul unei liste nlnuite} [6.3.2.1.c] [1] new(auxiliar); [2] auxiliar^.urm:= nil; {performanta O(1)} [3] ultim^.urm:= auxiliar; [4] ultim:= auxiliar; [5] ultim^.info= ....; -----------------------------------------------------------------

Reprezentarea grafic a acestei inserri apare n figura 6.3.2.1.b.

ultim [4]

[3]

auxiliar

[1] Nil [5] [2]

Fig.6.3.2.1.b. Inseria unui nod la sfritul unei liste nlnuite

Referitor la secvena [6.3.2.1.c] se atrage atenia c ea nu poate insera un nod ntr-o


list vid. Acest lucru se observ imediat ntruct ultim^.urm nu exist n acest caz. alt procedeu spre exemplu conform lui [6.3.2.1.a] (inserie la nceputul listei). n continuare nodurile se pot aduga conform secvenei precizate.

Ca atare, la crearea unei liste n ordine natural, primul nod trebuie inserat printr-un

O alt posibilitate de a rezolva aceast problem o constituie utilizarea unei liste

implementate cu ajutorul tehnicii nodului fictiv. n acest caz, primul nod al listei exist ntotdeauna i ca atare ultim^.urm exist chiar i n cazul unei liste vide. nodului fanion. n acest caz nodul de inserat se introduce peste nodul fanion i se creeaz un nou nod fanion.

O a treia posibilitate este aceea de a utiliza o list implementat cu ajutorul tehnicii

n continuare se descrie inseria unui nod nou ntr-un loc oarecare al unei liste. Fie curent un pointer care indic un nod listei, Fie auxiliar o variabil pointer ajuttoare.

n aceste condiii inseria unui nod nou dup nodul indicat de curent se realizeaz
conform figurii 6.3.2.1.c n care nodul nou inserat are cheia 25.
10 20 30

curent [1] 25

auxiliar

[4] [3] [2]

10

20

30

curent

Fig.6.3.2.1.c. Inseria unui nod nou dup un nod precizat (curent)

Secvena de program care realizeaz aceast inserie apare n [6.3.2.1.d].


----------------------------------------------------------------{Inseria unui nod nou dup un nod precizat de indicatorul curent} [6.3.2.1.d] [1] new(auxiliar); [2] auxiliar^.urm:= curent^.urm; {performanta O(1)} [3] curent^.urm:= auxiliar; [4] auxiliar^.info= ...; -----------------------------------------------------------------

Dac se dorete ns inseria noului nod n lista liniar naintea unui nod indicat de
pointerul curent, apare o complicaie generat de imposibilitatea practic de a afla simplu, adresa predecesorului nodului indicat de curent. Dup cum s-a precizat deja, n practic nu se admite parcurgerea de la nceput a listei pn la detectarea nodului respectiv.

Aceast problem se poate ns rezolva simplu cu ajutorul urmtoarei tehnici: (1) Se insereaz un nod nou dup nodul indicat de pointerul curent, (2) Se asigneaz acest nod cu coninutul nodului indicat de curent, (3) Se creeaz cmpurile cheie i info pentru noul nod i se asigneaz cu
ele cmpurile corespunztoare ale vechiului nod indicat de pointerul curent.

Secvena de program care implementeaz aceast tehnic apare n [6.3.2.1.e] iar


reprezentarea sa grafic a n figura 6.3.2.1.d.
-----------------------------------------------------------------

{Inseria unui nod nou naintea unui nod precizat de indicatorul curent} [6.3.2.1.e] [1] new(auxiliar); [2] auxiliar^:= curent^; {performanta O(1)} [3] curent^.urm:= auxiliar; [4] curent^.info= ....; -----------------------------------------------------------------

auxiliar [2] 10

[1] 20

[3] 30

20


curent

10

15

30

[4]

(a)

(b) curent

Fig.6.3.2.1.d. Inseria unui nod nou n faa unui nod indicat

6.3.2.2. Tehnici de suprimare a nodurilor

Se consider urmtoarea problem: se d un pointer curent care indic un nod al

unei liste liniare nlnuite i se cere s se suprime succesorul nodului indicat de curent. auxiliar este o variabil pointer ajuttoare.

Aceasta se poate realiza prin intermediul fragmentului de program [6.3.2.2.a] n care

-----------------------------------------------------------{Suprimarea succesorului nodului precizat de indicatorul curent (varianta 1)} [1] auxiliar:= curent^.urm; {performanta O(1)} [2] curent^.urm:= auxiliar^.urm [6.3.2.2.a] [3] Dispose(auxiliar); ------------------------------------------------------------

Efectul execuiei aceste secvene de program se poate urmri n figura 6.3.2.2.a. Se observ c secvena de program de mai sus se poate nlocui cu urmtoarea
secven n care nu mai este necesar pointerul auxiliar: -----------------------------------------------------------{Suprimarea succesorului nodului precizat de indicatorul curent (varianta 2)} {performantaO(1)} curent^.urm:= curent^.urm^.urm; [6.3.2.2.b]

------------------------------------------------------------

Utilizarea pointerului auxiliar are ns avantajul c prin intermediul lui,

programatorul poate avea acces ulterior la nodul suprimat din list n vederea disponibilizrii zonei de memorie alocate lui, zon care n condiiile execuiei secvenei [6.3.2.2.b] se pierde.
[2]

10


curent

20

30

10

20

30


curent

[1]

(auxiliar) [3]

Fig.6.3.2.2.a. Tehnica suprimrii succesorului nodului indicat de curent

Revenind la problema suprimrii unui nod, se consider cazul n care se dorete

suprimarea nodului indicat de curent. Aici apare aceeai dificultate semnalat n paragraful anterior, generat de imposibilitatea aflrii simple a adresei predecesorului nodului indicat de pointerul curent.

Soluia se bazeaz pe aceeai tehnic: (1) Se copiaz coninutul nodului succesor n nodul indicat de curent, (2) Se suprim nodul succesor. Aceasta se poate realiza printr-o singur instrucie i anume [6.3.2.2.c]:
-----------------------------------------------------------{Suprimarea nodului precizat de indicatorul curent (varianta 1)} {performanta O(1)} curent^:= curent^.urm^; [6.3.2.2.c] ------------------------------------------------------------

Ca i nainte, aceast soluie prezint dezavantajul c pierde iremediabil zona de


memorie ocupat iniial de succesorul nodului indicat de pointerul curent.

O soluie care evit acest dezavantaj este cea prezentat n secvena [6.3.2.2.d]:
-----------------------------------------------------------{Suprimarea nodului precizat de indicatorul curent (varianta 2)} auxiliar:= curent^.urm; {performanta O(1)} curent^:= auxiliar^; Dispose(auxiliar); [6.3.2.2.d] ------------------------------------------------------------

Se remarc ns faptul c ambele tehnici de suprimare se pot aplica numai dac nodul
indicat de curent nu este ultimul nod al listei, respectiv numai dac curent^.urm nil.

Pentru a evita acest neajuns se pot utiliza alte modaliti de implementare a listei
nlnuite (cu nod fictiv sau cu nod fanion).

6.3.2.3. Traversarea unei liste nlnuite

Prin traversarea unei liste se nelege executarea unei anumite operaii asupra tuturor

nodurilor listei. Fie pointerul p care indic primul nod al listei i fie curent o variabil pointer auxiliar. Dac curent este un nod oarecare al listei se noteaz cu Prelucrare(curent) operaia amintit, a crei natur nu se precizeaz. direct a listei nlnuite

n aceste condiii fragmentul de program [6.3.2.3.a] reprezint traversarea n sens Fragmentul [6.3.2.3.b] reprezint traversarea unei liste nnuite n sens invers.
-----------------------------------------------------------{Traversarea unei liste nlnuite} [6.3.2.3.a] auxiliar:= p; WHILE auxiliar<>nil DO BEGIN Prelucrare(auxiliar^); auxiliar:= auxiliar^.urm END; -----------------------------------------------------------{Traversarea unei liste nlnuite n sens invers (variant recursiv} PROCEDURE TraversareInversa(curent: TipLista); BEGIN IF curent<>nil THEN [6.3.2.3.b] BEGIN TraversareInversa(curent^.urm); Prelucrare(curent^) END END;{TraversareInversa} ------------------------------------------------------------

O operaie care apare frecvent n practic, este cutarea adic depistarea unui nod
care are cheie egal cu o valoare dat x [6.3.2.3.c].

Cutarea este de fapt o traversare cu caracter special a unei liste.


-----------------------------------------------------------{Cutarea unui nod cu o cheie precizat x (varianta 1)} auxiliar:= inceput; [6.3.2.3.c] WHILE (auxiliar<>nil) AND (auxiliar^.cheie<>x) DO auxiliar:= auxiliar^.urm; IF auxiliar<>nil THEN {nodul cutat este indicat de auxiliar} ------------------------------------------------------------

Dac acest fragment se termin cu auxiliar = nil, atunci nu s-a gsit nici un nod
cu cheia x, altfel nodul indicat de auxiliar este primul nod avnd aceast cheie.

n legtur cu acest fragment de program trebuie subliniat faptul c n majoritatea

compilatoarelor el trebuie considerat incorect. ntr-adevr la evaluarea expresiei booleene din cadrul instruciunii WHILE, dac lista nu conine nici un nod cu cheia x, atunci n momentul n care auxiliar devine nil, nodul indicat de auxiliar nu exist. n consecin, funcie de implementare, se semnaleaz eroare, dei expresia boolean complet este perfect determinat, ea fiind fals din cauza prime i subexpresii.

Varianta [6.3.2.3.d] a operaiei de cutare este corect n toate cazurile, ea utiliznd o


variabil boolean ajuttoare notat cu gasit. -----------------------------------------------------------{Cutarea unui nod cu o cheie precizat x (varianta 2)} gasit:= false; auxiliar:= inceput; WHILE (auxiliar<>nil) AND NOT gasit DO IF auxiliar^.cheie=x THEN gasit:= true ELSE [6.3.2.3.d] auxiliar:= auxiliar^.urm; IF gasit=true THEN {nodul cutat este indicat de auxiliar} ------------------------------------------------------------

Dac la terminarea acestui fragment de program gasit=true atunci auxiliar


indic nodul cutat. n caz contrar nu exist un astfel de nod i auxiliar=nil.

Pornind de la cele prezentate n acest subparagraf, se pot concepe cu uurin funciile

i procedurile care materializeaz operatorii aplicabili listelor implementate cu ajutorul pointerilor att n varianta restrns ct i n varianta extins.

6.3.3. Implementarea listelor cu ajutorul cursorilor

n anumite limbaje de programare ca i FORTRAN sau ALGOL nu exist definit


tipul pointer.

De asemenea, n anumite situaii este mai avantajos pentru programator din punctul
de vedere al performanei codului implementat s evite utilizarea pointerilor. ntregi ce indic poziia n cadrul tablourilor.

n astfel de cazuri, pointerii pot fi simulai cu ajutorul cursorilor care sunt valori n cadrul acestui paragraf va fi abordat implementarea listelor nlnuite cu ajutorul
cursorilor, scop n care se definesc structurile de date [6.3.3.a].

n accepiunea acestei implementri:

(Zona) avnd elementele de TipElement Fiecare element al tabloului conine un cmp nodLista:TipNod i un cmp urm:TipCursor definit ca subdomeniu al tipului ntreg. TipLista este n aceste condiii identic cu TipCursor, orice list fiind precizat de fapt de un cursor. -----------------------------------------------------------{Implementarea listelor cu ajutorul cursorilor - structuri de date} TYPE TipCursor = 0..LungMax; TipElement = RECORD nodLista: TipNod; urm: TipCursor END; [6.3.3.a] TipLista = TipCursor; VAR Zona: ARRAY[1..LungMax] OF TipElement; L,M,disponibil: TipLista; ------------------------------------------------------------

Pentru toate listele ale cror noduri sunt de TipNod, se creaz un tablou

Spre exemplu dac L:TipLista este un cursor care indic nceputul unei liste
atunci

Valoarea lui Zona[L].nodLista reprezint primul nod al listei L Zona[L].urm este indexul (cursorul) care indic cel de-al doilea element al
listei L, .a.m.d. Valoarea zero a unui cursor, semnific legtura vid, adic faptul c nu urmeaz nici un element. tabloul Zona cu lungimea maxim 10.
L 5

n figura 6.3.3.a s-au reprezentat dou liste L = A, B, C i M = D, E care partajeaz


Zona

1 9

disponibil

1 2 3 4 5 6 7 8 9 10

D C A E B

7 4 0 6 8 0 0 3 10 2
urm

nodLista

Fig.6.3.3.a. Implementarea listelor nlnuite cu ajutorul cursorilor

Se observ: Acele locaii ale tabloului care nu apar n nici una din cele dou liste sunt

nlnuite ntr-o alt list numit disponibil. Aceast list este necesar fie: Pentru a furniza o nou locaie n vederea realizrii unei inserii, Pentru a depozita o locaie a tabloului rezultat din suprimarea unui nod n vederea unei reutilizri ulterioare.

Avem de fapt de-a face cu o gestionare dinamic a zonei de memorie alocat listelor
(tabloul Zona) realizat de ctre programator. Astfel, pentru a insera un nod x n lista L: (1) Se suprim prima locaie a listei disponibil (2) Se insereaz n poziia dorit a listei L, actualiznd valorile cursorilor implicai. (3) Se asigneaz cmpul nodLista al acestei locaii cu valoarea lui x. Suprimarea unui element x din lista L presupune: (1) Extragerea locaiei care-l conine din lista L (2) nlnuirea sa n capul listei disponibililor.

Att inseria ct i suprimarea sunt de fapt cazuri speciale ale urmtoarei situaii: Fie dou liste precizate prin cursorii sursa i destinatie Fie y prima locaie a listei indicate de sursa. Se suprim y din lista indicat de sursa i se nlnuie pe prima poziie a

listei indicate de destinatie. Acest lucru se realizeaz astfel: (1) Se atribuie valoarea lui sursa cursorului destinatie care astfel l va indica pe y, (2) Se atribuie lui sursa valoarea legturii lui y, (3) Legtura lui y se asigneaz cu fosta valoare a lui destinatie.

Reprezentarea grafic a acestei aciuni apare n fig.6.3.3.b unde sunt prezentate


legturile nainte (linie continu) i dup (linie ntrerupt) desfurarea ei,
sursa

destinaie

temp

Fig.6.3.3.b. Mutarea unui nod dintr-o list nlnuit (surs) n alta list nlnuit (destinaie)

n [6.3.3.b] apare codul Pascal al funciei Muta care o implementeaz.

-----------------------------------------------------------{Liste nlnuite implementate cu ajutorul cursorilor operatorul de gestionare dinamic a memoriei} FUNCTION Muta(VAR sursa,destinatie:TipCursor):boolean; {mut elementul indicata de sursa n faa celui indicat de destinatie} VAR temp: TipCursor; BEGIN

IF sursa=0 THEN {performanta O(1)} BEGIN mesaj('locatia nu exista'); Muta:= false END ELSE [6.3.3.b] BEGIN temp:= destinatie; destinatie:= zona[destinatie].urm; zona[destinatie].urm:= temp; Muta:= true END END;{Muta} ------------------------------------------------------------

n secvena [6.3.3.c] se prezint pentru exemplificarea celor mai sus prezentate: Implementarea operatorilor Insereaza i Suprima Procedura Init care nlnuie elementele tabloului Zona n lista indicat de
disponibil Procedurile omit verificarea erorilor. Se precizeaz c variabila inceput indic nceputul listei curente. -------------------------------------------------------{Liste nlnuite implementate cu ajutorul cursorilor operatorii Insereaza, Suprima i Init} PROCEDURE Insereaza(x: TipNod; p: TipPozitie; VAR inceput: TipPozitie); BEGIN {performana O(1)} IF p=0 THEN BEGIN {se insereaz pe prima poziie} IF Muta(disponibil,inceput) THEN zona[inceput].nodLista:= x [6.3.3.c] END ELSE {se insereaz ntr-o poziie diferit de prima} IF Muta(disponibil,zona[p].urm) THEN {locaia pentru x va fi indicat de ctre zona[p].urm} zona[zona[p].urm].nodLista:= x END;{Insereaza} PROCEDURE Suprima(p:TipPozitie;VAR inceput:TipPozitie); BEGIN IF p=0 THEN {performanta O(1)} Muta(inceput,disponibil) ELSE Muta(zona[p].urm,disponibil) END;{Suprima} PROCEDURE Init; {initializeaza elementele zonei nlnuindu-le n lista de disponibili} VAR i: TipCursor; BEGIN {performana O(n)}

FOR i:=lungMax-1 DOWNTO 1 DO zona[i].urm:= i+1; disponibil:= 1; zona[lungMax].urm:= 0 END;{Init} -----------------------------------------------------------6.3.4. Implementarea listelor cu ajutorul referinelor

n limbajele orientate pe obiecte care nu definesc tipul pointer, implementarea


structurilor de date recursive n general i a listelor nlnuite n mod special se poate realiza foarte elegant cu ajutorul referinelor.

Astfel, pentru a implementa o list nlnuit n limbajul JAVA, ca punct de pornire se

poate defini clasa Nod, care specific formatul obiectelor asociate nodurilor listei [6.3.4.a]. -----------------------------------------------------------class Nod { private Object element; //elementul memorat in nodul curent private Nod urm; //referina la nodul urmtor al listei // constructori [6.3.4.a] Nod() { //creaz un nod cu un element nul i cu o referin nul this(null,null); } public Nod(Object e, Nod n) { //creaz un nod cu un anumit element i o anumit referin urm element = e; urm = n; } //metode de actualizare void setElement(Object elemNou) { element = elemNou; } void setUrm(Nod urmNou) { urm = urmNou } //metode de acces Object getElement() { return element; } Node getUrm() { return urm; } } --------------------------------------------------------

n continuare pornind de la clasa Nod se poate defini o clas ListaInlantuita


care:

Pstreaz o referin la nodul de nceput al listei

n mod opional poate pstra i alte informaii referitoare la list cum ar fi o


referin la ultimul element al listei i / sau numrul de noduri.

n secvena [6.3.4.b] apare un fragment dintr-o astfel de clas n care se prezint


structura de date list ca atare precum i unele dintre metodele care implementeaz operatorii specifici. -----------------------------------------------------------public class ListaInlantuita implements Lista { private Nod inceput; //referin la nceputul listei private int dimensiune; //numrul de elemente al listei public ListaInlantuita() { //Iniializeaz lista inceput = null; dimensiune = 0; [6.3.4.b] } public int dimensiune() { //Returneaz dimensiunea curent return dimensiune; } public boolean listaVida() { //Returneaz true dac lista este vid if (inceput == null) return true; return false; } public void InserInceput(Object elem){ //Inserie la nceputul listei Nod n = new Nod(); n.setElement(elem); n.setUrm(inceput); inceput = n; dimensiune++; } public Object SuprimaPrimul() { //Suprimarea elementului de la nceputul listei Object obj; if (inceput.listaVida()) //se trateaz excepia in mod specific; obj = inceput.getElement(); inceput = inceput.getUrm(); dimensiune--; return obj; } //alte metode ... } -----------------------------------------------------------6.3.5. Comparaie ntre metodele de implementare a listelor

Este greu de precizat care dintre metodele de implementare a listelor este mai bun,
deoarece rspunsul depinde de: Limbajul de programare utilizat, Operaiile care se doresc a fi realizate,

Frecvena cu care operaiile sunt invocate, Constrngerile de timpul de acces, .a. n orice caz se pot formula urmtoarele observaii: Implementarea bazat pe tablouri sau pe cursori necesit specificarea dimensiunii

maxime a listei n momentul compilrii. Dac nu se poate determina o astfel de limit superioar a listei se recomand implementarea bazat pe pointeri sau referine. Anumite operaii dureaz mai mult ntr-o implementare dect n alta. Spre exemplu inseria i suprimarea au o durat constant la implementarea nlnuit (O(1)), dar necesit o perioad de timp proporional cu numrul de noduri care urmeaz nodului n cauz n implementarea bazat pe tablouri (O(n)). n schimb operatorul Anterior necesit un timp constant n implementarea prin tablouri (O(1)) i un timp ce depinde de lungimea total, respectiv de poziia nodului n implementarea bazat pe pointeri, referine sau cursori (O(n)). Implementarea bazat pe tablouri sau pe cursori poate fi ineficient din punctul de vedere al utilizrii memoriei, deoarece ea ocup tot timpul spaiul maxim solicitat, indiferent de dimensiunea real a listei la un moment dat. Implementarea nlnuit utilizeaz n fiecare moment spaiul de memorie strict necesar lungimii curente a listei, dar necesit n plus spaiul pentru nlnuire n cadrul fiecrui nod.

n funcie de circumstane una sau alta dintre implementri poate fi mai mult sau mai
puin avantajoas.

6.4. Aplicaii ale listelor nlnuite


6.4.1. Problema concordanei

Formularea problemei: Se d un text format dintr-o succesiune de cuvinte, Se baleeaz textul i se depisteaz cuvintele. Pentru fiecare cuvnt se verific dac este sau nu la prima apariie. n caz c este la prima apariie, cuvntul se nregistreaz, n caz c el a mai fost gsit, se incrementeaz un contor asociat cuvntului

care memoreaz numrul de apariii. n final se dispune de toate cuvintele distincte din text i de numrul de apariii al fiecruia.

Se menioneaz c aceast problem este important, deoarece ea reflect ntr-o form


simplificat una din activitile pe care le realizeaz un compilator i anume construcia listei identificatorilor.

Programul Concordanta [6.4.1.a]: Construiete o list nlnuit coninnd cuvintele distincte ale unui text surs. Iniial lista este vid, ea urmnd a fi completat pe parcursul parcurgerii
textului.

este realizat de procedura Cauta. Pentru simplificare, se presupune c "textul" este de fapt o succesiune de numere ntregi pozitive care reprezint "cuvintele". Cuvintele se citesc de la tastatur ele terminndu-se cu un cuvnt fictiv, n cazul de fa numrul zero care precizeaz sfritul textului. Cutarea n list se face conform celor descrise n paragraful &6.3.2.3 cu deosebirea c variabila gasit s-a nlocuit cu negata ei. Variabila pointer inceput, indic tot timpul nceputul listei. Se precizeaz faptul c inserrile se fac la nceputul listei iar procedura Tiparire reprezint un exemplu de traversare a unei liste n sensul celor precizate anterior. -----------------------------------------------------------PROGRAM Concordanta; TYPE TipReferinta = ^TipNod; [6.4.1.a] TipNod = RECORD cheie: integer; numar: integer; urmator: TipReferinta END; VAR cuv: integer; inceput: TipReferinta; {*} PROCEDURE Cauta(x:integer; VAR inceput:TipReferinta); VAR q: TipReferinta; negasit: boolean; BEGIN q:= inceput; negasit:= true; WHILE (q<>NIL) AND negasit DO IF q^.cheie=x THEN negasit:= false ELSE q:= q^.urmator; IF negasit THEN {nu s-a gsit, deci inserie} BEGIN q:= inceput; new(inceput); WITH inceput^ DO BEGIN cheie:= x; numar:= 1; urmator:= q END END ELSE {s-a gsit, deci incrementare} q^.numar:= q^.numar+1 END;{Cauta} PROCEDURE Tiparire(q: TipReferinta); VAR r: TipReferinta; BEGIN r:= q; WHILE r<>NIL DO BEGIN WriteLn(r^.cheie,r^.numar); r:= r^.urmator

Procesul de cutare n list mpreun cu inseria sau incrementarea contorului

END END;{Tiparire} BEGIN inceput:= NIL; {**} Read(cuv); WHILE cuv<>0 DO BEGIN Cauta(cuv,inceput); Read(cuv) END; Tiparire(inceput) END. ------------------------------------------------------------

n continuare se descrie o optimizare a procedurii de cutare prin utilizarea "metodei


fanionului".

n acest scop, lista cuvintelor ntlnite se prelungete cu un nod suplimentar numit


fanion (fig 6.4.1.a).

Lista iniial

fanion nod fictiv

nceput

fanion

nceput

Fig.6.4.1.a. Implementarea unei liste nlnuite utiliznd tehnica nodului fanion

Tehnica de cutare este similar celei utilizate n cazul tablourilor liniare (&1.4.2.1). Pentru aplicarea procedurii de cutare optimizate Cauta1, n programul
Concordanta [6.4.1.a] trebuiesc efectuate dou modificri: La declararea variabilelor, n locul indicat prin [*], se adaug declararea nodului fanion fanion: tipReferinta; Se modific iniializarea listei de cuvinte indicat n cadrul programului prin [**], respectiv instruciunea inceput:= nil se nlocuiete cu secvena new(inceput), fanion:= inceput. Prin aceasta lista de cuvinte conine de la bun nceput un nod.

n aceste condiii, procedura Cauta1 apare n secvena [6.4.1.b]. Fa de varianta [6.4.1.a] condiia din cadrul instruciei WHILE este mai
simpl, realizndu-se un ctig simitor de timp.

astfel nct s reflecte noua situaie. -----------------------------------------------------------{Cutare n liste nlnuite utiliznd metoda "fanionului"} PROCEDURE Cauta1(x:integer, VAR inceput:TipReferinta); VAR q: TipReferinta; BEGIN q:= inceput; fanion^.cheie:= x; WHILE q^.cheie<>x DO q:= q^.urmator; IF q=fanion THEN {elementul nu s-a gasit} BEGIN q:= inceput; new(inceput); [6.4.1.b] WITH inceput^ DO BEGIN cheie:= x; numar:= 1; urmator:= q END END ELSE {s-a gasit} q^.numar:= q^.numar+1 END;{Cauta1} -----------------------------------------------------------6.4.2. Crearea unei liste ordonate. Tehnica celor doi pointeri

Desigur trebuie modificat i condiia de test din procedura Tiparire

n cazul acestui paragraf se abordeaz problema crerii unei liste astfel nct ea s fie
mereu ordonat dup chei cresctoare. Cu alte cuvinte, odat cu crearea listei, aceasta se i sorteaz.

n contextul problemei concordanei, acest lucru se realizeaz simplu deoarece nainte

de inseria unui nod, acesta trebuie oricum cutat n list. (1) Dac lista este sortat, atunci cutarea se va termina cu prima cheie mai mare dect cea cutat, apoi n continuare se insereaz nodul n poziia care precede aceast cheie. (2) n cazul unei liste nesortate, cutarea nseamn parcurgerea ntregii liste, dup care nodul se insereaz la nceputul listei. procesul de cutare devine mai eficient.

Dup cum se vede, procedeul (1) nu numai c permite obinerea listei sortate, dar Este important de observat faptul c la crearea unor structuri tablou sau fiier nu
exist posibilitatea simpl de a le obine gata sortate.

n schimb la listele liniare sortate nu exist echivalentul unor metode de cutare


avansate (spre exemplu cutarea binar) care sunt foarte eficiente la tablourile sortate. indicat de pointer.

Inseria unui nod ntr-o list sortat presupune inseria unui nod naintea celui

O modalitate de rezolvare a unei astfel de situaii a fost prezentat n paragraful


[&6.3.2.1].

n continuare se va descrie o alt tehnic de inserie bazat pe utilizarea a doi pointeri

q1 i q2, care indic tot timpul dou noduri consecutive ale listei, conform figurii 6.4.2.a.
nceput

q2

q1

Fig.6.4.2.a. Traversarea unei liste nlnuite utiliznd doi pointeri.

Cei doi pointeri avanseaz simultan de-a lungul listei pn cnd cheia nodului indicat

de q1 devine mai mare sau egal cu cheia de inserat x, Acest lucru ce se va ntmpla cu certitudine, cel mai trziu n momentul n care q1 devine egal cu fanionul. Dac n acest moment cheia nodului indicat de q1 este strict mai mare dect x sau q1 = fanion, atunci trebuie inserat un nou nod n list ntre nodurile indicate de q2 i q1, n caz contrar, s-a gsit cheia cutat i trebuie incrementat contorul q1^.numar. presupune existena iniial n list a cel puin dou noduri, deci cel puin un nod n afar de cel indicat de fanion. (fig.6.4.2.b).

n implementarea acestui proces se va ine cont de faptul c, funcionarea sa corect

Din acest motiv lista se va implementa utiliznd tehnica celor dou noduri fictive
nceput

nod fictiv

fanion

lista iniial

fanion

inceput

nod fictiv

Fig.6.4.2.b. Implementarea unei liste utiliznd tehnica celor dou noduri fictive.

n vederea realizrii acestor deziderate programul Concordanta (secvena [6.4.1.a])


trebuie modificat dup cum urmeaz; (1) Se adaug la partea de declaraie a variabilelor, (locul indicat cu [*]), declaraia fanion: TipRef; (2) Se nlocuiete instrucia inceput:=nil (indicat prin [**]) cu urmtoarea secven de instruciuni care creaz lista vid specific implementrii prin tehnica celor doi pointeri [6.4.2.a]: -----------------------------------------------------------new(inceput); new(fanion); [6.4.2.a] inceput^.urmator:= fanion;
-----------------------------------------------------------------

(3) Se nlocuiete procedura Cauta cu Cauta2 secvena [6.4.2.b].


-----------------------------------------------------------{Cutare n liste nlnuite utiliznd tehnica celor doi pointeri} PROCEDURE Cauta2(x: integer; inceput: TipReferinta); VAR q1,q2,q3: TipReferinta; BEGIN q2:= inceput; q1:= q2^.urmator; fanion^.cheie:= x; WHILE q1^.cheie<x DO BEGIN q2:= q1; [6.4.2.b] q1:= q2^.urmator END; IF (q1^.cheie=x) AND (q1<>fanion) THEN q1^.numar:= q1^.numar+1 ELSE BEGIN {se creeaz un nou nod indicat de q3 i se insereaz ntre q2^ si q1^} new(q3); WITH q3^ DO BEGIN cheie:= x; numar:= 1; urmator:= q1 END; q2^.urmator:= q3 END END;{Cauta2} ------------------------------------------------------------

Aceste modificri conduc la crearea listei sortate. Trebuie ns observat faptul c beneficiul obinut n urma sortrii este destul de
limitat. El se manifest numai n cazul cutrii unui nod care nu se gsete n list;

Aceast operaie necesit parcurgerea n medie a unei jumti de list n

cazul listelor sortate n comparaie cu parcurgerea ntregii liste dac aceasta nu este sortat. La cutarea unui nod care se gsete n list se parcurge n medie jumtate de list indiferent de faptul c lista este sortat sau nu. Aceast concluzie este valabil dac se presupune c succesiunea cheilor sortate este un ir de variabile aleatoare cu distribuii identice.

n definitiv, cu toate c sortarea listei practic nu cost nimic, se recomand a fi


utilizat numai n cazul unor texte cu multe cuvinte distincte n care acelai cuvnt se repet de puine ori.

6.4.3. Cutarea n liste cu reordonare

Utilizarea structurii de date list liniar este deosebit de avantajoas n activitatea de


compilare la crearea i exploatarea listei identificatorilor. n general la parcurgerea unui text surs, compilatorul insereaz n list fiecare identificator declarat mpreun cu o serie de informaii necesare procesului de compilare. Apariiile ulterioare ale unor astfel de identificatori, presupun cutarea lor n list, n vederea obinerii informaiilor necesare n procesul de generare a codului. La prsirea domeniului lor de existen, identificatorii sunt suprimai din lista indentificatorilor. de cutare ntr-o list nlnuit utilizabil cu precdere la crearea i exploatarea listei identificatorilor de ctre un compilator.

n continuare, pornind de la contextul mai sus precizat, se va prezenta o alt tehnic

Studiindu-se un numr mare de programe surs considerate tipice, s-a fcut

urmtoarea constatare experimental: Apariiile unui identificator oarecare n textul surs al unui program au tendina de a se "ngrmdi" n anumite locuri ale programului, n timp ce n restul textului, apariia aceluiai identificator se produce cu o probabilitate mai redus. Acesta este principiul localizrii. Conform acestui principiu, apariia unui identificator oarecare, poate fi urmat curnd, cu mare probabilitate, de una sau mai multe reapariii. identificatorilor care s conduc la o ameliorare substanial a procesului de cutare.

Pornind de la aceast constatare se poate concepe o metod de construire a listei Metoda, denumit "cutare n list cu reordonare", const n aceea c ori de cte ori

un identificator se caut i se gsete n list, el se "mut" la nceputul listei, astfel nct la proxima apariie el va fi gsit imediat. Cu alte cuvinte, lista se reordoneaz dup fiecare cutare finalizat cu gsirea nodului. Dac un nod nu este gsit n list, el se insereaz la nceputul acesteia.

n secvena [6.4.3.a] apare procedura Cauta3 care implementeaz aceast tehnic. Pointerul q1 indic nodul gsit iar pointerul q2 nodul precedent.

Structurile de date sunt cele definite n [6.4.1.a].


-----------------------------------------------------------{Cutare n liste cu reordonare} PROCEDURE Cauta3(x:integer; VAR inceput:TipReferinta); VAR q1,q2,q3: TipReferinta; BEGIN q1:= inceput; fanion^.cheie:= x; IF q1=fanion THEN {se insereaza primul nod} BEGIN new(inceput); WITH inceput^ DO BEGIN cheie:= x; numar:= 1; urmator:= fanion END END ELSE IF q1^.cheie=x THEN q1^.numar:= q1^.numar+1 ELSE BEGIN {cautarea} [6.4.3.a] REPEAT q2:= q1; q1:= q2^.urmator UNTIL q1^.cheie=x; IF q1=fanion THEN {insertie} BEGIN q2:= inceput; new(inceput); WITH inceput^ DO BEGIN cheie:= x; numar:= 1; urmator:= q2 END END ELSE {s-a gasit, deci reordonare} BEGIN q1^.numar:= q1^.numar+1; q2^.urmator:= q1^.urmator; q1^.urmator:= inceput; inceput:= q1 END END END;{Cauta3} ------------------------------------------------------------

Se observ c reordonarea presupune existena a cel puin dou noduri n list.

Acestea pot fi un nod real i nodul fanion. Deoarece n implementarea realizat s-a utilizat tehnica nodului fanion, n cadrul procedurii s-a prevzut n mod explicit o secven care trateaz inseria primului nod al listei.

n acest context, iniializarea listei se face nlocuind instruciunea notat cu


[**] n programul [6.4.1.a] cu secvena inceput;.

new(inceput); fanion:=

Cercetri empirice n care s-au comparat timpii de rulare ai programului

Concordanta, utiliznd lista sortat (Cauta2) respectiv tehnica cutrii cu reordonare (Cauta3), au pus n eviden un factor de ameliorare n favoarea celei din urm cuprins ntre 1.37 i 4.7 [Wi76].

Ameliorarea mai pronunat apare la textele mai lungi.


6.4.4. Sortarea topologic

Un exemplu de utilizare flexibil a unor structuri de date dinamice este procesul sortrii
topologice.

Acesta este un proces de sortare, al unei mulimi elemente peste care a fost definit o

relaie de ordonare parial, adic ordonarea este valabil pentru anumite perechi de elemente, nu pentru toate.

Aceasta este de fapt o situaie real ilustrat prin urmtoarele exemple: (1) ntr-un dicionar, cuvintele sunt definite n termenii altor cuvinte. Dac un cuvnt w este definit n termenii cuvntului v, se va nota

aceasta prin vw. Sortarea topologic a cuvintelor n dicionar nseamn aranjarea lor ntr-o astfel de ordine nct s nu existe nici o referin n fa (adic s nu fie utilizat nici un cuvnt nainte ca el s fi fost definit). (2) Un task (spre exemplu un proiect ingineresc) poate fi de regul divizat n subtaskuri. Terminarea unor subtaskuri trebuie n mod uzual s precead lansarea n execuie a altora. Dac un subtask v trebuie s precead un alt subtask w aceasta se va nota vw. Sortarea topologic a subtaskurilor nseamn aranjarea lor ntr-o astfel de ordine nct la iniializarea ori crui subtask, toate subtaskurile care-l condiioneaz s fie terminate.

(3) ntr-un program anumite proceduri pot conine apeluri la alte proceduri. Dac o procedur v este apelat de o procedur w, aceasta se va
preciza prin notaia vw. Sortarea topologic presupune un astfel de aranjament al declaraiilor de proceduri nct s nu existe nici o referire nainte.

n general, ordonarea parial a unei mulimi M presupune existena unei relaii


ntre elementele lui M.

Aceast relaie este desemnat prin simbolul , care nseamn "precede" i satisface
urmtoarele trei proprieti: (1) Dac xy i yz atunci xz (tranzitivitate); (2) Dac xy, atunci yx (asimetrie);

(3) xx (nereflexivitate). Se presupune c mulimea M a elementelor care urmeaz a fi sortate topologic este
finit.

n aceste condiii o relaie de ordine parial poate fi ilustrat printr-o diagram sau

graf n care nodurile desemneaz elementele iar sgeile orientate reprezint relaiile dintre ele. Un exemplu apare n fig.6.4.4.a.
1 2 6 4 8 3 5 9 7

1 0

Fig.6.4.4.a. Mulime de elemente parial ordonat

Problema sortrii topologice este aceea de a transforma ordinea parial ntr-o ordine
liniar.

Din punct de vedere grafic, aceasta implic rearanjarea nodurilor ntr-un ir astfel
nct toate sgeile s indice acelai sens, spre exemplu spre dreapta (fig.6.4.4.b).

10

Fig.6.4.4.b. Ordonarea liniar a unei mulimi parial ordonate

Proprietile (1) i (3) ale relaiei de ordonare parial garanteaz faptul c graful nu
conine bucle.

Aceasta este de fapt condiia necesar i suficient ca ordonarea liniar respectiv


sortarea topologic s fie posibil.

Algoritmul sortrii topologice nu este complicat:

Se ncepe cu selectarea oricrui element care nu este precedat de nici un altul


(trebuie s existe cel puin unul, altfel graful conine o bucl). Elementul este extras din mulimea M i este plasat n lista care se creeaz. Mulimea rezultat rmne parial ordonat i n consecin poate fi aplicat din nou acelai algoritm, pn cnd ea devine vid. reprezentarea mulimii M i a relaiei de ordonare.

Pentru a descrie acest algoritm mai riguros, trebuiesc precizate structurile de date, Este evident faptul c alegerea acestor reprezentri este determinat de operaiile care

urmeaz s fie realizate, n mod particular de operaia de selecie a elementelor care nu au nici un predecesor.

n acest scop, fiecare element trebuie reprezentat prin trei caracteristici: (1) Cheia de identificare, (2) Setul su de succesori (3) Numrul predecesorilor si. Deoarece numrul n al elementelor mulimii M nu este cunoscut a priori, mulimea
iniial este de regul organizat ca o list nlnuit numit lista principalilor. n consecin, fiecare element trebuie s mai conin legtura la elementul urmtor al listei. Pentru simplificare se va presupune c cheile sunt numere ntregi (nu neaprat consecutive) cuprinse ntre 1 i n. nlnuit. Fiecare element al listei succesorilor este descris prin identificatorul propriu (legtur la lista principalilor) i printr-o legtur la urmtorul element al listei succesorilor numit i lista secundarilor. dat, se vor numi principali,

n mod analog, mulimea succesorilor unui element va fi reprezentat tot ca o list

Nodurile listei principale, n care fiecare element al mulimii M apare exact o singur Nodurile corespunztoare elementelor din lista succesorilor, se vor numi secundari. n aceste condiii se pot defini urmtoarele structuri de date [6.4.4.a].
-----------------------------------------------------------{Sortarea topologic - structuri de date} TYPE TipPointerPrincipali = ^TipNodPrincipal; TipPointerSecundari = ^TipNodSecundar; TipCheie = integer; TipNodPrincipal = RECORD {Nod n Lista principalilor} cheie: TipCheie; contor: integer; urm: TipPointerPrincipali; [6.4.4.a] secund: TipPointerSecundari END; TipNodSecundar = RECORD {Nod n Lista secundarilor}

id: TipPointerPrincipali; urm: TipPointerSecundari END; ------------------------------------------------------------

Se presupune c mulimea M i relaiile sale de ordonare sunt furnizate iniial ca o

secven de perechi de chei. Astfel, datele de intrare pentru exemplul din fig.6.4.4.a apar n [6.4.4.b] unde simbolul este prezent numai din motive de claritate. -----------------------------------------------------------1 2 2 4 4 6 2 10 4 8 6 3 1 3 [6.4.4.b] 3 5 5 8 7 5 7 9 9 4 9 10 ------------------------------------------------------------

Prima parte a programului de sortare topologic trebuie s citeasc datele de intrare i

s construiasc structurile de date aferente. Aceasta se realizeaz prin citirea succesiv a perechilor de elemente x i y (x y). Pointerii corespunztori celor dou elemente n lista principalilor sunt notai cu p i q. Nodurile corespunztoare sunt localizate prin cutare n lista principalilor i dac nu sunt gsite sunt inserate n aceast list. Aceast sarcin este ndeplinit de funcia Caut. n continuare n lista secundarilor lui x se adaug un nod nou a crui cmp identificator se va referi la nodul y, iar contorul de predecesori ai lui y va fi incrementat cu 1.

Aceast prim parte a algoritmului se numete FazaInitiala i apare n secvena

[6.4.4.c]. Dup cum se observ, acest fragment de program utilizeaz funcia Caut(w) care furnizeaz pointerul nodului din lista principalilor care are cheia w. Se presupune c secvena perechilor de chei furnizate la intrare se ncheie cu o cifr 0. -----------------------------------------------------------inceput,sfarsit,p,q: TipPointerPrincipali; t: TipPointerSecundari; x,y: TipCheie {chei} z: integer; {contor} {Sortarea topologic - Faza iniial} Read(x); new(inceput); sfirsit:= inceput; z:= 0; WHILE x<>0 DO BEGIN [6.4.4.c] Read(y); p:= Caut(x); q:= Caut(y); new(t); t^.id:= q; t^.urm:= p^.secund; p^.secund:= t; q^.contor:= q^.contor+1; Read(x) END; ------------------------------------------------------------

Dup ce structura de date a fost construit, urmeaz realizarea procesului de sortare


topologic descris anterior.

Sortarea topologic const n principiu n selecia repetat a unui element cu


contorul de predecesori nul, ca atare, pentru nceput se caut astfel de noduri i se nlnuie ntr-o list numit lista principalilor cu zero predecesori. ntruct se presupune c nu mai este nevoie de lista iniial a principalilor, cmpul de nlnuire urm va fi utilizat pentru a nlnui principalii cu zero predecesori. frecvent n procesul de prelucrare a listelor.

Aceast operaie prin care se nlocuiete o list printr-o alt list apare deosebit de Ea apare n detaliu n secvena [6.4.4.d] n care, din raiuni de convenien noua list
este construit n sens invers prin inserie n fa. -----------------------------------------------------------{Cautarea principalilor cu zero predecesori} p:= inceput; inceput:= nil; WHILE p<>sfirsit DO BEGIN [6.4.4.d] q:= p; p:= q^.urm; IF q^.contor=0 THEN BEGIN {insereaza q^ in noua list} q^.urm:= inceput; inceput:= q END END; ------------------------------------------------------------

n continuare se poate trece la sortarea topologic propriu-zis. Rafinarea n dou etape a algoritmului de sortare apare n [6.4.4.e,f] i poart
denumirea de Faz de ieire. Ambele secvene constituie exemple de traversare a listelor. n fiecare moment, variabila p desemneaz nodul din lista principalilor al crui contor trebuie decrementat i testat. -----------------------------------------------------------{Faza de iesire} q:= inceput; [6.4.4.e] WHILE q<>nil DO BEGIN {tipareste elementul curent apoi suprima-l} WriteLn(q^.cheie); z:= z-1; t:= q^.secund; q:= q^.urm; *decrementeaz contorul de predecesori n toi succesorii si din lista de secundari; dac vreun contor devine zero, insereaz acel nod n lista principalilor; END; -----------------------------------------------------------{Decrementeaz contorul de predecesori ...} WHILE t<>nil DO BEGIN [6.4.4.f] p:= t^.id; p^.contor:= p^.contor-1; IF p^.contor=0 THEN BEGIN {inserare p^ in lista principalilor} p^.urm:= q; q:= p

END; t:= t^.urm END; ----------------------------------------------------------- Contorul z a fost introdus pentru a numra nodurile principale generate n Faza initiala ([6.4.4.c]). Acest contor este decrementat de fiecare dat cnd un element principal este scris n Faza de iesire. Valoarea sa trebuie s devin n cele din urm zero. Dac acest lucru nu se ntmpl, nseamn c n list mai exist elemente dintre care nici unul nu este lipsit de predecesori. n acest caz, n mod evident mulimea M nu este parial ordonat i n consecin nu poate fi sortat topologic.

Faza de iesire este un exemplu de proces care gestioneaz o list care


pulseaz, n care elementele sunt inserate i suprimate ntr-o ordine impredictibil.

Codul integral al programului TopSort apare n secvena [6.4.4.g].


-----------------------------------------------------------PROGRAM Topsort; TYPE TipPointerPrincipali = ^TipNodPrincipal; TipPointerSecundari = ^TipNodSecundar; TipCheie = integer; TipNodPrincipal = RECORD cheie: TipCheie; contor: integer; urm: TipPointerPrincipali; [6.4.4.g] secund: TipPointerSecundari END; TipNodSecundar = RECORD id: TipPointerPrincipali; urm: TipPointerSecundari END; VAR inceput,sfirsit,p,q: TipPointerPrincipali; t: TipPointerSecudari; z: integer; {contor noduri} x,y: TipCheie; {chei} FUNCTION Caut(w: TipCheie): TipPointerPrincipali; {furnizeaza referinta la noul principal cu cheia w} VAR h: TipPointerPrincipali; BEGIN h:= inceput; sfirsit^.cheie:= w; {fanion} WHILE h^.cheie<>w DO h:= h^.urm; IF h=sfirsit THEN BEGIN {nu exista nici un element cu cheia w in lista} new(sfirsit); z:= z+1; h^.contor:= 0; h^.secund:= nil; h^.urm:=sfirsit END; Caut:= h END;{Caut} BEGIN {se iniializeaz lista principalilor cu un nod

fictiv} new(inceput); sfirsit:= inceput; z:= 0; {Faza initiala} Read(x); WHILE x<>0 DO BEGIN Read(y); WriteLn(x,y); p:= Caut(x); q:= Caut(y); new(t); t^.id:= q; t^.urm:= p^.secund; p^.secund:= t; q^.contor:= q^.contor+1; Read(x) END; {Cutarea principalilor cu zero predecesori (contor=0)} p:= inceput; inceput:= nil; WHILE p<>sfirsit DO BEGIN q:= p; p:= p^.urm; IF q^.contor=0 THEN BEGIN q^.urm:= inceput; inceput:= q END END; {Faza de ieire} q:= inceput; WHILE q<>nil DO BEGIN Write(q^.cheie); z:= z-1; t:= q^.secund; q:= q^.urm; WHILE t<>nil DO BEGIN p:= t^.id; p^.contor:= p^.contor-1; IF p^.contor=0 THEN BEGIN {inserare p^ n lista principalilor} p^.urm:= q; q:= p END; t:= t^.urm END END; IF z<>0 THEN WriteLn('Multimea nu este partial ordonata!'); END. ------------------------------------------------------------

6.5. Structuri de date derivate din structura list n cadrul acestui subcapitol vor fi prezentate cteva dintre structurile de date care
deriv din structura de date list, fiind considerate liste speciale.

Este vorba despre: listele circulare, listele dublu nlnuite, stivele i cozile. De asemenea se prezint funcia de asociere a memoriei precum i tipurile abstracte
de date care pot fi utilizate pentru implementarea ei.

n general se pstreaz regula ca pentru fiecare tip abstract de date s se prezinte


cteva posibiliti de implementare

6.5.1. Liste circulare

Listele circulare sunt liste nlnuite ale cror nlnuiri se nchid. n aceste condiii se pierde noiunea de nceput i sfrit, lista fiind referit de un
pointer care se deplaseaz de-a lungul ei (fig.6.5.1.a).

ListaCircular

Fig.6.5.1.a. List circular

Listele circulare ridic unele probleme referitoare la inseria primului nod n list i
la suprimarea ultimului nod.

O modalitate simpl de rezolvare a acestor situaii este aceea de a utiliza un nod fictiv
ntr-o manier asemntoare celei prezentate la listele obinuite (tehnica nodului fictiv &6.3.2). Aceast modalitate este ilustrat n figura 6.5.1.b.

fictiv

fictiv


Lista (a)


Lista 1

(b)

Fig.6.5.1.b. List circular implementat prin tehnica nodului fictiv. List vid (a), list normal (b) 6.5.2. Liste dublu nlnuite

Unele aplicaii necesit traversarea listelor n ambele sensuri. Cu alte cuvinte fiind dat un element oarecare al listei trebuie determinat cu rapiditate
att succesorul ct i predecesorul acestuia.

Maniera cea mai rapid de a realiza acest lucru este aceea de a memora n fiecare nod

al listei referinele "nainte" i "napoi", lucru care conduce la structura list dublu nlnuit. (fig.6.5.2.a). Preul care se pltete este prezena unui cmp suplimentar de tip pointer n fiecare nod i o oarecare cretere a complexitii unor proceduri care implementeaz operatorii de baz care prelucreaz astfel de liste.

Fig.6.5.2.a. List dublu nlnuit

Dac implementarea acestor liste se realizeaz cu pointeri se pot defini tipurile de


date din secvena [6.5.2.a]. -----------------------------------------------------------{Liste dublu nlnuite - structuri de date}

TYPE TipPointerNod = ^TipNod; TipNod = RECORD element: TipElement; [6.5.2.a] anterior,urmator: TipPointerNod END; TipPozitie: TipPointerNod; TipListaDubluInlantuita: TipPointerNod; ------------------------------------------------------------

Pentru exemplificare se prezint procedura de suprimare a elementului situat n


poziia p a unei liste dublu nlnuite.

n secvena [6.5.2.b] se prezint maniera n care se realizeaz aceast aciune, n

accepiunea faptului c nodul suprimat nu este nici primul nici ultimul nod al listei. Pentru nceput se localizeaz nodul precedent i se face cmpul urmator al acestuia s indice nodul care urmeaz celui indicat de p. n continuare se modific cmpul anterior al nodului care urmeaz celui indicat de p astfel nct el s indice nodul precedent celui indicat de p. Nodul suprimat este indicat n continuare de p, spaiul de memorie afectat lui putnd fi reutilizat n regim de alocare dinamic a memoriei. -----------------------------------------------------------{Liste dublu nlnuite - suprimarea unui nod} PROCEDURE Suprima(VAR p: TipPozitie); BEGIN [6.5.2.b] IF p^.anterior<>NIL THEN {nu este primul nod} p^.anterior^.urmator:= p^.urmator; IF p^.urmator<>NIL THEN {nu este ultimul nod} p^.urmator^.anterior:= p^.anterior; END;{Suprima} ------------------------------------------------------------

n practica programrii, se pot utiliza diferite tehnici de implementare a listelor dublu

nlnuite, derivate din tehnicile de implementare a listelor liniare. Aceste tehnici simplific implementarea operatorilor care prelucreaz astfel de liste n mod deosebit n situaii limit (list vid sau list cu un singur nod).

Astfel o prim posibilitate o reprezint lista dublu nlnuit cu dou noduri fictive
(fig.6.5.2.b). Cele dou noduri fictive (Fict1 i Fict2) permit ca inseria primului nod al listei respectiv suprimarea ultimului nod s se realizeze n manier similar oricrui alt nod al listei.

Fict1

Fict2

Fig.6.5.2.b. List dublu nlnuit. Varianta cu dou noduri fictive

O alt variant de implementare se bazeaz pe utilizarea: (1) De indicatori pentru cele dou capete ale listei (2) Unui contor de noduri pentru sesizarea situaiilor limit (fig.6.5.2.c).


nceput NrNoduri


Sf rit

Fig.6.5.2.c. List dublu nlnuit. Varianta cu indicatori la capete

Listele dublu nlnuite pot fi implementate i ca liste circulare. n figurile 6.5.2.d respectiv 6.5.2.e apare o astfel de list n dou reprezentri grafice
echivalente.

Fig.6.5.2.d. List dublu nlnuit circular

Fig.6.5.2.e. List dublu nlnuit circular

Este de asemenea posibil de a se utiliza la implementarea listelor dublu nlnuite


circulare tehnica nodului fictiv, adic un nod care practic "nchide cercul". Astfel, cmpul anterior al acestui nod indic ultimul nod al listei, iar cmpul su urmator pe primul (fig.6.5.2.f). Cnd lista este vid, ambele nlnuiri indic chiar nodul fictiv.

Fictiv

Fig.6.5.2.f. List dublu nlnuit circular. Varianta cu nod fictiv

6.5.3. Stive

O stiv este un tip special de list n care toate inserrile i suprimrile se execut la
un singur capt care se numete vrful stivei.

Stivele se mai numesc structuri list de tip LIFO (last-in-first-out) adic "ultimulintrodus-primul-suprimat" sau liste de tip "pushdown".

Modelul intuitiv al unei stive este acela al unui vraf de cri sau al unui vraf de

farfurii pe o mas, n care maniera cea mai convenabil de a lua un obiect sau de a aduga un altul este, din motive uor de neles, aceea de a aciona n vrful vrafului.

6.5.3.1. TDA Stiv

n maniera consecvent de prezentare a tipurilor de date abstracte adoptat n acest


manual, definirea TDA Stiv presupune precizarea: (1) Modelului matematic asociat (2) Notaiilor utilizate (3) Operatorilor definii pentru acest tip.

Toate aceste elemente apar n [6.5.3.1.a].


-----------------------------------------------------------TDA Stiv Modelul matematic: o secven finit de noduri. Toate nodurile aparin unui acelai tip numit tip de baz. O stiv este de fapt o list special n care toate inseriile i toate suprimrile se fac la un singur capt care se numete vrful stivei. Notaii: s: TipStiva; x: TipElement.

[6.5.3.1.a]

Operatori: 1.Initializeaza(s:TipStiva){:TipPozitie}; - face stiva s vid. 2.VarfSt(s:TipStiva):TipElement; - furnizeaz elementul din vrful stivei s. 3.Pop(s:TipStiva); - suprim elementul din vrful stivei. 4.Push(x:TipElement,s:TipStiva); - insereaz elementul x n vrful stivei s. Vechiul vrf devine elementul urmtor .a.m.d. 5.Stivid(s:TipStiva):boolean; - returneaz valoarea adevrat dac stiva s este vid i fals n caz contrar. ------------------------------------------------------------

n secvena [6.5.3.1.b] apare un exemplu de implementare a TDA Stiv utiliznd


TDA List varianta restrns.

tip de date abstract care ilustreaz: Pe de o parte flexibilitatea i simplitatea unei astfel de abordri Pe de alt parte, invariana ei n raport cu nivelurile ierarhiei. Cu alte cuvinte, modificarea implementrii TDA List nu afecteaz sub nici o form implementarea TDA Stiv n accepiunea pstrrii nemodificate a prototipurilor operatorilor definii. -----------------------------------------------------------{Implementarea TDA Stiv bazat pe TDA Lista (varianta restrns)} TYPE TipStiva = TipLista; VAR s: TipStiva; p: TipPozitie; x: TipNod; b: boolean;

Acesta este n acelai timp i un exemplu de implementare ierarhic a unui

[6.5.3.1.b]

{Initializeaza(s:TipStiva);} p:= initializeaza(s); {VarfSt(s);} x:= Furnizeaza(Primul(s),s); {Pop(s);} Suprima(Primul(s),s); {Push(x,s);} Insereaza(s,x,Primul(s)); {Stivid(s);} b:= Fin(s)=0; ----------------------------------------------------------- Utilizarea deosebit de frecvent i cu mare eficien a structurii de date stiv n domeniul programrii, a determinat evoluia acesteia de la statutul de structur de date avansat spre cel de structur fundamental.

Aceast tendin s-a concretizat n implementarea hardware a acestei structuri n

toate sistemele de calcul moderne i n includerea operatorilor specifici tipului de date abstract stiv n setul de instrucii cablate al procesoarelor actuale.

6.5.3.2. Implementarea TDA Stiv cu ajutorul structurii tablou

ntruct stiva este o list cu caracter mai special, toate implementrile listelor descrise
pn n prezent sunt valabile i pentru stive.

n particular, reprezentarea stivelor ca liste nlnuite nu ridic nici un fel de

probleme, operatorii PUSH i POP opernd doar cu pointerul de nceput i cu primul nod al listei.

n ceea ce privete implementarea TDA Stiv cu ajutorul tablourilor, Implementarea listelor bazat pe tipul tablou prezentat n (&6.3.1) nu este cea mai
propice

Explicaia: fiecare PUSH i fiecare POP necesit mutarea ntregii stive,


activitate care necesit un consum de timp proporional cu numrul de elemente ale stivei.

O utilizare mai eficient a structurii tablou ine cont de faptul c inserrile i


suprimrile se fac numai n vrf.

Astfel se poate considera drept baz a stivei sfritul tabloului (indexul su

cel mai mare), stiva crescnd n sensul descreterii indexului n tablou. Un cursor numit vrf indic poziia curent a ultimului element al stivei (fig.6.5.3.2).

Structura de date abstract care se definete pentru aceast implementare este


urmtoarea [6.5.3.2.a]. -----------------------------------------------------------{Implementarea stivelor cu ajutorul tablourilor} TYPE TipStiva = RECORD varf: integer; [6.5.3.2.a] elemente: ARRAY[1..lungimeMaxima] OF TipElement END; -----------------------------------------------------------1 2 3 vrf

(zona liber) (ultimul element)

(al doilea element) lungimeMaxima elemente (primul element)

Fig.6.5.3.2. Implementarea TDA Stiv cu ajutorul structurii tablou

O instan a stivei const din secvena elemente[varf], elemente [varf+1],


...,elemente[lungimeMaxima].

Stiva este vid dac varf = lungime Maxima+1. Operatorii specifici acestei implementri sunt prezentai n [6.5.3.2.b].
-----------------------------------------------------------{Structura stiva - implementare bazat pe tablouri operatori specifici} PROCEDURE Initializeaza(VAR s: TipStiva); BEGIN s.varf:= lungimeMaxima+1 END;{Initializeaza} FUNCTION Stivid(s: TipStiva): boolean; BEGIN IF s.varf>lungimeMaxima THEN Stivid=: true ELSE Stivid:= false END;{Stivid}

FUNCTION VarfSt(VAR s: TipStiva): TipElement; BEGIN IF Stivid(s) THEN BEGIN er:= true; mesaj('stiva este vida') END ELSE [6.5.3.2.b] VarfSt:= s.elemente[s.varf] END;{VarfSt} PROCEDURE Pop(VAR s: TipStiva, VAR x: TipElement); BEGIN IF Stivid(s) THEN BEGIN er:= true; mesaj('stiva este vida') END ELSE BEGIN x:= s.elemente[s.varf]; s.varf:= s.varf+1 END END;{Pop} PROCEDURE Push(x: TipElement; VAR s: TipStiva); BEGIN IF s.varf=1 THEN BEGIN er:= true; mesaj('stiva este plina') END ELSE BEGIN s.varf:= s.varf-1; s.elemente[s.varf]:= x END END;{Push} -----------------------------------------------------------6.5.3.3. Exemple de utilizare a stivelor

Exemplul 6.5.3.3.a. Evaluarea unei expresii n format postfix. Se consider o expresie aritmetic n format postfix. Se cere s se redacteze o funcie care evalueaz expresia n cauz. n aceast situaie se poate utiliza urmtoarea tehnic: (1) Se parcurge expresia de la stnga la dreapta (2) Se execut operaiile pe msur ce se ntlnesc, nlocuind de fiecare dat
grupul operand, operand, operator cu valoarea obinut n urma evalurii.

Se presupune c forma corect postfix a expresiei aritmetice de evaluat este

memorat n lista PostFix, ale crei noduri conin fie un operand fie un operator.

Funcia EvaluarePostfix realizeaz evaluarea expresiei utiliznd stiva Eval n

care memoreaz valori numerice aflate n ateptarea execuiei operaiilor aritmetice necesare [6.5.3.3.a]. -----------------------------------------------------------{Evaluarea unei expresii n format postfix} FUNCTION EvaluarePostfix (PostFix: TipLista): REAL; {Evalueaz o expresie postfix reprezentat ca i o list de noduri. Se presupune c expresia postfix este corect} VAR Eval: TipStiva; NrVirf,NrSecund,Raspuns: REAL; t: NodPostFix; BEGIN Initializeaza(Eval); WHILE *mai exist noduri n PostFix DO [6.5.3.3.a] BEGIN *citete un nod al listei PostFix n t; IF t este un numar THEN Push(t,Eval) ELSE BEGIN {t este un operator} NrVirf:= VarfSt(Eval); Pop(Eval); NrSecund:= VarfSt(Eval); Pop(Eval); Raspuns:= NrSecund <t> NrVirf; {operaia t} Push(Raspuns, Eval) END END EvaluarePostfix:= VarfSt(Eval) END;{EvaluarePostfix} ------------------------------------------------------------

Exemplul 6.5.3.3.b. Eliminarea recursivitii. Dup cum s-a precizat n &5.2.4, recursivitatea poate fi eliminat practic n orice
situaie.

Eliminarea ei poate conduce la creterea performanelor, ns algoritmul devine mult


mai complicat i mai greu de neles.

Scopul acestui paragraf este acela de a prezenta cazul general al eliminrii

recursivitii, adic a manierei n care un algoritm recursiv oarecare poate fi transformat ntr-unul nerecursiv.

n acest scop se poate utiliza o structur de date stiv. Pentru exemplificare se va prezenta o soluie recursiv i una nerecursiv a unei
versiuni simplificate a problemei clasice a "sacului cltorului" ("knapsack problem").

Formularea problemei:

prin ntregi pozitivi, a1, a2, ..., an Se cere s se afle dac greutile se pot selecta astfel nct suma lor s fie exact GT. Spre exemplu dac GT = 10 i greutile sunt 7, 5, 4, 4, 1 atunci pot fi selectate a doua, a treia i ultima greutate deoarece 5+4+1=10. Aceasta justific denumirea de "sac al cltorului" dat problemei, ntruct un cltor poate duce n principiu un bagaj de o greutate precizat GT. Varianta complet a acestei probleme care ine cont att de greutatea obiectelor selectate ct i de valoarea lor, a fost prezentat n variant recursiv n &5.4.6).

Fiind dat o valoare GT (greutatea total) i un set de greuti reprezentate

Algoritmul recursiv care rezolv problema n varianta sa simpl apare n secvena


[6.5.3.3.b]. -----------------------------------------------------------{Problema Knapsack - Problema sacului caltorului formularea recursiv} FUNCTION Knapsack(GT:integer; Candidat:integer):boolean; BEGIN IF GT=0 THEN Knapsack:=true [6.5.3.3.b] ELSE IF (GT<0) OR (candidat>n) THEN Knapsack:=false ELSE {soluia cu i fr candidat} [*] IF Knapsack(GT-G[candidat],candidat+1) THEN BEGIN Write(G[candidat]); Knapsack:=true END ELSE {singura soluie posibil este fr Candidat [**] Knapsack:= Knapsack(GT,Candidat+1); Writeln END;{Knapsack} ------------------------------------------------------------

Funcia recursiv Knapsack opereaz asupra unui tablou de forma:


G:ARRAY[1..n] OF integer; care memoreaz greutile obiectelor.

Un apel al funciei Knapsack(s,i) stabilete dac exist un set de elemente


cuprinse n domeniul G[i], G[n] a cror sum este exact s i n caz afirmativ le tiprete.

Modul preconizat de lucru este urmtorul. n primul rnd funcia verific dac poate furniza rspunsul imediat. Acest lucru este posibil dac s = 0 (caz n care soluia problemei o reprezint setul vid de greuti) Sau dac s > 0 i i > n (caz n care au fost parcurse toate greutile
fr a se gsi o sum egal cu s).

Dac nici una din aceste situaii nu este valabil, atunci se apeleaz

Knapsack(s-G[i],i+1) pentru a verifica dac exist o soluie care l include pe G[i]. Dac exist o astfel de soluie, atunci se afieaz G[i]. Dac nu exist o astfel de soluie, se apeleaz Knapsack(s,i+1), pentru a verifica dac exist o soluie care nu-l include pe G[i].

Metoda cea mai general de transformare a unui algoritm recursiv ntr-unul iterativ

este cea bazat pe introducerea unei stive definite i gestionate de ctre utilizator. Un nod al acestei stive va conine urmtoarele elemente: (1) Valorile curente ale parametrilor de apel ai procedurii; (2) Valorile curente ale tuturor variabilelor locale ale procedurii; (3) O indicaie referitoare la adresa de retur, adic referitoare la locul n care revine controlul execuiei n momentul n care apelul curent al instanei procedurii se termin.

n cazul procedurii Knapsack, acest lucru se realizeaz dup cum urmeaz. n primul rnd se observ c ori de cte ori se realizeaz un apel al procedurii
(materializat prin introducerea unui nod nou n stiva utilizator), numrul de candidaii crete cu 1. n aceste condiii, variabila Candidat poate fi definit ca i o variabil global care este incrementat cu 1 la fiecare introducere n stiv i decrementat cu 1 la fiecare extragere.

O a doua simplificare, se poate face n legtur cu modificarea adresei de retur

pstrat n stiv. n mod concret, adresa de retur pentru aceast funcie este: Fie situat ntr-o alt procedur care a apelat iniial funcia Knapsack, Fie este adresa urmtoare unuia dintre cele dou apeluri recursive din interiorul funciei, nsemnate cu [*] respectiv [**] n secvena [6.5.3.3.b]. Cele trei situaii pot fi reprezentate printr-o variabil de tip Stare care poate avea una din urmtoarele valori: extern - indicnd un apel din afara funciei Knapsack; inclus - indicnd un apel recursiv din locul precizat de [*], apel care l include pe G[Candidat] n soluie; exclus - indicnd un apel recursiv din locul precizat de [**], care l exclude pe G[Candidat]. de revenire, atunci GT poate fi tratat ca i o variabil global. G[Candidat] din GT, la GT.

Dac se memoreaz valoarea variabilei de tip Stare ca i un indiciu pentru adresa Cnd variabila de tip Stare se modific din extern n inclus se scade Cnd aceasta se modific din inclus n exclus se adaug G[Candidat]

Pentru a reprezenta efectul revenirii din procedur n sensul precizrii dac s-a gsit
sau nu o soluie, se utilizeaz variabila global Victorie.

O dat pus pe adevrat, Victorie rmne adevrat i determin golirea


stivei i tiprirea greutilor aflate n starea inclus.

Stiva ca atare va fi declarat ca o list de stri [6.5.3.3.c]:


-----------------------------------------------------------{Eliminarea recursivitii - Problema sacului caltorului definirea stivei} TYPE Stare =(extern,inclus,exclus); [6.5.3.3.c] TipStiva ={o declaraie potrivit de stiv avnd noduri de tip Stare} ------------------------------------------------------------

Avnd n vedere cele mai sus precizate, n secvena [6.5.3.3.d] apare procedura
nerecursiv Knapsack care: Opereaz asupra unui tablou de greuti G, Este conceput n termenii operatorilor definii asupra unei structuri de date abstracte stiv. este strict specific, este n mod evident mai lung, mai complicat i este mai dificil de neles.

Dei aceast procedur poate fi mai rapid dect funcia Knapsack recursiv, ea

Din aceste motive eliminarea recursivitii se recomand a fi utilizat atunci cnd


factorul vitez este deosebit de important. -----------------------------------------------------------PROCEDURE KnapsackNerecursiv (GT: integer); VAR Candidat: integer; Victorie: boolean; s: TipStiva; BEGIN Candidat:= 1; Victorie:= false; Initializare(s); Push(extern,s); {iniializare stiv cu G[1]} REPEAT IF Victorie THEN BEGIN {golire stiv i afiarea greutilor incluse n soluie} IF VarfSt(s)=inclus THEN Writeln(G[Candidat]); Candidat:= Candidat-1; Pop(s) END ELSE IF GT=0 THEN BEGIN {soluie gsit} Victorie:= true; Candidat:= Candidat-1; Pop(s) END ELSE IF(((GT<0 AND (VarfSt(s)=extern)) OR

(Candidat>n)) THEN BEGIN {nici o soluie nu este posibil cu aceasta alegere} Candidat:= Candidat-1; Pop(s) END ELSE {nu exist nca o soluie; se consider starea candidatului curent} IF VarfSt(s)=extern THEN BEGIN {prima ncercare de includere a unui candidat} GT:= GT-G[Candidat]; Candidat:= Candidat+1; Pop(s); Push(inclus,s); Push(extern,s) END ELSE IF VarfSt(s)=inclus THEN BEGIN {ncercare excludere candidat} GT:= GT + G[Candidat]; Candidat:= Candidat+1; Pop(s); Push(exclus,s); Push(extern,s) END ELSE BEGIN {VarfSt(s)=exclus; Abandonare selecie curent} Pop(s); Candidat:= Candidat-1 END UNTIL Stivid(s) END;{KnapsackNecursiv} -----------------------------------------------------------6.5.4. Cozi

Cozile sunt o alt categorie special de liste n care elementele sunt inserate la un
capt (spate) i sunt suprimate la cellalt (cap). venit-primul-servit".

Cozile se mai numesc liste "FIFO" ("first-in first-out") adic liste de tip "primul Operaiile care se execut asupra cozii sunt analoage celor care se realizeaz asupra

stivei cu diferena c inserrile se fac la spatele cozii i nu la nceputul ei i c ele difer ca i terminologie.

6.5.4.1. Tipul de date abstract Coad

n acord cu abordrile anterioare n secvena [6.5.4.1.a] se definesc dou variante ale


TDA Coad

n secvena [6.5.4.1.b] se prezint un exemplu de implementare a TDA Coad bazat

pe TDA List. -----------------------------------------------------------TDA Coad Modelul matematic: o secven finit de noduri. Toate nodurile aparin unui aceluiai tip numit tip de baz. O coad este de fapt o list special n care toate inseriile se fac la un capt (spate) i toate suprimrile la cellalt capt (cap). Notaii: C: TipCoada; x: TipElement; b: boolean; [6.5.4.1.a]

Operatori (setul 1): 1.Initializeaza(C: TipCoada){: TipPozitie}; - face coada C vid. 2.Cap(C: TipCoada): TipEelement; - funcie care returneaz primul element al cozii C. 3.Adauga(x: TipElement, C: TipCoada); - insereaz elementul x la spatele cozii C. 4.Scoate(C: TipCoada); - suprim primul element al lui C. 5.Vid(C: TipCoada): boolean; - este adevrat dac i numai dac C este o coad vid. Operatori (setul 2): 1.CreazaCoadaVida(C: TipCoada); - face coada C vid. 2.CoadaVida(C: TipCoada): boolean; - este adevrat dac i numai dac C este o coad vid. 3.CoadaPlina(C: TipCoada): boolean; - este adevrat dac i numai dac C este o coad plin (operator dependent de implementare). 2.InCoada(C: TipCoada x: TipElement); - insereaz elementul x la spatele cozii C (EnQueue). 3.DinCoada(C: TipCoada, x: TipElement); - suprim primul element al lui C (DeQueue). -----------------------------------------------------------{Exemplu de implementare a TDA Coad cu ajutorul TDA List (setul 1 de operatori)} TYPE TipCoada = TipLista; VAR C: TipCoada; p: TipPozitie; x: TipElement; b: boolean; {Initializeaza(C);} {Cap(C);} {Adauga(x,C);} {Scoate(C);}

[6.5.4.1.b] p:= Initializeaza(C); x:= Furnizeaza(Primul(C),C); Insereaza(C,x,Fin(C)); Suprima(Primul(C),C);

{Vid(C),C);} b:= Fin(C)=0; -----------------------------------------------------------6.5.4.2. Implementarea cozilor cu ajutorul pointerilor

Ca i n cazul stivelor, orice implementare a listelor este valabil i pentru cozi. Pornind ns de la observaia c inserrile se fac numai la spatele cozii, procedura
Adauga poate fi conceput mai eficient. Astfel, n loc de a parcurge de fiecare dat coada de la nceput la sfrit atunci cnd se dorete adugarea unui element, se va pstra un pointer la ultimul element al cozii. element al listei utilizat n execuia comenzilor Cap i Scoate.

De asemenea, ca i la toate tipurile de liste, se va pstra i pointerul la primul n implementare se poate utiliza un nod fictiv ca i prim nod al cozii (tehnica nodului
fictiv), caz n care pointerul de nceput va indica acest nod. Aceast convenie care permite o manipulare mai convenabil a cozilor vide, va fi utilizat n continuare n implementarea bazat pe pointeri a structurii de date coad.

Tipurile de date care se utilizeaz n acest scop sunt urmtoarele [6.5.4.2.a]:


-----------------------------------------------------------{Implementarea cozilor cu ajutorul pointerilor - definirea structurilor de date} TYPE TipReferintaNod=^TipNod; TipNod = RECORD element: TipElement; [6.5.4.2.a] urm: TipReferintaNod END; ------------------------------------------------------------

n aceste condiii se poate defini o structur de tip coad care const din doi pointeri
indicnd nceputul respectiv spatele unei liste nlnuite. Primul nod al cozii este unul fictiv n care cmpul element este ignorat. Aceast convenie, dup cum s-a menionat mai nainte permite o reprezentare i o manipulare mai simpl a cozii vide.

Astfel TipCoada se poate defini dup cum se prezint n [6.5.4.2.b].


-----------------------------------------------------------{Implementarea cozilor cu ajutorul pointerilor - definirea lui TipCoada} TipCoada = RECORD inceput,spate: TipReferintaNod [6.5.4.2.b] END; ------------------------------------------------------------

n continuare n [6.5.4.2.c] se prezint secvenele de program care implementeaz


operatorii (setul 1) definii asupra cozilor.

-----------------------------------------------------------{Implementarea cozilor cu ajutorul pointerilor implementarea operatorilor specifici (setul 1)} PROCEDURE Initializeaza(VAR C: TipCoada); BEGIN NEW(C.inceput); {creaz nodul fictiv} C.inceput^.urm:= nil; C.spate:= C.inceput; {nodul fictiv este primul i ultimul nod al cozii} END;{Initializeaza} FUNCTION Vid(C: TipCoada): boolean; BEGIN IF C.inceput:=C.spate THEN Vid:=true ELSE Vid:=false END;{Vid} FUNCTION Cap(C: TipCoada): TipElement; BEGIN [6.5.4.2.c] IF Vid(C) THEN BEGIN er:= true; MESAJ('coada este vid') END ELSE Cap:= C.inceput^.urm^.element END;{Cap} PROCEDURE Adauga(x: TipElement; VAR C: TipCoada); BEGIN NEW(C.spate^.urm); {se adaug o nou celul la spatele cozii] C.spate:= C.spate^.urm; C.spate^.element:= x; C.spate^.urm:= nil END;{Adauga}

PROCEDURE Scoate(VAR C: TipCoada); BEGIN IF Vid(C) THEN BEGIN er:= true; WRITE('coada este vid') END ELSE C.inceput:= C.inceput^.urm END;{Scoate} ------------------------------------------------------------

6.5.4.3. Implementarea cozilor cu ajutorul tablourilor circulare

Reprezentarea listelor cu ajutorul tablourilor, prezentat n &6.3.1, poate fi utilizat i


pentru cozi, dar nu este foarte eficient. ntr-adevr, pointerul la ultimul element al listei, permite implementarea simpl, ntr-un numr fix de pai, a operaiei Adauga.

n tablou, operaie care necesit un timp O(n), dac coada are lungimea n. Pentru a depi acest dezavantaj se poate utiliza o structur de tablou circular n care prima poziie urmeaz ultimei, dup cum rezult din figura 6.5.4.3.
LungimeMaxima 1 2 . . .

Execuia operaiei Scoate presupune ns mutarea ntregii cozi cu o poziie

. . . ... c.spate

coada

c.fatza

Fig.6.5.4.3. Implementarea circular a unei cozi

Coada se gsete undeva n jurul cercului, n poziii consecutive, avnd spatele

undeva naintea feei la parcurgerea cercului n sensul acelor de ceasornic. nlnuirea unui element presupune micarea pointerului C.spate cu o poziie n sensul acelor de ceasornic i introducerea elementului n aceast poziie Scoaterea unui element din coad, presupune simpla mutare a pointerului C.fatza cu o poziie n sensul rotirii acelor de ceasornic. Astfel coada se rotete n sensul rotirii acelor de ceasornic dup cum se adaug sau se scot elemente din ea.

n aceste condiii att procedura Adauga ct i Scoate pot fi redactate astfel nct
s presupun un numr fix de pai de execuie.

n cazul implementrii ce apare n continuare, pointerul C.fatza indic primul


element al cozii, iar pointerul C.spate ultimul element al cozii. i a celei pline.

Acest mod de implementare ridic ns o problem referitoare la sesizarea cozii vide Presupunnd c coada reprezentat n figura 6.5.4.3 este plin (conine

LungimeMaxima elemente), pointerul C.spate va indica poziia adiacent lui C.fatza n sens trigonometric pozitiv. singur element. n acest caz C.fatza i C.spate indic aceeai poziie. Dac se scoate singurul element, C.fatza avanseaz cu o poziie n fa (n sensul acelor de ceasornic) indicnd coada vid.

Pentru a preciza reprezentarea cozii vide se presupune o coad format dintr-un

Se observ ns c aceast situaie este identic cea anterioar care indic coada
plin, adic C.spate se afl cu o poziie n faa lui C.fatza, la parcurgerea n sens trigonometric pozitiv a cozii.

n vederea rezolvrii acestei situaii, n tablou se vor introduce doar


LungimeMaxima-1 elemente, dei n tablou exist LungimeMaxima poziii. Astfel testul de coad plin conduce la o valoare adevrat dac C.spate devine egal cu C.fatza dup dou avansri succesive, Testul de coad vid conduce la o valoare adevrat dac C.spate devine egal cu C.fatza dup avansul cu o poziie. Evident avansrile se realizeaz n sensul trigonometric pozitiv al parcurgerii cozii.

n continuare se prezint implementarea operatorilor definii peste o structur de date


de tip coad definit dup cum urmeaz [6.5.4.3.a]: -----------------------------------------------------------{Implementarea cozilor cu ajutorul tablourilor circulare definirea structurilor de date}

TYPE TipCoada = RECORD [6.5.4.3.a] elemente: ARRAY[1..LungimeMaxima] OF TipElement; fatza,spate: TipIndice END; ------------------------------------------------------------

Secvena de instrucii corespunztoare apare n [6.5.4.3.b]. Funcia Avanseaza(C) furnizeaz poziia urmtoare celei curente n coad.

-----------------------------------------------------------{Implementarea cozilor cu ajutorul tablourilor circulare implementarea operatorilor specifici} FUNCTION Avanseaza(VAR i: TipIndice): TipIndice; BEGIN Avanseaza:= (i mod LungimeMaxima)+1 END;{Avanseaza} PROCEDURE Initializeaza(VAR C: TipCoada); BEGIN C.fatza:= 1; [6.5.4.3.b] C.spate:= LungimeMaxima END;{Initializeaza} FUNCTION Vid(C: TipCoada): boolean; BEGIN IF Avanseaza(C.spate)=C.fatza THEN Vid:= true ELSE Vid:= false END;{Vid} FUNCTION Cap(VAR C: TipCoada): TipElement; BEGIN

IF Vid(C) THEN BEGIN er:= true; MESAJ('coada e vid') END ELSE Cap:= C.elemente[C.fatza] END;{Cap} PROCEDURE Adauga(x: TipElement; VAR C: TipCoada); BEGIN IF Avanseaza(Avanseaza(C.spate))=C.fatza THEN BEGIN er:= true; MESAJ('coada este plin') END ELSE BEGIN C.spate:= Avanseaza(C.spate); C.elemente[c.spate]:= x END END;{Adauga} PROCEDURE Scoate(VAR C: TipCoada); BEGIN IF Vid(C) THEN BEGIN er:= true; MESAJ ('coada este vid') END ELSE C.fatza:= Avanseaza(C.fatza) END;{Scoate} ------------------------------------------------------------

Problemele legate de sesizarea cozii vide i a celei pline pot fi rezolvate mai simplu

prin introducerea unui contor al numrului de elemente din coad. Astfel valoarea 0 a acestui contor semnific coad vid iar valoarea DimMax coad plin.

Structura de date corespunztoare acestei abordri apare n secvena [6.5.4.3.c]. Procedurile care implementeaz operatorii specifici n aceste circumstane pot fi
dezvoltai cu uurin n baza exemplelor prezentate anterior. -----------------------------------------------------------{Implementarea cozilor cu ajutorul tablourilor circulare definirea structurilor de date (varianta 2)} CONST DimMax = ...; DimCoada = DimMax+1; TYPE TipElement = ...; TipIndex = 0..DimMax; TipContor = 0..DimCoada; TipTablou = ARRAY[TipIndex] OF TipElement; TipCoada = RECORD fatza,spate: TipIndex;

contor: TipContor; elemente: TipTablou END; VAR C: TipCoada; x: TipElement; -----------------------------------------------------------6.5.4.4. Aplicaie. Conversia infix-postfix

Aplicaia de fa realizeaz conversia unei expresii infix n reprezentare postfix


utiliznd o implementare nerecursiv.

Ambele expresii sunt reprezentate prin intermediul unor liste nlnuite. Se vor utiliza dou structuri de date auxiliare: (1) O stiv StivaOp (stiva operatorilor) care memoreaz operatorii pentru
care nu s-au determinat nc ambii operanzi (2) O coad CoadaPost care nregistreaz forma postfix a expresiei care se construiete.

Fiecrui operator din expresia infix i se asociaz o valoare de preceden: Operatorii * i / au cea mai mare preceden, Operatorii + i precedena urmtoare. n vederea simplificrii proiectrii algoritmului, n mod artificial se acord
parantezei stnga ( prioritatea cea mai sczut.

Codul efectiv al procedurii apare n secvena [6.5.4.4.a]. Dup cum se observ transferurile se realizeaz din StivaOp n

CoadaPost, care n final va putea fi utilizat ca i intrare a procedurii de evaluare. -----------------------------------------------------------{Aplicaie: Conversie din format Infix n format Postfix} PROCEDURE ConversieInPostFix(Infix: TipLista, VAR CoadaPost: TipCoada); VAR StivaOp: TipStiva; PROCEDURE Transfer(VAR S: TipStiva, VAR Q: TipCoada); {Transfer vrful stivei S la sfritul cozii Q. Se presupune c atomii din S i Q aparin aceluiai tip} BEGIN Adauga(VarfSt(S),Q); Pop(S) [6.5.4.4.a] END;{Transfer} BEGIN Initializeaza(StivaOp); Initializeaza(CoadaPost); WHILE *mai exist noduri n lista Infix DO BEGIN *ia un nod t, din lista Infix; [1] IF *t este un numr THEN

Adauga(t, CoadaPost) ELSE [2] IF Stivid(StivaOp) THEN Push(t, StivaOp) ELSE [3] IF *t este parantez stnga THEN Push(t, StivaOp) ELSE [4] IF *t este parantez dreapt THEN BEGIN WHILE *VarfSt(StivaOp) este diferit de parantez stnga DO Transfer (StivaOp,CoadPost); Pop(StivaOp){descarc proxima parantez stnga din stiv} END ELSE [5] BEGIN WHILE *precedena lui t precedena lui VarfSt(StivaOp) DO Transfer(StivaOp, CoadaPost) Push(t, StivaOp) END END;{WHILE} [6] WHILE NOT Stivid(StivaOp) DO Transfer(StivaOp,CoadaPost) {transfer operatorii rmai} END;{ConversieInPostFix} ------------------------------------------------------------

Procedura ConversieInPostfix, ilustreaz o mic parte a activitii pe care


compilatorul sau interpretorul o realizeaz n momentul translatrii expresiilor aritmetice n cod executabil. natura efectiv a operanzilor (constante, variabile etc.).

De fapt sunt eludate fazele de analiz sintactic i semantic ale expresiei precum i

6.5.5. TDA Coad bazat pe prioritate

Coada bazat pe prioritate (priority queue) este structura de date abstract care
permite inseria unui element i suprimarea celui mai prioritar element.

O astfel de structur difer att fa de structura coad (din care se suprim primul

venit, deci cel mai vechi) ct i fa de stiv (din care se suprim ultimul venit, deci cel mai nou). cozile i stivele, Cozile i stivele pot fi implementate prin cozi bazate pe prioritate utiliznd prioriti corespunztoare.

De fapt cozile bazate pe prioritate pot fi concepute ca i structuri care generalizeaz

Aplicaiile acestor cozi sunt: sistemele de simulare (unde cheile pot corespunde unor
evenimente "de timp" ce trebuie s decurg n ordine), planificarea job-urilor, traversri speciale ale unor structuri de date, etc.

Considernd coada bazat pe prioritate drept o structur abstract de date ale crei
elemente sunt articole cu chei (sau prioriti), definirea acesteia apare n secvena [6.5.5.a]. -----------------------------------------------------------TDA Coad bazat pe prioritate Modelul matematic: o secven finit de noduri. Toate nodurile aparin unui aceluiai tip numit tip de baz. O coad bazat pe prioritate este de fapt o list special n care se permite inseria normal i suprimarea doar a celui mai prioritar element. Notaii: q: TipCoadaBazataPePrioritate; x: TipElement; Operatori: 1.Initializeaz(q: TipCoadaBazataPePrioritate); face coada q vid. 2.Insereaz(x: TipElement, q: CoadaBazataPePrioritate); - inseria unui nou element x n coada q. 3.Extrage(q: CoadaBazataPePrioritate): TipElement; - extrage cel mai prioritar element al cozii q. 4.Inlocuiete(q: CoadaBazataPePrioritate, x: TipElement): TipElement; - nlocuiete cel mai prioritar element al cozii q cu elementul x, mai puin n situaia n care noul element este cel mai prioritar element. Returneaz cel mai prioritar element. 5.Schimb(q: TipCoadaBazataPePrioritate), x: TipElement; p: TipPrioritate); - schimb prioritatea elementului x al cozii q i i confer valoarea p. 6.Suprim(q: TipCoadaBazataPePrioritate, x: TipElement); - suprim elementul x din coada q. 7.Vid(q: TipCoadaBazataPePrioritate): boolean; operator care returneaz true dac i numai dac q este o coad vid. ------------------------------------------------------------

[6.5.5.a]

Operatorul Inlocuiete const dintr-o inserie urmat de suprimarea celui mai

prioritar element. Este o operaie diferit de succesiunea suprimare-inserie deoarece necesit creterea pentru moment a dimensiunii cozii cu un element. Acest operator se definete separat deoarece n anumite implementri poate fi conceput foarte eficient.

n mod analog operatorul Schimb poate fi implementat ca i o suprimare, urmat


de o inserie, iar generarea unei cozi ca i o succesiune de inserii.

Cozile bazate pe prioritate pot fi n general implementate n diferite moduri unele


bazate pe structuri simple altele pe structuri avansate, fiecare presupunnd ns performane diferite pentru operatorii specifici.

n continuare vor fi prezentate unele dintre aceste posibiliti.


6.5.5.1. Implementarea cozilor bazate pe prioritate cu ajutorul tablourilor

Implementarea unei cozi bazate pe prioritate se poate realiza prin memorarea


elementelor cozii ntr-un tablou neordonat.

n secvena [6.5.5.1.a] apare structura de date corespunztoare acestei abordri iar n


secvena [6.5.5.1.b] implementarea operatorilor Insereaza i Extrage. -----------------------------------------------------------{Implementarea cozilor bazate pe prioritate cu tablouri structuri de date} TYPE TipElement = RECORD prioritate: TipPrioritate; info: TipInfo [6.5.5.1.a] END; TipCoadaBazataPePrioritate = RECORD elemente: ARRAY[1..DimMax] OF TipElement; nrElemente: 0..DimMax END; -----------------------------------------------------------{Implementarea cozilor bazate pe prioritate cu tablouri operatorii Insereaza i Extage} PROCEDURE Insereaz(x: TipElement, q: TipCoadaBazataPePrioritate); BEGIN {O(1)} q.nrElemente:= q.nrElemente+1; q.elemente[nrElemente]:= x END;{Insereaza} FUNCTION Extrage(q: TipCoadaBazataPePrioritate): TipElement; VAR j,max: 1..DimMax; BEGIN {O(n)} [6.5.5.1.b] max:= 1; FOR j:=1 TO q.nrElemente DO IF q.elemente[j].prioritate > q.elemente[max].prioritate THEN Extrage:= q.elemente[max]; q.elemente[max]:= q.elemente[nrElemente]; q.nrElemente:= q.nrElemente-1

max:= j;

END;{Extrage} ------------------------------------------------------------

Pentru inserie se incrementeaz nrElemente i se adaug elementul de


inserat tabloului q.elemente, operaie care este constant n timp (O(1)).

Pentru extragere se baleeaz ntreg tabloul gsindu-se cel mai mare element,
apoi se introduce ultimul element pe poziia celui care a fost extras i se decrementeaz nrElemente. Operaia necesit o regie O(n). urmat de o extragere.

Implementarea lui Inlocuieste este similar, ea presupunnd o inserie Redactarea celorlali operatori n acest context nu ridic nici un fel de
probleme.

n implementarea cozilor bazate pe prioritate se pot utiliza i tablouri ordonate. Elementele cozii sunt pstrate n tablou n ordinea cresctoare a prioritilor. In aceste condiii, operatorul Extrage, returneaz ultimul element

q.elemente[nrElemente] i decrementeaz nrElemente, operaie ce dureaz un interval constant de timp. Operatorul Insereaza presupune mutarea spre dreapta a elementelor mai mari ca i elementul de inserat, operaie care dureaz O(n).

Operaiile care se refer la cozi bazate pe prioritate pot fi utilizate n implementarea


unor algoritmi de sortare. Spre exemplu utiliznd n mod repetat operaia Insereaza pentru a crea o coad bazat pe prioriti Iar apoi extrgnd pe rnd elementele pn la golirea cozii se obine secvena sortat a elementelor n ordinea descresctoare a prioritii lor. Dac se utilizeaz o coad bazat pe prioriti reprezentat ca un tablou neordonat se obine sortarea prin selecie; Dac se utilizeaz o coad bazat pe prioriti reprezentat ca un tablou ordonat, se obine sortarea prin inserie. 6.5.5.2. Implementarea cozilor bazate pe prioritate cu ajutorul listelor nlnuite

n implementarea cozilor bazate pe prioritate pot fi utilizate i listele nlnuite n


variant ordonat sau neordonat.

Aceast abordare nu modific principial implementarea operatorilor specifici, dar

face posibil realizarea mai eficient a fazelor de inserie i suprimare a elementelor cozii datorit flexibilitii listelor nlnuite. pentru implementarea cozilor bazate pe prioritate listele nlnuite neordonate.

n continuare se prezint un exemplu de realizare a operaiei Extrage, utiliznd Se utilizeaz urmtoarele structuri de date [6.5.5.2.a].
------------------------------------------------------------

{Implementarea cozilor bazate pe prioritate cu liste nlnuite neordonate - structuri de date} TYPE TipReferintaNod = ^TipNod; TipNod = RECORD element: TipElement; [6.5.5.2.a] urm: TipReferintaNod END; CoadaBazataPePrioritate = TipReferintaNod; ------------------------------------------------------------

n implementare se utilizeaz o variant a tehnicii celor doi pointeri utiliznd un


singur pointer (curent) ntr-o exploatare de tip look ahead.

Primul nod al listei este un nod fictiv, el nu conine nici un element avnd poziionat

numai cmpul urm (tehnica nodului fictiv)[6.5.5.2.b]. -----------------------------------------------------------{Implementarea cozilor bazate pe prioritate cu liste nlnuite neordonate - operatorul Extrage} FUNCTION Extrage(VAR q: CoadaBazataPePrioritate): TipReferintaNod; VAR curent: TipReferintaNod; {nodul din faa celui baleat} mare: TipElement; {cel mai mare element curent} anterior: TipReferintaNod; {nodul anterior celui mai mare} BEGIN IF Vid(q) THEN Eroare(`coada este vid) ELSE BEGIN mare:= q^.urm^.element; {q indic nodul fictiv} anterior:= q; curent:= q^.urm; WHILE curent^.urm <> nil DO BEGIN {compar valoarea lui mare cu valoarea urmtoare elementului curent} IF curent^.urm^.element > mare TEHN BEGIN anterior:= curent; {anterior reine pointerul nodului ce precede nodul cu cheia cea mai mare } mare:= curent^urm^.element END;{IF} curent:= curent^.urm END;{WHILE} Extrage:= anterior^.urm ; {poziioneaz pointerul pe cel mai mare element} curent:= anterior^.urm; anterior^.urm:= anterior^.urm^.urm; END;{ELSE} END;{Extrage} ------------------------------------------------------------

Implementarea celorlalte funcii referitoare la cozile bazate pe prioritate utiliznd liste


neordonate nu ridic nici un fel de probleme.

Aceste implementri simple n multe situaii sunt mai utile dect cele bazate pe
modele sofisticate. Astfel implementarea bazat pe liste neordonate e potrivit n situaiile n care se fac multe inserii i mai puine extrageri, n schimb, implementarea bazat pe liste ordonate este potrivit dac prioritile elementelor care se insereaz au tendina de a fi apropiate ca valoare de prioritatea maxim.

6.5.5.3. Implementarea cozilor bazate pe prioritate cu ajutorul ansamblelor

Un ansamblu este un arbore binar parial ordonat reprezentat printr-un tablou, ale
crui elemente satisfac condiiile ansamblului (&3.2.5). n particular cea mai mare (prioritar) cheie este aezat ntotdeauna n prima poziie a tabloului care materializeaz ansamblul. mai defavorabil caz O (log2 N ) pai.

Algoritmii care opereaz asupra structurilor de date de tip ansamblu necesit n cel Ansamblele pot fi utilizate n implementarea cozilor bazate pe prioritate. Exist algoritmi care prelucreaz ansamblul de sus n jos, alii l prelucreaz de jos n
sus.

Pentru simplificare, se presupune c elementele cozii (ansamblului) : (1) Sunt formate numai din prioritate (2) Sunt memorate ntr-un tablou cu dimensiunea maxim precizat

(DimMax), a crui dimensiune curent este pstrat n variabila nrElemente care face parte din definiia cozii [6.5.5.3.a]. -----------------------------------------------------------{Implementarea cozilor bazate pe prioritate cu ajutorul ansamblelor - structuri de date} TYPE TipElement = RECORD prioritate: TipPrioritate; info: TipInfo [6.5.5.3.a] END; TipIndice = 1..DimMax; TipCoadaBazataPePrioritate = RECORD ansamblu: ARRAY[TipIndice] OF TipElement; nrElemente: 0..DimMax END; ----------------------------------------------------------- Pentru construcia unui ansamblu se utilizeaz de regul operatorul Insereaza . O posibilitate de realizare a inseriei o reprezint extinderea spre dreapta a ansamblului, prin introducerea elementului de inserat pe ultima sa poziie. Aceast operaie poate viola ns regulile ansamblului (dac noul element introdus are prioritatea mai mare ca i printele su), situaie care necesit avansul elementului n ansamblu prin interschimbare cu printele su. Acest proces se repet pn cnd elementul devine mai mic ca i printele su, sau a ajuns pe prima poziie a ansamblului.

Procedura UpHeap prezentat n secvena [6.5.5.3.b] implementeaz aceast metod


respectiv avansul elementului nou introdus de pe ultima poziie a ansamblului, de jos n sus n ansamblu pn la locul potrivit

Aceast metod opus procedurii Deplasare utilizat la sortarea "heap sort"


(&3.2.5).

n aceeai secven apare i procedura care implementeaz inseria propriu-zis.


-----------------------------------------------------------{Implementarea cozilor bazate pe prioritate cu ajutorul ansamblelor - operatorii UpHeap i Insereaza} PROCEDURE UpHeap(q: CoadaBazataPePrioritate, k: TipIndice); VAR v: TipElement; BEGIN v:= q.ansamblu[k]; {elementul nou introdus este pe poziia k} q.ansamblu[0].prioritate:=(cea mai mare prioritate)+1; WHILE q.ansamblu[k DIV 2].prioritate <= v.prioritate DO {look ahead} BEGIN q.ansamblu[k]:= q.ansamblu[k DIV 2]; k:= k DIV 2 END; q.ansamblu[k]:= v [6.5.5.3.b] END;{UpHeap} PROCEDURE Insertie(x: TipElement, q: CoadaBazataPePrioritate); BEGIN q.nrElemente:= q.nrElemente+1; q.elemente[q.nrElemente]:= v; UpHeap(q,q.nrElemente) END;{Insertie} --------------------------------------------------------

Dac n operatorul UpHeap,(k DIV 2) se nlocuiete cu (k-1) se obine


n esen sortarea prin inserie la care gsirea locului n care se insereaz noul element se realizeaz verificnd i deplasnd secvenial elementele cu cte o poziie spre dreapta.

n procedura UpHeap deplasarea se face nu liniar ci din nivel n nivel de-a


lungul ansamblului.

La fel ca la sortarea prin inserie, interschimbarea nu este total, v fiind


implicat mereu n aceast operaie.

q.elemente[0] se utilizeaz pe post de fanion care se asigneaz iniial cu


o prioritate mai mare dect a oricrui alt element.

Operatorul Inlocuieste presupune nlocuirea celui mai prioritar element (cel

situat n rdcina ansamblului) cu un nou element care se deplaseaz de sus n jos n ansamblu pn la locul potrivit n concordan cu definiia ansamblului.

Operatorul Extrage presupune: (1) Extragerea elementului cel mai prioritar (situat

pe poziia q.elemente[1]) (2) Introducerea lui q.elemente[q.nrElemente] (ultimul element al ansmblului) pe prima poziie (3) Deplasarea primului element de sus n jos, spre baza ansamblului, pn la locul potrivit (4) Decrementarea numrului de elemente (q.nrElemente).

Deplasarea de sus n jos n ansamblu de la poziia k spre baza acestuia este


materializat de procedura DownHeap [6.5.5.3.c]. -----------------------------------------------------------{Implementarea cozilor bazate pe prioritate cu ajutorul ansamblelor - operatorul DownHeap} PROCEDURE DownHeap(q: CoadaBazataPePrioritate, k: TipIndice); VAR j: TipIndice; v: TipElement; ret: boolean; BEGIN v:= q.elemente[k]; [6.5.5.3.c] ret:= false; WHILE(k < q.nrElemente DIV 2) AND (NOT ret) DO BEGIN j:= k+k; IF j < q.nrElemente THEN IF q.elemente[j].prioritate < q.elemente[j+1].prioritate THEN j:= j+1; IF v >= q.elemente[j] THEN ret:= true ELSE BEGIN q.elemente[k]:= q.elemente[j]; k:= j END END;{WHILE} q.elemente[k]:= v END;{DownHeap} ------------------------------------------------------------

Deplasarea n ansamblu se realizeaz interschimbnd elementul de pe poziia


curent k cu cel mai prioritar dintre fiii si i avansnd pe nivelul urmtor. oricare dintre fiii si sau s-a ajuns la baza ansamblului.

Procesul continu pn cnd elementul din k devine mai prioritar dect Ca i n situaia anterioar nu este necesar interschimbarea complet ntruct
v este implicat tot timpul.

Bucla WHILE este prevzut cu dou ieiri: Una corelat cu atingerea bazei ansamblului (k>q.nrElemente
DIV 2) A doua corelat cu gsirea poziiei elementului de inserat n interiorul ansamblului (variabila boolean ret).

Cu aceste precizri, implementarea operatorilor Extrage i Inlocuieste este


imediat [6.5.5.3.d]. -----------------------------------------------------------{Implementarea cozilor bazate pe prioritate cu ajutorul ansamblelor - operatorii Extrage i Inlocuieste} FUNCTION Extrage(q: CoadaBazataPePrioritate): TipElement; BEGIN Extrage:= q.elemente[1]; q.elemente[1]:= q.elemente[q.nrElemente]; q.NrElemente:= q.NrElemente-1; DownHeap(q,1) END;{Extrage} [6.5.5.3.d] FUNCTION Inlocuieste(q: CoadaBazataPePrioritate x: TipElement): TipElement; BEGIN q.elemente[0]:= x; DownHeap(q,0); Inlocuieste:= q.elemente[0] END;{Inlocuieste} ------------------------------------------------------------

n cazul operatorului Inlocuieste se utilizeaz i poziia q.elemente[0] ai


crei fii sunt poziiile 0 (ea nsi) i 1. Astfel dac x este mai prioritar dect oricare element al ansamblului, ansamblul rmne nemodificat; altfel x este deplasat n ansamblu. n toate situaiile este returnat q.elemente[0] n calitate de cel mai prioritar element.

Operatorii Suprima i Schimba pot fi implementai utiliznd combinaii simple


ale metodelor de mai sus. Spre exemplu dac prioritatea elementului situat pe poziia k este ridicat atunci poate fi apelat procedura UpHeap(q,k), iar dac prioritatea sa este cobort, procedura DownHeap(q,k) rezolv situaia.

6.6. Structura de date multilist Se numete multilist, o structur de date ale crei noduri conin mai multe cmpuri
de nlnuire.

Cu alte cuvinte, un nod al unei astfel de structuri poate aparine n acelai timp la mai
multe liste nlnuite simple.

n literatura de specialitate termenii consacrai pentru a desemna o astfel de structur


sunt "braid", "multilist", "multiply linked list" [De89]. prezint un exemplu de definire a unei astfel de structuri

n figura 6.6.a o reprezentare grafic a unei structuri multilist iar n secvena [6.6.a] se

IncepNume

UrmSalar Nume Vechime Salar UrrnNume UrmVechime Nil ANA 10 0.4 M Lei


IncepVechime

BIL

25 1.2 M Lei

IncepSalar

IOAN

20

1.8 M Lei

TOM

0.7 M Lei

Nil

Nil

Fig.6.6.a. Exemplu de structur de date multilist -----------------------------------------------------------{Structura multilist - definirea structurilor de date} TYPE TipNume = STRING[20]; TipInfo = RECORD Nume: TipNume; Vechime: integer; Salar: real [6.6.a] END; TipReferintaNod = ^TipNod; TipNod = RECORD Informaie: TipInfo; UrmSalar,UrmNume,UrmVechime: TipReferintaNod END; VAR incepNume,incepVechime,incepSalarii: TipReferintaNod; ------------------------------------------------------------

Avantajul utilizrii unor astfel de structuri este evident. Prezena mai multor nlnuiri ntr-un acelai nod, Respectiv apartenena simultan a aceluiai nod la mai multe liste Asigur acestei structuri de date o flexibilitate deosebit, Avantaj care coroborat cu o manipulare relativ facil specific structurilor
nlnuite, Este exploatat cu precdere la implementarea bazelor de date.

Aria de utilizare a structurilor multilist este ns mult mai extins. Spre exemplu, o astfel de structur poate fi utilizat cu succes la memorarea
matricelor rare.

Este cunoscut faptul c matricele rare sunt matrice de mari dimensiuni


care conin de regul un numr redus de elemente restul fiind poziionate de obicei pe zero. Din acest motiv memorarea lor n forma obinuit a tablourilor bidimensionale presupune o mare risip de memorie. [6.6.b]

Spre a evita aceast risip se poate utiliza structura multilist din secvena n aceast structur, fiecrui element valid al matricei i se asociaz un
nod al structurii n nod se memoreaz: Indicii elementului curent nlnuirile la proximul element valid din acelai rnd respectiv la proximul element valid situat pe aceeai coloan. -----------------------------------------------------------{Aplicaie la structura multilist - memorarea matricilor rare} CONST TYPE randMax = ...; colMax = ...; TipReferintaNod = ^TipNod; TipNod = RECORD rand: 1..randMax; coloana: 1..colMax; info: ...; dreapta,jos: TipReferintaNod; END;

VAR matrice: TipReferintaNod; ------------------------------------------------------------

6.7. Liste generalizate Singura structur de date definit n limbajul de programare LISP este lista. Dup cum se cunoate limbajul LISP este destinat manipulrii simbolurilor fiind
utilizat cu precdere n domeniul Inteligenei Artificiale. un ir de caractere i / sau cifre.

Unitatea structural fundamental definit n LISP este atomul care este conceput ca n acest context, o list este o mulime de paranteze coninnd orice numr de atomi i
liste. Spre exemplu: B =(b) reprezint n LISP o list care conine un singur element L =(a,b(cde)f) reprezint o list cu o structur mai complex. numit list generalizat. Nodurile unei astfel de liste au trei cmpuri: Atom - cmp boolean; Un cmp dependent de valoarea cmpului Atom .

Atomii i listele sunt memorate n LISP cu ajutorul unei structuri de date speciale

memoreaz o informaie (un atom). Dac Atom este FALSE cmpul se numeste lista i memoreaz o referin la o list; Urm cmp de nlnuire.

Dac Atom este TRUE atunci acest cmp se numeste info i

n figura 6.7.a apar reprezentrile grafice ale atomului a i ale listelor B i L


definite anterior, n accepiunea acestei definiii.
a B T

Nil

Nil

T F

Nil

Nil

Nil

T e Nil

Fig.6.7.a. Exemple de liste generalizate

Pentru o mai bun nelegere a acestui concept, n secvena [6.7.a] apare specificarea
PASCAL a structurii de date ListGeneralizat.

Din cauza restriciei de implementare a articolului cu variante n acest limbaj,

implementare care impune plasarea variantelor la sfritul articolului, cmpul de nlnuire Urm apare n declaraia nodului la nceputul acestuia.

-----------------------------------------------------------{Liste generalizate - definirea structurilor de date (varianta Pascal)} TYPE TipListaGeneralizata = ^TipNod; TipNod = RECORD Urm: TipListaGeneralizat; [6.7.a] CASE Atom: boolean OF TRUE:(info: CHAR); FALSE: (lista: TipListaGeneralizata) END; VAR a,B,L: TipListaGeneralizata; ----------------------------------------------------------- Pentru aceast structur de date n LISP sunt definii trei operatori specifici: car (lista:TipListaGeneralizata):TipListaGeneralizata; operator care returneaz primul element al listei (care poate fi atom sau list);

cdr (list:TipListaGeneralizata):TipListaGeneralizata; operatorul "cudder" returneaz restul listei, adic ceea ce rmne dup nlturarea primului element; cons (M,N:TipListaGeneralizata):TipListaGeneralizata; operator care realizeaz construcia unei liste n care M este primul element iar N restul listei.

Spre exemplu pentru lista generalizat L =(ab(cde)f), car(L)= a iar cdr(L)


=(b(cde)f), operatori vizualizai n figura 6.7.b.
cdr(L) car(L)

T a Nil T

Nil

T d

Nil

Nil

Fig.6.7.b. Operatorii car i cdr aplicai asupra listei generalizate L din fig.6.7.a

n mod asemntor, pentru listele generalizate M =(gh) i N =(ijk), aplicarea

operatorului cons conduce la lista generalizat cons(M,N)=((gh)ijk)) care apare reprezentat grafic n fig. 6.7.c.
N

Nil

Nil

Nil

T Cons(M,N) F

Nil

Nil

Nil

Nil

Fig.6.7.c. Exemplu de utilizare a operatorului cons

6.8. Tipul de date abstract asociere Mapping-ul sau asocierea memoriei este o funcie definit pe mulimea elementelor
unui tip denumit tip domeniu cu valori n mulimea elementelor unui alt tip (eventual acelai) numit tip valoare (codomeniu).

De fapt o asociere M , pune n coresponden un element V aparinnd tipului valoare


cu un element D al tipului domeniu prin relaia M(D)=V.

Anumite asocieri, ca spre exemplu PATRAT(i)=i2, pot fi implementate simplu cu


ajutorul unor funcii care implementeaz expresia aritmetic de calcul, sau prin alte mijloace simple de determinare a lui M(D) pornind de la D. memorarea valorii corespunztoare pentru fiecare D n parte.

n multe situaii ns, nu este posibil descrierea asocierii M(D) dect prin n cadrul acestui paragraf, se vor face n continuare referiri la metodele de
implementare ale unor astfel de asocieri.

Pentru nceput se va preciza tipul de date abstract asociere.

6.8.1. TDA Asociere

Fiind dat un element D aparinnd unui TipDomeniu este necesar: (1) S se determine valoarea lui M(D), (2) S se afle dac M(D) este definit sau nu pentru un D dat, (3) S de afle dac D aparine sau nu domeniului asocierii. (4) De asemenea este necesar un operator care permite introducerea de noi
elemente n domeniul lui M, preciznd valorile corespunztoare din domeniul valorilor. (5) n anumite situaii apare necesitatea modificrii valorii lui M(D) sau a iniializrii domeniului valorilor cu asocierea vid, adic cea a crui domeniu este vid.

Aceste operaii sunt precizate n secvena [6.8.1.a] care descrie TDA Asociere.
-----------------------------------------------------------TDA Asociere Modelul matematic: o funcie algebric definit pe mulimea elementelor unui tip denumit TipDomeniu i cu valori n mulimea elementelor unui alt tip denumit TipValoare. Notaii: M: TipAsociere; D: TipDomeniu; V: TipValoare; Operatori: 1.Initializeaz(M: TipAsociere); - face asocierea M vid. 2.Atribuie(M: TipAsociere, D: TipDomeniu, V: TipValoare); - operator care definete pe M(D) ca fiind egal cu V, indiferent dac M(D) a fost definit sau nu anterior. [6.8.1.a]

3.Determina(M: TipAsociere, D: TipDomeniu, V: TipValoare): boolean; - operator care returneaz valoarea adevrat i asigneaz variabile V cu M(D) dac aceasta din urm exist; altfel returneaz valoarea fals. ------------------------------------------------------------

6.8.2. Implementarea TDA Asociere cu ajutorul tablourilor

n multe situaii tipul domeniu al funciei de asociere este un tip elementar care poate
fi utilizat ca i indice ntr-o structur tablou.

n limbajul Pascal gama acestor indici poate fi extins la oricare tip scalar (subdomeniu,
caracter sau enumerare).

n cele ce urmeaz se va utiliza tipul asociere definit n secvena [6.8.2.a].


-----------------------------------------------------------------

{TDA Asociere - implementare cu ajutorul tablourilor definirea structurilor de date}


TYPE TipDomeniu = primaValoare ..ultimaValoare; [6.8.2.a] TYPE TipAsociere = ARRAY[TipDomeniu] OF TipValoare; -----------------------------------------------------------------

Presupunnd n continuare c nedefinit este o constant a lui TipValoare,


operatorii afereni TDA Asociere apar definii n secvena [6.8.2.b].
-----------------------------------------------------------------

{TDA Asociere - implementare cu ajutorul tablourilor operatorii Initializeaza, Atribuie i Determina}


PROCEDURE Initializeaza(VAR M: TipAsociere); VAR i: TipDomeniu; BEGIN FOR i:= primaValoare TO ultimaValoare DO M[i]:= nedefinit END;{Initializeaza} PROCEDURE Atribuie(VAR M: TipAsociere; D: TipDomeniu;
V: TipValoare);

BEGIN M[D]:= V END;{Atribuie} FUNCTION Determina(VAR M:TipAsociere; D: TipDomeniu; VAR V: TipValoare): boolean; BEGIN IF M[D]:= nedefinit THEN Determina:= FALSE ELSE BEGIN V:= M[D] Determina:= TRUE END END;{Determina} -----------------------------------------------------------------

6.8.3. Implementarea TDA Asociere cu ajutorul listelor nlnuite

Exist multe posibiliti de a implementa funciile de asociere cu domenii finite. Astfel tabelele HASH ( 7.) reprezint n anumite situaii o soluie excelent. n paragraful de fa se va descrie o alt posibilitate. Orice structur asociere, poate fi reprezentat ca o list de perechi (D1,V1),

(D2,V2),...,(Dk,Vk), unde D1, D2,...,Dk sunt toate elementele ale domeniului, iar Vi valori ale codomeniului pe care asocierea le pune n coresponden cu Di pentru i = 1,2...,k. Pentru a implementa astfel de perechi de elemente se poate utiliza o structur de date list. n acest sens, tipul abstract de date asociere poate fi definit ca i o list nlnuit, implementat n oricare din manierele cunoscute, n care TipElement are urmtoarea structur [6.8.3.a]. -----------------------------------------------------------{TDA Asociere - implementare cu ajutorul listelor nlnuite - structuri de date} TYPE TipElement = RECORD argument: TipDomeniu; [6.8.3.a] valoare: TipValoare END; ------------------------------------------------------------

Cei trei operatori care gestioneaz structura asociere, descrii n termenii operatorilor
definii asupra TDA Lista apar n secvena [6.8.3.b]. -----------------------------------------------------------{TDA Asociere - implementare cu ajutorul listelor nlnuite - operatorii Initializeaza, Atribuie i Determina} PROCEDURE Initializeaza(VAR M: TipAsociere); BEGIN [identica cu cea de la liste] END;{Initializeaza}

[6.8.3.b]

PROCEDURE Atribuie(VAR M: TipAsociere; D: TipDomeniu; V: TipValoare); VAR x: TipElement; p: TipPozitie; BEGIN Creaza(x); x.argument:= D; x.valoare:= V; p:= Primul(M) WHILE p<>Fin(M) DO IF Furnizeaza(p,M).argument=D THEN Suprima(p,M) {suprim M(D)} ELSE p:= Urmator(p,M): Insereaza(M,x,Primul(M)) {insereaza (D,V) in list}

END;{Atribuie} FUNCTION Determina(VAR M: TipAsociere; D: TipDomeniu; VAR V: TipValoare): boolean; VAR p: TipPozitie; calculeaza: boolean; BEGIN p:= Primul(M); calculeaza:= false; WHILE (p<>Fin(M)) AND NOT calculeaza DO BEGIN IF Furnizeaza(p,M).argument=D THEN BEGIN V:= Furnizeaza(p,M).valoare; calculeaza:= true END;{IF} p:= Urmator(p,M) END;{WHILE} Determina:= calculeaza END;{Determina} ------------------------------------------------------------

7. Tabele
7.1. TDA Tabel Noiunea de tabel este foarte cunoscut, fiecare dintre noi consultnd tabele cu
diverse ocazii.

Ceea ce intereseaz ns n acest context se refer la evidenierea acelor caracteristici


ale tabelelor care definesc tipul de date abstract tabel.

n figura 7.1.a apare un exemplu de tabel n dou variante.


Nume Prenume Antonescu Ion Brbulescu Petre Card Gheorghe Mare Vasile Suciu Horia An 3 2 5 2 1 Medie 7,89 9,20 9,80 8,33 9,60 Nume Prenume Suciu Horia Brbulescu Petre Mare Vasile Antonescu Ion Card Gheorghe An 1 2 2 3 5 Medie 9,60 9,20 8,33 7,89 9.80

Fig.7.1.a. Exemple de tabele

Dup cum se observ, tabela din figur, ca i marea majoritate a tabelelor, este
alctuit din articole. Acest lucru nu este ns obligatoriu, deoarece spre exemplu prima coloan a acestei tabele poate fi considerat la rndul ei o tabel (tabela studenilor nscrii). n prima variant studenii sunt aranjai n ordine alfabetic, n a doua n ordinea cresctoare a anilor de studiu.

Se spune c tabela este ordonat dup o "cheie" element care faciliteaz regsirea

informaiilor pe care le conine. Astfel o "cheie" este un cmp sau o parte component a unui cmp care identific n mod unic o intrare n tabel. Nu este necesar ca tabela s fie sortat dup o cheie ns o astfel de sortare simplific cutarea n tabel.

Dintre operaiile frecvente care se execut asupra tabelelor se menioneaz: n primul rnd cutarea n tabel n vederea localizrii informaiilor care

satisfac una sau mai multe condiii. n anumite situaii se impune prelucrarea integral a tabelei ntr-un proces de "traversare" a acesteia. Prelucrarea unei anumite intrri a tabelei n cadrul procesului de traversare se numete "vizitare" ("visiting"). Vizitarea poate include toate genurile de prelucrri ncepnd de la afiare i terminnd cu modificarea coninutului intrrii.

n definitiv TDA Tabel poate fi descris n mod sintetic dup cum urmeaz [7.1.a].
-----------------------------------------------------------TDA Tabel Modelul Matematic: O secven finit de elemente. Fiecare element are cel puin o cheie care identific n mod unic intrarea n tabel i care este un cmp sau un subcmp al elementului. Notaii: TipElement - tipul elementelor tabelei. TipCheie - tipul cheii. t: TipTabela; e: TipElement; k: TipCheie; b: boolean. [7.1.a] Operatori: 1.CreazTabelaVid(t: TipTabela); - operator care face tabela t vid. 2.TabelVid(t: TipTabela): boolean; - operator boolean care returneaz true dac tabela t este goal. 3.TabelPlin(t: TipTabela): boolean; - operator boolean care returneaz true dac tabela e plin. 4.CheieElemTabel(e: TipElement): TipCheie; -operator care returneaz cheia elementului e. 5.CautCheie(t: TipTabela,k: TipCheie): boolean; operator care returneaz true dac cheia k se gsete n tabela t. 6.InserElemTabel(t: TipTabela,e: TipElement); -operator care nsereaz elementul e n tabela t. Se presupune c e nu exist n tabel. 7.SuprimElemTabel(t: TipTabela,k: TipCheie); - operator care suprim din t elementul cu cheia k. Se presupune c exist un astfel de element n t. 8.FurnizeazElemTabel(t: TipTabela,k: TipCheie): TipElement; - operator care returneaz elementul cu cheia k. Se presupune c elementul aparine lui t. 9.TraverseazTabel(t: TipTabela, Vizitare (List Argumente)) operator care execut procedura Vizitare pentru fiecare element al tabelei t. ------------------------------------------------------------

n anumite implementri este convenabil s se cunoasc numrul curent de elemente


coninute n tabel. n acest scop se poate asocia tabelei un contor de elemente i un operator care-l furnizeaz (DimensTabel(t:TipTabela):TipContor). Contorul se iniializeaz la crearea tabelei i se actualizeaz ori de cte ori se realizeaz inserii sau suprimri n tabel.

n unele aplicaii este convenabil de asemenea s se defineasc operaia

Actualizare(t: TipTabela, e: TipElement) Operaia realizeaz actualizarea acelei intrri din tabel care conine elementul e. n termenii operatorilor anterior definii, actualizarea poate fi implementat astfel [7.1.b]:

-----------------------------------------------------------{TDA Tabel - operatorul Actualizare} SuprimElemTabel(t,CheieElemTabel(e)); InserElemTabel(t,e) [7.1.b] ------------------------------------------------------------

Structura de date tabel este strns legat de bazele de date relaionale. De fapt structura tabel este componenta principal a unei baze de date relaionale. O baz de date n general, este o colecie de date integrate, destinat unei mari
varieti de aplicaii, memorat ntr-o manier eficient.

O baz de date relaional este format din mai multe tabele numite relaii. Asupra acestor tabele pot fi realizate prelucrri extrem de diverse i de fapt prin
intermediul lor se realizeaz accesul eficient la elementele bazei de date.

7.2. Tehnici de implementare a TDA Tabel Exist n principiu mai multe posibiliti de implementare a tipului de date abstract
tabel.

Este vorba despre tablouri, liste nlnuite, tabele de dispersie, arbori etc. Dintre aceste posibiliti n cadrul paragrafului de fa vor fi analizate cele bazate pe
structurile tablou i list nlnuit.

Implementarea bazat pe tehnica dispersiei face obiectul seciunii urmtoare iar cea
bazat pe arbori binari va fi tratat ulterior.

n general listele nlnuite sau tablourile, ambele n variant ordonat sau

neordonat se constituie n 4 metode posibile de implementare a tabelelor. n continuare se va realiza o analiz comparat a acestor metode, detaliile de implementare fiind lsate ca exerciiu. Aceasta analiz are un caracter de generalitate care se rsfrnge i asupra altor tipuri de date n a cror implementare sunt implicate structurile tablou respectiv list nlnuit. n esen, n fiecare dintre cele 4 situaii vor fi evideniate avantajele, dezavantajele, concluziile i recomandrile referitoare la utilizare. aceea c particularitatea aplicaiei n care este el utilizat, determin cea mai potrivit metod de implementare efectuate asupra structurii de date.

Observaia fundamental de la care pornete orice implementare a unui TDA este

Particularitatea unei aplicaii deriv direct din natura i din dinamica operaiilor

7.2.1. Implementarea TDA Tabel cu ajutorul tabourilor ordonate a) Avantaje Posibilitatea utilizrii tehnicii cutrii binare (O(log2 n)). Acest avantaj se rsfrnge asupra fazei de cutare a cheii n operaiile de inserie, suprimare, furnizare element i cutare cheie. Furnizarea informaiei i cutarea cheii este foarte rapid O(log 2 n). De fapt furnizarea const din doi timpi: faza de cutare (O(log 2 n)) i din faza de returnare O(1). Traversarea ordonat a cheilor este liniar (O(n)). b) Dezavantaje Faza de instalare la inserie este lent (O(n)) deoarece se mut toate intrrile ncepnd cu punctul de inserie pentru a face loc n tabel. Inseria este lent(O(n)). Ea const din faza de cutare care este rapid (O(log2 n)) i din faza de instalare care realizeaz inseria propriu-zis (O(n)). Faza de extragere la suprimare este lent (O(n)) deoarece se mut toate intrrile ncepnd cu locul de suprimare. Suprimarea este lent (O(n)). Ea const din faza de cutare (O(log2 n)) i din faza de extragere (O(n)). Crearea tabelei lent (O(n log2 n)). Pentru fiecare din cele n elemente se realizeaz o inserie O(log2 n). Trebuie cunoscut apriori numrul total de intrri n tabel. Operatorul TabelPlin trebuie executat naintea fiecrei inserii. c) Concluzii Utilizarea acestei implementri a tabelelor este avantajoas atunci cnd: Se realizeaz multe consultri, verificri sau rapoarte asupra tabelei, Numrul de inserii i suprimri este redus Tabela este de mari dimensiuni. Trebuie cunoscut cu bun precizie apriori dimensiunea maxim a tabelei.

7.2.2. Implementarea tabelelor cu ajutorul tablourilor neordonate a) Avantaje Inserie rapid care se realizeaz la sfritul tabloului (O(1)). Faza de extragere la suprimare este rapid (O(1)) dac se mut ultima intrare a tabelei peste cea suprimat. Construcia tabelei este rapid (O(n)). b) Dezavantaje Cutarea se realizeaz secvenial O(n). Acest dezavantaj se reflect asupra suprimrii, furnizrii i cutrii cheilor. Suprimarea este lent (O(n)). Ea const din faza de cutare (O(n)) urmat de faza de extragere (O(1)). Furnizarea este lent (O(n)).

Cutarea unei chei este lent (O(n)). Ea const din cutarea propriu-zis

(O(n)) urmat de faza de returnare (O(1)). Traversarea ordonat a cheilor este lent, ea necesitnd un efort de calcul cuprins ntre O(n log 2 n) i O(n2) n dependen de metoda de sortare utilizat. Trebuie cunoscut cu precizie numrul total de intrri n tabel. c) Concluzii Acest mod de implementare este recomandat a fi utilizat atunci cnd se execut multe traversri neordonate ale tabelei, sau cnd se fac comparaii sau calcule care afecteaz fiecare intrare. Construcia i inseria sunt simple. Trebuie cunoscut apriori dimensiunea maxim a tabelei. Implementarea nu este recomandabil atunci cnd se efectueaz frecvent suprimri i cutri sau traversri ordonate. 7.2.3. Implementarea tabelelor cu ajutorul listelor nlnuite ordonate a) Avantaje Faza de instalare la inserie este rapid (O(1)). Faza de extragere la suprimare este rapid (O(1)). Traversarea n manier ordonat a tabelei este rapid (O(n)). Nu este necesar precizarea dimensiunii maxime a tabelei. Operatorul TabelPlin este ntotdeauna fals. b) Dezavantaje Cutarea se realizeaz n manier secvenial (O(n)). Acest dezavantaj se rsfrnge asupra fazei de cutare la inserie, suprimare, furnizare element i cutare cheie. Inseria este lent (O(n)), ea constnd din cutare (O(n)) i instalare (O(1)). Suprimarea este lent (O(n)) ea constnd din cutare (O(n)) i extragere (O(1)). Furnizarea este lent (O(n)) deoarece presupune o cutare (O(n)) urmat de un retur (O(1)). Cutarea cheii este lent (O(n)). Ea const dintr-o cutare (O(n)) urmat de un retur (O(1)). Totui n situaia n care cheia nu exist n tabel, cutarea se poate opri chiar dup poziia unde ar trebui s se gseasc cheia. Construcia tabelei este lent (O(n log 2 n) O(n2)) funcie de tehnica utilizat pentru sortare. Efortul de calcul necesitat de gestionarea nlnuirilor. c) Concluzii Structura este eficient pentru situaiile n care: Se realizeaz traversri frecvente n manier ordonat ale tabelei Nu se cunoate numrul maxim de elemente. Structura nu este propice investigaiilor. Inseria i suprimarea consum timp n tabele de dimensiuni mari. 7.2.4. Implementarea tabelelor cu ajutorul listelor nlnuite neordonate

a) Avantaje Inseria este rapid ea realizndu-se la nceputul sau la sfritul listei (O(1)). Faza de extragere la suprimare este rapid (O(1)). Construcia tabelei este rapid (O(n)) deoarece const dintr-o succesiune de inserii. Nu este necesar precizarea dimensiunii maxime a tabelei. Operatorul TabelPlin este ntotdeauna fals. b) Dezavantaje Cutarea este secvenial (O(n)). Dezavantajeaz inseria, suprimarea, furnizarea i cutarea propriu-zis. Suprimarea este lent (O(n)) deoarece ea const din faza de cutare (O(n)) urmat de faza de extragere (O(1)). Furnizarea este lent (O(n)). Const din cutare (O(n)) i retur (O(1)). Cutarea cheii este lent (O(n)). Similar ca i la furnizare. Traversarea n ordinea cheilor este lent (O(n log2 n) O(n2)) funcie de metoda de sortare utilizat. Regia legturilor. c) Concluzii Aceast modalitate de implementare este indicat atunci cnd: Se cere construcia rapid a tabelei, Se realizeaz multe inserii adiionale Nu se cunoate dimensiunea maxim a tabelei. Investigrile, suprimrile sau traversrile ordonate sunt consumatoare de timp.

7.3. Implementarea tabelelor prin tehnica dispersiei Una dintre metodele avansate de implementare a tabelelor o reprezint tehnica
dispersiei.

Aceast tehnic cunoscut i sub denumirea de "hashing", reprezint un exemplu de

rezolvare elegant i deosebit de eficient a problemei cutrii ntr-un context bine precizat.

7.3.1. Consideraii generale

Formularea problemei: Se d o mulime S de noduri identificate fiecare prin valoarea unei chei,

organizate ntr-o structur tabel. Pe mulimea cheilor se consider definit o relaie de ordonare. Se cere ca tabela S s fie organizat de o asemenea manier nct regsirea unui nod cu o cheie precizat k, s necesite un efort ct mai redus.

n ultim instan, accesul la un anumit nod presupune determinarea acelei intrri


din tabel, la care el este memorat.

Astfel, problema formulat se reduce la gsirea unei asocieri specifice (H) a


mulimii cheilor (K) cu mulimea intrrilor (A) [7.3.1.a]. -----------------------------------------------------------H: K A [7.3.1.a] ------------------------------------------------------------

Problema nu este nou, ea a mai fost abordat pe parcursul acestei lucrri. Pn la momentul de fa astfel de asocieri au fost implementate cu ajutorul
unor algoritmi de cutare n tablouri i liste n diferite strategii de abordare i n diverse moduri de organizare.

n seciunea de fa se propune o alt metod, simpl i foarte eficient n multe situaii. Metoda se bazeaz pe tehnica dispersiei i se aplic structurilor de date tablou. Dei este o metod care utilizeaz o structur static de memorie, prin performanele
sale este un competitor serios al metodelor bazate pe structuri de date avansate.

Ideea pe care se bazeaz tehnica dispersrii este urmtoarea: Se rezerv static un volum constant de memorie pentru "tabela dispersat",

care conine nodurile cu care se lucreaz. Tabela se implementeaz n forma unei structuri de date tablou T avnd drept elemente nodurile n cauz. Notnd cu p numrul elementelor tabloului, indicele a care precizeaz un nod oarecare, ia valori ntre 0 i p-1. Se noteaz cu K mulimea tuturor cheilor i cu k o cheie oarecare. Numrul cheilor este de obicei mult mai mare dect p. Un exemplu realist n acest sens este acela n care cheile reprezint identificatori cu cel mult zece caractere, caz n care exist aproximativ 1015 chei diferite n timp ce valoarea practic a lui p este 103. Se consider n aceste condiii funcia H, care definete o aplicaie a lui K pe mulimea tuturor indicilor, mulime care se noteaz cu L (L = {0,1,2,...,p-1}) [7.3.1.b]. -----------------------------------------------------------a = H(k) unde k K i a L [7.3.1.b] ------------------------------------------------------------

Funcia de asociere H este de fapt o funcie de dispersie i ea permite ca pornind de

la o cheie k s se determine indicele asociat a. Este evident c H nu este o funcie bijectiv deoarece numrul cheilor este mult mai mare dect numrul indicilor. Practic exist o mulime de chei (clas) crora le corespunde acelai indice a. Din acest motiv, o alt denumire uzual sub care este cunoscut tehnica dispersrii este aceea de "transformare a cheilor" ("key transformation"), ntruct cheile se transform practic n indici de tablou.

Principiul metodei este urmtorul. Pentru nregistrarea unui nod cu cheia k: (1) Se determin mai nti indicele asociat a=H(k), (2) Se depune nodul la T[a].

Dac n continuare apare o alt cheie k care are acelai indice asociat

a = H(k)= H(k), atunci s-a ajuns la aa numita "situaie de coliziune", n acest caz care trebuie adoptat o anumit strategie de rezolvare a acestei probleme. La cutare se procedeaz similar. (1) Dat fiind o cheie k, se determin a = H(k), (2) Se verific dac T[a] este nodul cutat, adic dac T[a].cheie=k. (3) Dac da, atunci s-a gsit nodul, n caz contrar s-a ajuns la coliziune.

n concluzie, aplicarea tehnicii dispersiei presupune soluionarea a dou probleme: (1) Definirea funciei de dispersie H; (2) Rezolvarea situaiilor de coliziune. Rezolvarea favorabil a celor dou probleme conduce la rezultate remarcabil de
eficiente, cu toate c metoda prezint i anumite dezavantaje, asupra crora se va reveni.

7.3.2. Alegerea funciei de dispersie

Funcia de dispersie trebuie: Pe de o parte s repartizeze ct mai "uniform" cheile pe mulimea indicilor
deoarece n felul acesta se reduce probabilitatea coliziunilor, Pe de alt parte trebuie s fie uor i rapid calculabil.

Proprietile acestei funcii sunt aproximate n literatura de specialitate de termenul


"hashing" (amestec) motiv pentru care funcia se noteaz generic cu H fiind denumit funcie de amestec ("hash function").

Din acelai motiv tehnica dispersiei se mai numete i tehnic "hashing". n literatur se definesc diverse funcii de dispersie fiecare cu avantaje i dezavantaje
specifice, activitate care contureaz un domeniu de cercetare extrem de activ [Kn76].

n cele ce urmeaz se va prezenta o variant a unei astfel de funcii. Fie ORD(k) o valoare ntreg unic ataat cheii k, valoare care precizeaz
numrul de ordine al cheii k n mulimea ordonat a tuturor cheilor (1.3.2.). Funcia H se definete n aceste condiii astfel [7.3.2.a]: -----------------------------------------------------------H(k)= ORD(k) MOD p [7.3.2.a] ----------------------------------------------------------- Aceast funcie care asigur distribuia cheilor pe mulimea indicilor (0,p-1) st la baza mai multor metode de repartizare.

S-a demonstrat experimental c n vederea repartizrii ct mai uniforme a cheilor pe


mulimea indicilor este recomandabil ca p s fie un numr prim.

Cazul n care p este o putere a lui 2 este extrem de favorabil din punctul de
vedere al eficienei calculului funciei, dar foarte nefavorabil din punctul de vedere al coliziunilor, mai ales n cazul n care cheile sunt secvene de caractere [Wi76].

7.3.3. Tratarea situaiei de coliziune

Prezena unei situaii de coliziune presupune generarea unui nou indice n tabel
(indice secundar), pornind de la cel anterior.

Exist dou modaliti principiale de generare a indicilor secundari: (1) O modalitate care presupune un spaiu suplimentar asociat tabelei i care

prefigureaz tehnica dispersiei deschise (2) O a doua modalitate care exploateaz doar spaiul de memorie alocat tabelei i care prefigureaz tehnica dispersiei nchise.

7.3.3.1. Tehnica dispersiei deschise

Tehnica dispersiei deschise presupune nlnuirea ntr-o list nlnuit a tuturor

nodurilor ale cror indici primari sunt identici, metod care se mai numete i nlnuire direct. necesar alocndu-se din aa numita "zon de depire" a tabelei.

Elementele acestei liste pot sau nu aparine tabloului iniial: n ultimul caz memoria Structurile de date aferente acestei tehnici sunt prezentate n [7.3.3.1.a].
-----------------------------------------------------------{Tehnica dispersiei deschise} CONST DimensiuneTabela = p {numr prim}

TYPE TipIndex = 0..DimensiuneTabela; TipInfo = ... ; TipCheie = ... ; TipElement = RECORD cheie: TipCheie; info: TipInfo END; TipReferinta = ^TipNod; [7.3.3.1.a] TipNod = RECORD element: TipElement; urm: TipReferinta END; TipTabela = ARRAY[TipIndex] OF TipReferinta; VAR t: TipTabela; ----------------------------------------------------------- Aceast metod are avantajul c permite suprimarea elementelor din tabel, operaie care nu este posibil n toate implementrile.

Dintre dezavantaje se evideniaz dou: Necesitatea meninerii unei liste secundare Prelungirea fiecrui nod cu un spaiu de memorie pentru pointerul (indexul)
necesar nlnuirii.

7.3.3.2. Tehnica dispersiei nchise

Tehnica dispersiei nchise utilizeaz, dup cum s-a precizat anterior, strict spaiul de
memorie alocat tabelei.

n cazul unei coliziuni se realizeaz parcurgerea dup o anumit regul a tabloului


dispersat T pn la gsirea primului loc liber.

Structurile de date aferente acestei tehnici apar n [7.3.3.2.a].


-----------------------------------------------------------{Tehnica dispersiei nchise} CONST DimensiuneTabela = p; {numr prim}

TYPE TipIndex = 0..DimensiuneTabela; TipInfo = ... ; TipCheie = ... ; TipElement = RECORD cheie: TipCheie; info: TipInfo [7.3.3.2.a] END; TipTabela = ARRAY[TipIndex] OF TipElement; -----------------------------------------------------------Schia de principiu a algoritmului care realizeaz cutarea n tabel este prezentat n secvena [7.3.3.2.b]. -----------------------------------------------------------{Tehnica dispersiei nchise - cutarea unui element n tabel} VAR t: TipTabela; l: TipIndex; a:= H(k); i:= 0; REPEAT [7.3.3.2.b] IF t[a].cheie=k THEN *element gsit ELSE IF t[a].cheie=liber THEN *elementul nu este n tabel ELSE BEGIN{coliziune} i:= i+1; a:= a+g(i) END UNTIL (gsit) OR (nu este in tabel) OR (tabel plin); ------------------------------------------------------------

Funcia g(i) este aceea care precizeaz regula dup care se parcurge tabela n caz
de coliziune.

Exist numeroase astfel de funcii g crora le corespund modaliti diverse n


conformitate cu care se realizeaz parcurgerea tabelei. deschis patratic".

Dintre acestea cele mai cunoscute sunt "adresarea deschis liniar" i "adresarea

Adresarea deschis liniar

Adresarea deschis liniar prefigureaz cea mai simpl metod de parcurgere Conform adresrii deschise liniare, intrrile tabelei se parcurg secvenial, tabela
considerndu-se circular. Cu alte cuvinte g(i)=i [7.3.3.2.c]. -----------------------------------------------------------a0 = H(k) ai =(a0+i) MOD p i=1...p-1 [7.3.3.2.c] ----------------------------------------------------------- n fragmentul de program din secvena [7.3.3.2.d], se prezint procesul de cutare n tabel a intrrii cu cheia k. Dac intrarea se gsete, se asigneaz variabila de ieire v cu coninutul su. -----------------------------------------------------------{Adresarea deschis liniar - cutarea unui element n tabel} VAR t: TipTabela a,i: TipIndex; gasit,absent: boolean; v: TipElement;

a:= H(k); i:= l; gasit:= false; absent:= false; REPEAT IF t[a]=liber THEN absent:= true ELSE IF t[a].cheie=k THEN BEGIN v:= t[a]; gasit:= true END [7.3.3.2.d] ELSE BEGIN{coliziune} a:= a+1; IF a=p THEN a:= 0; IF a=i THEN absent:= true END UNTIL gasit OR absent; ------------------------------------------------------------

Acest algoritm are ca dezavantaj tendina de a ngrmdi nodurile n continuarea


celor deja nregistrate.

Dezavantajul nu este inerent metodei de adresare deschis liniar, el datorndu-se


regulii de parcurgere a elementelor tabelei.

Ideal ar fi ca n caz de coliziune s se aplice o regul care distribuie ncercrile, cu

probabiliti egale, pe locurile libere rmase n tabel. Realizarea practic a acestui deziderat este ns deosebit de complex.

Adresarea deschis ptratic

Adresarea deschis ptratic reprezint o soluie de compromis relativ simpl i


eficient.

Funcia de repartizare are expresia g(i)=i2 [7.3.3.2.e].

-----------------------------------------------------------a0 = H(k) ai = (a0+i2) MOD p i=1,2,3,... [7.3.3.2.e] ------------------------------------------------------------

O manier simpl de implementare a acestei funcii de parcurgere este urmtoarea: Dac un indice a conduce la coliziune, atunci proxima ncercare se face la

indicele a + r unde r este o mrime care se iniializeaz pe 1 i care dup fiecare ncercare nereuit se incrementeaz cu 2. Astfel valorile lui r formeaz irul numerelor fr so, ir a crui sum este un ptrat perfect.

n caz de coliziuni repetate, numrul ncercrilor se va limita prin impunerea condiiei


formale r < p .

Algoritmul de inserie a unui nou element v n tabel este prezentat n secvena


[7.3.3.2.f]. -------------------------------------------------------{Adresarea deschis patratic - inseria unui element n tabel} VAR t: TipTabela a,r: TipIndex; depus: boolean; v: TipElement;

a:= H(k); r:= 1; depus:= false; REPEAT IF t[a]=liber THEN BEGIN t[a]:= v; depus:= true END ELSE BEGIN a:= a+r; IF a>=p THEN a:= a-p; r:= r+2 END

[7.3.3.2.f]

UNTIL depus OR (r=p); ------------------------------------------------------------

Un dezavantaj minor al acestei metode este c se poate ntmpla s nu se gseasc

nici un element liber, ajungndu-se la depirea tabelei, cu toate c n realitate n tabel mai exist locuri libere. Se demonstreaz ns c probabilitatea acestui eveniment este foarte mic i c n general metoda este foarte performant.

Traversarea ordonat a tabelei este greu de realizat, Dezavantajul major al acestei implementri se refer la faptul c ea nu permite
suprimarea elementelor din tabel. Suprimarea unui element oarecare al tabelei poate ntrerupe secvena liniar sau ptratic de parcurgere a tabelei fapt care poate conduce la pierderea iremediabil a unor intrri.

Pentru a remedia acest dezavantaj major se poate recurge la urmtoarea stratagem. Se asociaz fiecrei intrri a tabelei un cmp de tip enumerare numit stare

care poate lua valorile liber, ocupat sau ters, n consecin TipElement din secvena [7.3.3.2.a] se modific dup cum urmeaz [7.3.3.2.g]. -----------------------------------------------------------TYPE ... ; TipElement = RECORD cheie: TipCheie; info: TipInfo; [7.3.3.2.g] stare: (liber,ocupat,ters) END; ------------------------------------------------------------

Iniial la crearea tabelei se trec toate locaiile sale n starea liber. Pe msur ce se insereaz elemente, locaiile corespunztoare se trec n starea

ocupat. n aceste condiii, suprimarea se poate realiza simplu fr ndeprtarea efectiv a elementului prin simpla trecere a strii cmpului asociat n ters. n acest mod, n procesul de cutare a unei chei se parcurg i cmpurile aflate n starea ters, secvena de adresare creat rmnnd nemodificat. Cmpurile marcate cu ters pot fi ns reutilizate la introducerea unor noi elemente n tabel. 7.3.4. Analiza performanei tehnicii dispersiei nchise

Pentru analiza performanei tehnicii dispersiei nchise se presupune pentru nceput c: (1) Toate cheile au probabiliti de apariie egal posibile (2) C funcia de distribuie H le repartizeaz uniform pe mulimea intrrilor
tabelei.

Se presupune de asemenea, c o cheie oarecare se insereaz ntr-o tabel de dimensiune


n , care conine deja k elemente, adic k intrri sunt deja ocupate.

o singur comparaie pentru a gsi o cheie n tabela [7.3.4.a]. -----------------------------------------------------------p1 = 1 k nk = n n

n consecin: Probabilitatea de a nimeri o locaie ocupat este k/n Probabilitatea de a nimeri o locaie liber la prima ncercare este 1k/n. Aceasta este de asemenea i probabilitatea notat cu p1 ca s fie necesar
[7.3.4.a]

------------------------------------------------------------

Probabilitatea p2 ca s fie necesar exact o a doua ncercare pentru a gsi o intrare


liber n tabel este egal cu produsul dintre: (1) Probabilitatea apariiei unei coliziuni n prima ncercare (2) Probabilitatea de a nimeri o locaie liber n a doua ncercare.

n manier similar se poate calcula probabilitatea p3 de a gsi o intrare liber sau

cheia cutat dup 3 ncercri i prin generalizare probabilitatea pi valabil pentru i ncercri [7.3.4.b]. -----------------------------------------------------------p2 = k nk n n 1

p3 =

k k 1 n k n n 1 n 2

...

[7.3.4.b]

k k 1 k 2 k (i 2) n k pi = n n 1 n 2 n (i 2) n (i 1) ----------------------------------------------------------- Prin urmare, numrul total de ncercri ateptat a fi necesar pentru inseria celei dea k chei este o sum de termeni de forma i*pi pentru i = 1,...,k , unde: i reprezint numrul de ncercri, pi probabilitatea ca s fie necesare exact i ncercri [7.3.4.c]. -----------------------------------------------------------E k = i pi = 1
i =1 k

nk k nk + 2 ++ n n n 1

k k 1 k 2 k (k 2) nk n +1 +k = n n 1 n 2 n (k 2) n (k 1) n k + 2 ------------------------------------------------------------

[7.3.4.c]

Numrul de ncercri necesar pentru a insera un element este identic cu numrul de


ncercri necesar pentru a-l gsi, Ca atare, valoarea lui Ek care reprezint numrul total de ncercri necesar pentru a insera cea de-a k cheie n tabel sau pentru a extrage o cheie din tabel, Poate fi utilizat pentru calculul numrului mediu de ncercri E , necesar realizrii accesului la o cheie oarecare din tabelul dispersat care conine m elemente [7.3.4.d]. ------------------------------------------------------------

1 m n +1 m 1 n +1 [7.3.4.d] Ek = = (H n +1 H n m +1 ) m k =1 m k =1 n k + 2 m -----------------------------------------------------------E=

n formula de mai sus Hn este suma seriei armonice care poate fi aproximat prin
relaia [7.3.4.e] n care este constanta lui Euler[Wi74]. -----------------------------------------------------------1 1 1 H n = 1 + + + + ln (n) + [7.3.4.e] 2 3 n ----------------------------------------------------------- Dac se face substituia = m /(n+1) se obine relaia [7.3.4.f]. -----------------------------------------------------------1 1 n +1 1 E = [ln (n + 1) ln (n m + 1)]= ln = ln (1 ) n +1 m

[7.3.4.f]

----------------------------------------------------------- aproximeaz raportul dintre locaiile ocupate i cele disponibile n cadrul tabelei dispersate. El se mai numete i factor de umplere, = 0 preciznd tabela vid, iar = n /(n+1), tabela plin.

Numrul probabil de ncercri necesar gsirii sau inseriei unei chei k funcie de
factorul de umplere , apare tabelat n figura 7.3.4.a. 0.10 0.25 0.50 0.75 0.90 0.95 0.99 E() 1.05 1.15 1.39 1.85 2.56 3.15 4.66

Nr. crt. 1 2 3 4 5 6 7

Nr. crt. 1 2 3 4 5 6

0.1 0.25 0.5 0.75 0.9 0.95

E() 1.06 1.17 1.50 2.50 9.90 10.50

Fig.7.3.4.a. Tabelarea numrului probabil de ncercri funcie de factorul de umplere al tabelei (cazul ideal)

Fig.7.3.4.b. Tabelarea relaiei experimentale E() funcie de factorul de umplere

Rezultatele numerice sunt surprinztoare, ele nu depind de dimensiunea tabelei i n


acelai timp explic comportarea excepional a acestei metode. ncercri pentru a localiza o cheie sau a gsi un loc liber n ea.

Chiar dac tabela este plin n proporie de 90 %, n medie sunt necesare numai 2.56 Rezultatele prezentate presupun ns cazul ideal, respectiv o metod de tratare a
coliziunilor care repartizeaz n mod perfect uniform cheile pe mulimea locaiilor rmase libere.

n realitate, acest lucru este practic irealizabil. Analiza experimental a comportamentului real a dispersiei nchise bazate pe
adresarea deschis liniar a condus la relaia experimental [7.3.4.g] ------------------------------------------------------------

E=

1 / 2 1

[7.3.4.g]

----------------------------------------------------------- Reprezentarea grafic ale valorilor sale numerice funcie de factorul de umplere apar reprezentate n figura 7.3.4.b [Wi74].

Rezultatele obinute chiar pentru metoda cea mai puin elaborat de tratare a coliziunilor

(adresarea deschis liniar) sunt att de bune nct ar putea exista tentaia de a considera metoda dispersiei drept soluie pentru orice situaie. necesare pentru gsire sau inserie, sunt superioare celor mai sofisticate metode bazate pe structuri de arbori.

Performanele metodei, cel puin din punctul de vedere al numrului de comparaii

Desigur, metoda are i o serie de dezavantaje. Primul i cel mai important, este acela c, dimensiunea tabelei fiind fix aceasta nu
poate fi ajustat conform cererii curente. O estimare a priori a dimensiunii sale maxime este de regul greu de realizat i conduce fie la o slab utilizare a memoriei fie la performane sczute prin depirea tabelei dispersate. Chiar dac numrul de elemente este cunoscut exact lucru care se ntmpl foarte rar n vederea obinerii unei bune performane, dimensiunea tabelei trebuie aleas cam cu 10 % mai mare.

O a doua deficien major se refer la suprimarea cheilor. Aceasta se realizeaz ntr-o manier laborioas i complex mai puin n cazul
metodei nlnuirii care utilizeaz un spaiu de memorie suplimentar pentru nlnuirea direct, elemente care au fost deja abordate n cadrul paragrafului de fa.

n concluzie, utilizarea tehnicii dispersiei nu este recomandabil dac: Volumul de date este cunoscut cu mic probabilitate, Volumul de date este puternic variabil Volumul de date crete n timp.
7.3.5. Exemplu. Construcia unui tablou de referine ncruciate

n cadrul acestui paragraf se prezint un program pentru construirea unei aa numite

tabele de referine ncruciate ("cross reference index"), asociat unui text dat. Programul citete un text ca intrare i l afieaz adugnd la nceputul fiecrui rnd numrul curent al rndului (n = 1, 2, 3, ...). n acelai timp, programul selecteaz i memoreaz toate cuvintele textului precum i numerele rndurilor n care acestea au fost ntlnite. La terminarea parcurgerii textului, programul poate furniza o tabel coninnd toate cuvintele ordonate alfabetic i lista liniilor n care apare fiecare cuvnt (tabela de referine ncruciate). ptratic.

n implementare se utilizeaz tehnica dispersiei nchise bazat pe adresare deschis

Un element al tabelei dispersate (TipCuvant) este un articol care pe lng cmpul


cheie care memoreaz cuvntul ca atare, are asociat o list nlnuit a numerelor liniilor n care este depistat cuvntul respectiv n procesul de baleere.

Aceast list este precizat de variabilele inceput i sfarsit de tip referin. De asemenea odat cu introducerea lor n tablela dispersat, elementele sunt nlnuite
ntr-o list liniar simpl prin intermediul cmpului leg, list care va fi utilizat ulterior n procesul de traversare ordonat a tabelei.

Programul integral asociat apare n secvena [7.3.5.a]. Metoda de tratare a coliziunilor este metoda adresrii ptratice. Cmpul leg nregistreaz indici referitori la tabela T prin intermediul crora
elementele efectiv utilizate ale tabelei sunt nlnuite ntr-o list special utilizat ulterior la afiare. tabela T, are dezavantajul c elementele depuse nu sunt ordonate alfabetic.

Metoda dispersiei conform creia se construiete i se exploateaz cu mare eficien Aceast ordonare se realizeaz artificial, cu eficien mai redus, n momentul listrii
tabelei de referine ncruciate cu ajutorul listei nlnuite mai sus amintite. -----------------------------------------------------------program refincrucis; uses Strings; const C1 = 10; {Lungime maxima cuvnt} [7.3.5.a] C2 = 8; {Numere pe linii} C4 = 500; {Numr maxim de linii} P = 997; {Lungime tablou dispersie} LIBER = ' ' ; type TipIndice = 0..P; RefNumRand = ^NumRand; TipCuvant = record {Element tabela de dispersie} cheie: String; inceput,sfarsit: RefNumRand; leg: TipIndice; end; NumRand = record {Nod lista numere linii} nr: 0..C4; urm: RefNumRand; end; TabelaDispersie = array [0..P] of TipCuvant; var I,Init: TipIndice; Litere,Cifre: set of char; K,K1: integer; N: integer; {Numr curent al rndului} A: String; {Tampon pentru cuvntul curent} Car: char; {Caracterul curent} T: TabelaDispersie; Depasire: boolean; NumeIntrare: String; Intrare: Text;

function H(Id: String): indice; var I,S: Integer; begin S:= 0; for I:= 1 to length(Id) do S:= S + ord(Id[I]) * trunc(exp(I * ln(2.0))); H:= S mod P; end;{H} procedure Cauta; var L,R: Integer; X: RefNumRand; F: boolean; begin L:= H(A); F:= false; Depasire:= false; R:= L; new(X); X^.nr:= N; X^.urm:= NIL; repeat if T[L].cheie = A then {s-a gsit} begin F:= true; T[L].sfarsit^.urm:= X; T[L].sfarsit:= X; end else if T[L].cheie = LIBER then {se creeaz un nou element} begin F:= true; with T[L] do begin cheie:= A; inceput:= X; sfarsit:= X; leg:= Init; end; Init:= L; end else begin {coliziune} L:= L + R; R:= R + 2; if L >= P then L:= L - P; if (R = P) then begin writeln ('Depasire tabel'); Depasire:= true; end end; until F or Depasire; end;{Cauta} procedure Imprimare; var I, J, M: Indice; procedure ImprCuvant(R:TipCuvant); var L: Integer; X: RefNumRand; begin write (' ', R.cheie); X:= R.inceput; L:= 0;

repeat write(' '); if (L=C2) then begin writeln; for L:= 1 to C1+1 do write(' '); L:= 0 end; L:= L + 1; write (X^.nr); X:= X^.urm; until X=nil; writeln; end;{ImprCuvant} begin {Imprimare} I:= Init; while I<>P do {parcurge T i caut elementul cu cheia minim} begin M:= I; J:= T[I].leg; while J<>P do begin if T[J].cheie<T[M].cheie then M:= J; J:= T[J].leg; end; {s-a gsit elementul cu cheia minim} ImprCuvant(T[M]); if M<>I then begin T[M].cheie:= T[I].cheie; T[M].inceput := T[I].inceput; T[M].sfarsit := T[I].sfarsit end; I:= T[I].leg; end end;{Imprimare} begin {Program principal}; litere:= ['A'..'Z', '_']; cifre:= ['0'..'9']; write('Nume fisier'); readln(NumeIntrare); assign (Intrare, NumeIntrare); reset(Intrare); N:= 0; K1:= C1; Init:= P; Depasire:= false; read(Intrare, Car); Car:= UpCase(Car); {rezolva caracterele 'a'..'z'} for I:= 0 to P-1 do T[I].Cheie := LIBER; while not eof(Intrare) or Depasire do begin if N=C4 then N:= 0; N:= N + 1; write(N, ' '); while not (Car=chr(13)) or Depasire do begin if Car in Litere then begin K:= 0; A:= ''; repeat if K<C1 then

begin K:= K + 1; A:= A + car; end; write(Car); read(Intrare, Car); Car:=UpCase(Car); until not (Car in Litere) or (Car in Cifre); if (K>=K1) then K1 := K else repeat A[K1]:= ' '; K1:= k1 - 1; until K1=K; cauta end else begin if Car='''' then repeat write(Car); read(Intrare, Car); Car:= UpCase(Car); until Car='''' else if Car='[' then repeat write(Car); read(Intrare, Car); Car:= UpCase(Car); until Car=']'; write(Car); read(Intrare, Car); Car:= UpCase(Car); end{else} end;{while} if (Car = chr(13)) then read(Intrare, Car); {cerut de DOS} writeln; read(Intrare, Car); Car:= UpCase(Car); end;{while} Imprimare end. -------------------------------------------------------

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