Lucrare de licent a [605833]
Universitatea \Alexandru Ioan Cuza" din Ias i
Facultatea de Matematic a
Analiza Algoritmilor de
Sortare
Lucrare de licent a
Conduc ator stiint ic:
Lect. dr. Ana – Maria Mos neagu
Candidat: [anonimizat], 2019
Ia si
Cuprins
Introducere 2
1 Analiza algoritmilor de sortare elementari 4
1.1 Algoritmi de sortare bazat i pe compararea elementelor . . . . . . . . 4
1.1.1 Sortarea prin Select ie (Select sort) . . . . . . . . . . . . . . . 4
1.1.2 Sortarea prin Insert ie (Insert sort ) . . . . . . . . . . . . . . . 8
1.1.3 Sortarea prin Interschimbare . . . . . . . . . . . . . . . . . . 15
1.2 Algoritmi de sortare care nu folosesc compararea ^ ntre elemente . . . 22
1.2.1 Sortarea prin num arare(Counting sort) . . . . . . . . . . . . . 22
1.2.2 Radix sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2 Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 27
2.1 Sortarea prin interclasare (Mergesort) . . . . . . . . . . . . . . . . . 28
2.2 Sortarea rapid a (Quicksort) . . . . . . . . . . . . . . . . . . . . . . . 32
3 Aplicat ii practice 39
Bibliograe 40
1
Introducere
Aceast a lucrare urm are ste analiza din punct de vedere teoretic, c^ at si din punct
de vedere practic a unei categorie de algoritmi de sortare, ^ n diferite situat ii.
Elementele unei liste sunt aranjate ^ ntr-o anumit a ordine cu ajutorul unui algo-
ritm de sortare.
Ace sti algoritmi sunt folosit i pentru a sorta at^ at liste de tip numeric, c^ at si liste
de tip alfabetic. ^Inc a de la ^ nceputurile informaticii, algoritmii de sortare au atras
un num ar mare de studii ^ n domeniu.
De si ideea de baz a a lor este aparent simpl a si oarecum familiar a, complexitatea
rezolv arii eciente a sort arii este mare.
Existent a unor algoritmi de sortare ecient i este esent ial a pentru utilizarea op-
tim a a altor algoritmi, cum ar cei de c autare, sau cei de interclasare.
Pentru a considera un algoritm ca ind de sortare, datele acestuia de ie sire
trebuie s a ^ ndeplineasc a dou a condit ii:
ordinea elementelor din datele de ie sire trebuie s a e ori cresc atoare, ori des-
cresc atoare
datele de ie sire trebuie s a e o rearanjare/permutare a celor de intrare, nu o
alterare a acestora.
^In continuare, vom prezenta unele dintre cele mai cunoscute metode de sortare.
Metodele de sortare cele mai frecvent folosite se pot clasica ^ n dou a marii cate-
gorii: metode directe si metode avansate.
IMetodele directe se bazeaz a pe algoritmi de dicultate redus a, u sor de g asit
si de ^ nt eles. ^In aceast a categorie se reg ase ste:
Analiza algoritmilor de sortare elementari care se clasic a ^ n:
{Algoritmi de sortare bazat i pe compararea elementelor si este alc atuit
din:
(a) Sortarea prin select ie
(b) Sortarea prin insert ie
2
Cuprins 3
(c) Sortarea prin interschimbare
(d) Shaker sort
{Algoritmi de sortare care nu folosesc compararea ^ ntre elemente si
este alc atuit din:
(a) Sortarea prin num arare (Counting sort)
(b) Radix Sort
IIMetodele avansate se bazeaz a pe algoritmi put in mai complicat i, dar care
nu necesit a cuno stint e avansate de algoritmic a. Aici se reg ase ste:
Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera care
cuprinde:
(a) Sortarea MergeSort
(b) Sortarea QuickSort
Orice programator trebuie s a cunoasc a metodele de sortare si s a aleag a folosirea
unei metode, ^ n funct ie de criteriul de ecient a urm arit. Ecient a metodelor de
sortare, dup a cum am precizat mai sus pentru algoritmi ^ n general, se m asoar a dup a
tipul de execut ie si memoria folosit a.
Capitolul 1
Analiza algoritmilor de sortare
elementari
1.1 Algoritmi de sortare bazat i pe compararea elemen-
telor
^In acest capitol vom presupune o secvent a nit a format a din nelemente a0; a1; :::;
an 1, dintr-o mult ime total ordonat a. A sorta aceast a secvent a ^ nseamn a a rea-
ranja componentele acesteia ^ ntr-o anumit a ordine, premut^ andu-le. Presupunem c a
secvent a dat a este stocat a ^ ntr-un vector (tablou unidimensional). Formal, problema
sort arii ()cresc atoare) poate denit a astfel:
Input: O secvent a de n numere [ a0; a1; :::; a n 1].
Output: O permutarea a secvent ei de intrare, [ a00; a01: : : ; a0n 1], cua00a01
: : :a0n 1.
Vom introduce ^ n acest capitol c^ at iva algoritmi elementari de sortare, bazat i pe
compararea elementelor secvent ei de sortat care au avantajul c a sunt simpli, dar, ^ n
general, au performant e sc azute pentru tablouri de dimensiune mare :
I Sortarea prin Select ie
II Sortarea prin Insert ie
III Sortarea prin Interschimbare.
Vom verica corectitudinea pentru ecare algoritm descris si ^ i vom analiza comple-
xitatea.
1.1.1 Sortarea prin Select ie (Select sort)
Metoda se bazeaz a pe selectarea, la ecare pas al algoritmului, a unui anumit
element din secvent a si apoi plasarea sa pe pozit ia corect a a sirului. Algoritmii de
4
Capitolul 1. Analiza algoritmilor de sortare elementari 5
sortare ce au la baz a aceast a metod a diferit a ^ n funct ie de criteriul de select ie a
elementului: minim si maxim din secvent a corespunz atoare.
Sortarea prin select ie a elementului minim
Prima dat a, se determin a cel mai mic element al tabloului. ^In acest sens, se
determin a indicele mastfel ^ nc^ at a[i]> a[m];8i= 0;1; : : : ; n 1; i6=m:^In acest
caz, cel mai mic element, a[m], este plasat pe prima pozit ie ^ n sirul sortat prin
interschimbarea elementului a[0] cu a[m]. Se repet a aceea si operat ie pentru sub sirul
r amas de sortat la ecare pas p^ an a c^ and ram^ ane doar un singur element sau sirul
este aranjat cresc ator.
Algoritmul de sortare prin select ia elementului minim implementat ^ n C++ este
urm atorul:
1# include <iostream >
2using namespace std;
3void sort (int a[], int n) {
4 int i, min , m, j, temp ;
5 for (i = 0; i < n -1; i++)
6 {
7 min = a[i];
8 m = i;
9 for (j = i + 1; j < n; j++)
10 if (a[j]< min)
11 {
12 min = a[j];
13 m = j;
14 }
15 if(a[i ]!=a[m])
16 {
17 temp = a[i];
18 a[i] = a[m];
19 a[m] = temp ;
20 }
21 }
22}
23int main ()
24{
25 int a[20] , n, i;
26 cout << " Introduceti dimensiunea vectorului : n=";
Capitolul 1. Analiza algoritmilor de sortare elementari 6
27 cin >> n;
28 cout << " Introduceti elementele vectorului :" << endl ;
29 for (i = 0; i < n; i++)
30 {
31 cout << "a[" << i << "]=";
32 cin >> a[i];
33 }
34 sort (a, n);
35 cout << " Dupa sortarea prin selectia minimului ,
vectorul este : ";
36 for (i = 0; i < n; i++)
37 cout << a[i] << " ";
38 return 0;
39}
De exemplu, consider am urm atorul sir pe care ne propunem s a-l sort am cresc ator
folosind metoda prezentat a:
10, 91, 2, 6, 11, 56, 23, 15, 19, 0.
Pas 1: Se determin a indicele elementului minim, m= 9 si se interschimb a
elementele a[0] si a[9].
S irul devine: 0, 91, 2, 6, 11, 56, 23, 15, 19, 10.
Pas 2: Se determin a indicele elementului minim din sub sirul a[1::9],m= 2 si se
interschimb a elementele a[1] si a[2].
S irul este acum: 0, 2, 91, 6, 11, 56, 23, 15, 19, 10.
Pas 3: Se determin a indicele elementului minim din sub sirul a[2::9],m= 3 si se
interschimb a elementele a[2] si a[3].
S irul este acum: 0, 2, 6, 91, 11, 56, 23, 15, 19, 10.
Pas 4: Se determin a indicele elementului minim din sub sirul a[3::9],m= 9 si se
interschimb a elementele a[3] si a[9].
S irul este acum: 0, 2, 6, 10, 11, 56, 23, 15, 19, 91.
Pas 5: Se determin a indicele elementului minim din sub sirul a[4::9],m= 4, dar
nu se realizeaz a nicio interschimbare deoarece a[4] coincide cu minimul sub sirului.
Pas 6: Se determin a indicele elementului minim din sub sirul a[5::9],m= 7 si se
interschimb a elementele a[5] si a[7].
S irul este acum: 0, 2, 6, 10, 11, 15, 23, 56, 19, 91.
Pas 7: Se determin a indicele elementului minim din sub sirul a[6::9],m= 8 si se
interschimb a elementele a[6] si a[8].
S irul este acum: 0, 2, 6, 10, 11, 15, 19, 56, 23, 91.
Capitolul 1. Analiza algoritmilor de sortare elementari 7
Pas 8: Se determin a indicele elementului minim din sub sirul a[7::9],m= 8 si se
interschimb a elementele a[7] si a[8].
S irul este acum: 0, 2, 6, 10, 11, 15, 19, 23, 56, 91.
Pas 9: Se determin a indicele elementului minim din sub sirul a[8::9],m= 8 si nu
se realizeaz a nicio interschimbare deaorece a[8] coincide cu minimul sub sirului.
Algoritmul se opre ste, sirul ind complet sortat.
Vericarea corectitudinii: Consider am c a precondit ia si postcondit ia sunt:
Pin=fn2Ng siPout=fa[0]a[1]: : :a[n 1]g. S a ar at am c a
proprietatea
I1 =fa[0]a[1]: : :a[i 1] sia[i 1]a[j];8j=i; : : : ; n 1g
este invariant a pentru ciclul fordup a i. Init ial, i= 0, deci I1 este satisf acut a la
intrare ^ n structura repetitiv a deoarece sirul sortat este vid. Pentru ecare i, ^ n
ciclul fordup a j, se caut a minimul elementelor a[i]; a[i+ 1]; : : : ; a [n 1], care apoi
se interschimb a cu elementul a[i]. Un invariant pentru ciclul interior este deci
I2 =fa[m]a[k]; k= 1; : : : ; j 1g;
adev arat la intrarea ^ n bucl a, r amas adev arat pe parcursul iterat iei, iar la ie sirea din
bucl a, I2 =fa[m]a[k]; k=i; : : : ; n 1g:A sadar,
a[0]a[1]: : :a[i] sia[i]a[j];8j=i+ 1; : : : ; n 1:
Cum ieste incrementat la sf^ ar situl ec arei iterat ii, reg asim proprietatea invariant a.
La ie sirea din bucla fordup a i, invariantul conduce la postcondit ie, pentru c a
i=n 1 sia[0]a[1]: : :a[n 2] sia[n 2]a[n 1]:
Pentru ecare dintre cele dou a cicluri for, variabila contor de iterat ie este in-
crementat a la ecare iterat ie, deci pot identicate funct ii de terminare cores-
punz atoare. Pentru ciclul exterior, consider am funct ia de terminare t(k) =n 1 ik,
iar pentru ciclul interior, t(k) =n jk. Deci, dup a un num ar nit de iterat ii condit iile
de contiunuare nu vor mai ^ ndeplinite, execut ia algoritmului ^ ncheindu-se. Prin
urmare, algoritmul se va termina ^ ntr-un num ar nit de pa si, demonstr^ andu-se astfel
total corectitudinea algoritmului.
Analiza complexit at ii: Not am cu TC(n) num arul de comparat ii, iar cu TM(n)
num arul de mut ari efectuate pentru a sorta cresc ator tabloul dat si cu T(n) timpul de
Capitolul 1. Analiza algoritmilor de sortare elementari 8
execut ie al algoritmului ce depinde de dimensiunea problemei(num arul de elemente
ale tabloului) si de caracteristicile datelor de intrare (distribut ia init ial a a elementelor
^ n secvent a). Num arul de comparat ii nu depinde de distribut ia init ial a a elementelor,
ind egal cu
TC(n) = (n 1) + ( n 2) ++ 1 =n(n 1)
2:
^In schimb, num arul mut arilor depinde de repartizarea init ial a a elementelor ^ n
secvent a, ca atare vom analiza cazurile extreme. ^In cazul cel mai favorabil, num arul
interschimb arilor este 0, deci TM(n) = 0 :^In cazul cel mai defavorabil, la ecare
iterat ie ise efectueaz a o interschimbare, deci trei mut ari si TM(n) = 3( n 1).
A sadar,
0TM(n)3(n 1):
Cum T(n) =TC(n) +TM(n), obt inem c a
n(n 1)
2T(n)(n 1)(n+ 6)
2;
si deci T(n)2(n2), deoarece T(n)2
(n2) siT(n)2O(n2).
Sortarea prin select ie a elementului maxim
Aceast a metoda este similar a cu sortarea prin select ie elementului minim, doar
c a acum vom determina elementul maxim al tabloului, deci indicele M pentru care
a[i]< a[M],8i= 0;1; :::; n 1; i6=M. Apoi sunt interschimbate elementele a[n 1]
sia[M] (elementul maxim este plasat pe ultima pozit ie ^ n sir). Procedeul se repet a
pentru sub sirul format din elementele a[0]; a[1]; :::; a [n 2]. Se caut a elementul cel
mai mare din acest sub sir si se interschimb a cu a[n 2] s.a.m.d., p^ an a c^ and sub sirul
va cont ine un singur element.
Vericarea corectitudinii algoritmului prin select ia maximului se realizeaz a asem a-
n ator cu cel al algoritmului prin select ia minimului.
1.1.2 Sortarea prin Insert ie (Insert sort )
Sortarea prin insert ie direct a
Aceast a metod a de sortare const a ^ n identicarea pozit iei pentru ecare ele-
ment a[i]; i1, ^ n sub sirul a[0: : : i 1], deja ordonat. C autarea pozit iei lui a[i]
se realizeaz a folosind algoritmul de c autare secvent ial a. Astfel, a[i] se compar a cu
a[i 1]; a[i 2]; : : : , p^ an a c^ and se g ase ste un element a[j], ^ n sub sirul deja ordonat,
astfel ^ nc^ at a[j]6a[i]. Se insereaz a elementul a[i] dup a a[j], deplas^ andu-se ^ nt^ ai
elementele a[i 1]; a[i 2]; : : : ; a [j+ 1] cu o pozit ie la dreapta.
Capitolul 1. Analiza algoritmilor de sortare elementari 9
Algoritmul de sortare prin insert ie direct a implementat in C++ este urm atorul:
1 # include <iostream >
2 using namespace std;
3 void insert ( int a[], int n)
4 {
5 int aux ,j;
6 for (int i=0; i < n;i++)
7 {
8 j = i -1;
9 aux=a[i];
10 while (j >=0 && aux < a[j])
11 {
12 a[j +1]= a[j];
13 j –;
14 }
15 a[j +1]= aux;
16 }
17 }
18 int main ()
19 {
20 int a[100] , n, i;
21 cout << " Introduceti dimensiunea vectorului , n= ";
22 cin >> n;
23 cout << " Introduceti vectorul :" << endl ;
24 for (int i = 0; i < n; i++)
25 {
26 cout << "a[" << i << "]= ";
27 cin >> a[i];
28 }
29 insert (a, n);
30 cout << " Vectorul sortat este : ";
31 for (i = 0; i < n; i++)
32 cout << a[i] << " ";
33 return 0;
34 }
Presupunem c a vrem s a sort am sirul:
20, 7, 3, 5, 6, 2, 11, 10.
Lista de elemente sortate se va construi^ n partea din st^ anga a tabloului init ial. La
Capitolul 1. Analiza algoritmilor de sortare elementari 10
^ nceputul problemei suntem ^ n cazul ^ n care lista de elemente este vid a. Algoritmul
va alege c^ ate un element din sirul init ial si ^ l va insera ^ n lista de elemente sortate.
Elementele din sirul init ial se aleg ^ n ordinea ^ n care apar ele ^ n sir. ^In exemplul
nostru le vom alege ^ n ordinea 20, 7, 3, 5, etc.
Pasul 1. Vom ^ ncepe cu elementul a[0] = 20. Cum lista de elemente sortate este
vid a, insert ia lui va foarte simpl a. Vom obt ine:
20, 7, 3, 5, 6, 2, 11, 10.
Pasul 2. Apoi urmeaz a elementul a[1] = 7. Vrem s a ^ l inser am ^ n lista de
elemente sortate astfel ^ nc^ at ea s a r am^ an a sortat a. Asta ^ nseamn a c a trebuie s a ^ l
inser am ^ naintea lui 20. Pentru a folosi ^ n mod ecient spat iul de memorie ^ l vom
aduce pe 20 ^ n locul lui 7 si pe urm a ^ l vom pune pe 7 ^ naintea lui 20 prin intermediul
variablie aux. Vom obt ine:
7, 20, 3, 5, 6, 2, 11, 10.
Pasul 3. Apoi urmeaz a a[2] = 3. Pe 3 ar trebui s a ^ l inser am ^ naintea lui 7. Din
nou, pentru a folosi ecient spat iul de memorie, deplas am pe 7 si pe 20 spre dreapta
cu o pozit ie, si pe urm a plas am pe 3 ^ naintea lor. Vom obt ine:
3, 7, 20, 5, 6, 2, 11, 10.
Pasul 4. Urmeaz a elementul a[3] = 5. Pe 5 trebuie s a ^ l inser am ^ ntre 3 si 7.
Vom deplasa pe 7 si 20 spre dreapta cu o pozit ie. ^In felul acesta se va elibera o
pozit ie ^ ntre 3 si 7, loc unde ^ l vom plasa pe 5. Obt inem:
3, 5, 7, 20, 6, 2, 11, 10.
Procedeul continu a ^ n mod similar p^ an a c^ and si ultimul element din sirul original
este plasat pe pozit ia corect a ^ n sirul ordonat.
Putem vedea cum sirul ordonat se construie ste treptat peste sirul nesortat, ^ n
acela si spat iu de memorie ocupat de acesta.
Ideea de principiu este c a la pasul kavem deja k 1 numere sortate si vrem s a
plas am elementul a[k] pe pozit ia corect a ^ ntre aceste k 1 numere sortate. Deplas am
spre dreapta toate numerele care sunt mai mari dec^ at elementul k. Astfel se va
elibera un loc ^ n tablou, loc ^ n care vom plasa elementul a[k]. Cum toate numerele
de la dreapta lui sunt mai mari ca el si toate numerele de la st^ anga lui sunt mai mici
ca el, rezult a c a avem acum knumere sortate si putem trece la pasul k+ 14.
Vericarea corectitudinii: S a ar at am c a
I1 =fa[0]a[1]: : :a[i 1]g
este inavariant pentru ciclul fordup a i(elementele sirului sortat, a[0]; : : : ; a [i 1],
sunt elementele sirului original de la 0 la i 1, permutate). Init ial, i= 1, deci
precondit ia implic a faptul c a sirul format dintr-un singur element poate consi-
Capitolul 1. Analiza algoritmilor de sortare elementari 11
derat sortat cresc ator. S a ar at am c a proprietatea r am^ ane adev arat a prin execut ia
prelucr arilor din ciclul for.
Fie proprietatea
I2 =fa[0]a[1]: : :a[j] siauxa[j+ 1] = a[j+ 2]: : :a[i]g:
Vom ar ata c a aceasta este o proprietate invariant a pentru structura repetitiv a while .
La intarea ^ n ciclu, j=i 1; aux =a[i] =a[j+1], iar sirul a[0]a[1]: : :a[i 1]
este deja ordonat, deci proprietatea este adev arat a la intrarea ^ n ciclu. Apoi, daca
aux < a [j], din a[j+ 1] = a[j] rezult a c a aux < a [j] =a[j+ 1]: : :a[i]. Dup a
execut ie prelucr arii j=j 1; a[0]a[1]: : :a[j] siaux < a [j+ 1] = a[j+ 2]
: : :a[i]. Deci, I2 este un invariant pentru ciclul while .
La sf^ ar situl buclei while este adev arat a una dintre relat iile:
a[0]: : :a[j]auxa[j+ 1] = a[j+ 2]: : :a[i];
dac a ciclul s-a ^ ncheiat deoarece auxa[j] sau
auxa[0] = a[1]: : :a[i];
dac a ciclul s-a ^ ncheiat deoarece j= 1. Dar, dup a atribuirea a[j+ 1] = aux; a [0]
: : :a[j]a[j+ 1]a[j+ 2]: : :a[i], iar prin incrementarea contorului i,
reg asim proprietatea invariant a. La ie sirea din bucla for; i =n, invariantul implic a
postcondit ia.
Justicarea termin arii ^ n timp nit a algoritmului este aceea si ca ^ n cazul algo-
ritmului de sortare prin select ia minimului, consider^ and funct ia de terminare pentru
ciclul exterior t(k) =n ik, iar pentru ciclul interior
t(k) =8
<
:jk+ 1;dac a aux < a [jk]
0;dac a auxa[jk]
Analiza complexit at ii: At^ at num arul comparat iilor, c^ at si num arul mut arilor
depinde de distribut ia init ial a a elementelor ^ n secvent a. ^In cazul cel mai favorabil,
pentru ecare i= 1;2; : : : ; n 1, are loc o singur a comparat ie si dou a mut ari ( aux=
a[i]; a[j+ 1] = a[i]). Lu^ and ^ n calcul si mut arile si comparat ile, se obt ine o margine
inferioar a pentru timpul de execut ie,
3(n 1)T(n):
Capitolul 1. Analiza algoritmilor de sortare elementari 12
^In cazul cel mai defavorabil, pentru ecare i= 1;2; : : : ; n 1, au loc icomparat ii si
i+ 2 mut ari. Obt inem o margine superioar a timpului de execut ie,
T(n)n 1X
i=1(2i+ 2) = ( n 1)(n+ 2):
Putem scrie
3(n 1)T(n)(n 1)(n+ 2):
DeciT(n)2
(n) siT(n)2O(n2):
Sortarea prin insert ie cu pas variabil (shellsort)
Metoda a fost propus a, ^ n anul 1959, de c atre Donald Shell si este cunoscut a sub
numele de shellsort .
Shellsort este o extensie simpl a a sort arii prin insert ie direct a si care, se poate
executa cu o vitez a mai mare, permit ^ and schimburi de elemente care sunt foarte
^ ndep artate. Ideea este de a rearanja un vector pe grupe si ecare grup a av^ and
elementele distant ate la un anumit pas h, numit increment. Acest lucru poart a
numele de h-sortare .
Ideea principal a a acestui tip de sortare este aceea de a sorta prin insert ie di-
rect a^ n mod repetat toate elementele la distant e xe: h1; h2; : : : ; h k= 1 si hi+1< hi.
Exist a mai multe modalit at i de a alege increment ii, de alegerea acestora depinz^ and
performant a algoritmului. Alegerea increment ilor se dovede ste a foarte impor-
tant a. Increment ii pot ale si dup a o putere a lui 2 sau putem folosi un tablou de
increment i cu valori descresc atoare, ultima dintre acestea ind obligatoriu 1. Pentru
aumite alegeri ale secvent ei de increment i se pot obt ine algoritmi de complexitate
mai mic a dec^ at cea a algoritmului de sortare prin insert ie direct a. Un astfel de sir
de increment i este
1;3;7;15;31;63;127;255;511; : : : ; (2k 1;k1);
pentru care s-a demonstrat c a, ^ n cazul cel mai defavorabil, T(n)2O(n3=2). D. E.
Knuth recomand a urm atorul sir de increment i, deoarece este u sor de implementat
(are aceea si complexitate ^ n cel mai defavorabil caz):
1;4;13;40;121;364;1093;3280; : : :
A sadar, dup a cum se observ a, cel de-al i-lea increment este egal cu de 3 ori in-
crementul al ( i 1) lea+1. Sunt multe alte secvent e de increment i care pot conduce
Capitolul 1. Analiza algoritmilor de sortare elementari 13
la algoritmi de sortare mai efcient i. De exemplu, Sedgewick propune secvent a
1;8;23;77;281; : : : ; (4k+1+ 32k+ 1;k0;la care se adaug a incrementul 1) :
Algoritmul corespunz ator are o performant a de O(n4=3), ^ n cazul cel mai defavo-
rabil. Anumite secvent e de increment i ar trebui evitate, av^ and performant e slabe.
Un exemplu de astfel de secvent a este
1;2;4;8;16;32;64;128;256; : : :(puterile lui 2) ;
deoarece elementele de pe pozit iile pare nu sunt comparate cu cele de pe pozit iile
impare p^ an a la pasul nal.
Algoritmul de sortare prin insert ie cu pas variabil (shellsort) implementat in
C++ este urm atorul:
1 # include <iostream >
2 using namespace std;
3 void shellSort ( double a[], int n)
4 {
5 int i, j, h;
6 double aux ;
7 h=1;
8 while (h< n/3)
9 {
10 h=3* h+1; // sirul de incrementi sugerat de Knuth
11 }
12 while (h >=1)
13 {
14 for(i=h;i < n -1;i++)
15 {
16 aux =a[j];
17 j=i;
18 while (j >=h && a[j-h] > aux)
19 {
20 a[j]=a[j-h];
21 j=j-h;
22 }
23 a[j]= aux;
24 }
25 h=h/3;
Capitolul 1. Analiza algoritmilor de sortare elementari 14
26 }
27 }
28 int main ()
29 {
30 double a [100];
31 int i, n;
32 cout << " Introduceti dimensiunea vectorului pe care
doriti sa -l sortati : ";
33 cin >> n;
34 for (i = 0; i < n; i++)
35 {
36 cout << " Introduceti elementul vectorului " <<i+1<<"
:";
37 cin >> a[i];
38 }
39 shellSort (a, n);
40 cout << " Vectorul sortat este : ";
41 for (i = 0; i < n; i++)
42 cout << a[i] << " ";
43 return 0;
44 }
De exemplu, consider am urm atorul sir:
11, 23, 17, 46, 35, 49, 8, 66, 20, 52, 6, 21, 44, 32, 57.
Vom folosi algoritmul de sortare prin mic sorarea incrementului folosind secvent a
de increment i 7, 4, 1.
Pas 1: Grupele de sortat, la distant a de 7 pa si sunt: f11, 66, 57g,f23, 20g,f17,
52g,f46, 6g,f35, 21g,f49, 44g,f8, 32g. Sortate prin inserare direct a, devin: f11,
57, 66g,f20, 23g,f17, 52g,f6, 46g,f21, 35g,f21, 35g,f44, 49g,f8, 32g.
Noul tablou este:
11, 20, 8, 6, 21, 32, 17, 35, 23, 44, 46, 57, 49, 52, 66.
Pas 2: Grupele de sortat, la distant a de 4 pa si sunt: f11, 21, 23, 49g,f20, 44,
52, 32g,f17, 8, 46, 66g,f6, 57, 35g. Grupele sortate prin insert ie direct a: f11, 21,
23, 49g,f20, 32, 44, 52g,f8, 17, 46, 66g,f6, 35, 57g.
Tabloul devine:
11, 20, 8, 6, 21, 32, 17, 35, 23, 44, 46, 57, 49, 52, 66.
Pas 3: Sortarea la distant a de 1 pas duce la sortarea ^ ntregului tablou, de fapt
ind vorba de sortarea prin insert ie direct a aplicat a ^ ntregului tablou.
Capitolul 1. Analiza algoritmilor de sortare elementari 15
Avantajul metodei const a ^ n faptul c a elementele sunt mutate mai repede mai
aproape de pozit iile lor corecte, sortarea realiz^ andu-se la distant e mai mari.
1.1.3 Sortarea prin Interschimbare
Bubblesort
Este una dintre cele mai simple metode de sortare,^ ns a inecient a pentru tablouri
de dimensiuni mari.
Interschimb arile se consider a pentru elementele vecine ale secvent ei de sortat ce
nu sunt ^ n ordinea corect a si au loc p^ an a c^ and elementele vectorului sunt complet
ordonate. Aceast a variant a de sortare se mai nume ste si metoda bulelor sau
BubbleSort .
Elementul principal al acestui tip de sortare const a ^ n compararea elementelor
a[i] sia[i+ 1]; dac a a[i]< a[i+ 1], atunci se trece la urm atoarea comparare, adic a
a[i+ 1] cu a[i+ 2], dac a nu, se interschimb a a[i] cua[i+ 1] si apoi se compar a
a[i+ 1] cu a[i+ 2]. Dup a prima parcurgere a vectorului, pe ultima pozit ie va
cu sigurant a elementul av^ and valoarea cea mai mare, dup a a doua parcurgere, pe
penultima pozit ie va ajunge urm atorul element ca valoare, etc. O singur a parcurgere
a vectorului nu este sucient a, ci trebuie continuat p^ an a c^ and nu se mai efectueaz a
interschimb ari.
Algoritmul de sortarea prin interschimbare(Bubble Sort) implementat in C++
este urm atorul:
1 # include <iostream >
2 using namespace std;
3 void bubble ( int a[], int n)
4 {
5 int i,j;
6 double aux ;
7 for (i=n -1; i >= 0;i –)
8 {
9 for(j=0;j <= i -1;j++)
10 {
11 if(a[j]>a[j +1])
12 {
13 aux=a[j];
14 a[j]=a[j +1];
15 a[j +1]= aux;
16 }
Capitolul 1. Analiza algoritmilor de sortare elementari 16
17 }
18 }
19 }
20 int main () {
21 int n,a[100] , i;
22 cout << " Introduceti dimensiunea vectorului : n=";
23 cin >> n;
24 cout << " Introduceti elementele vectorului :" << endl ;
25 for (i = 0; i < n; i++)
26 {
27 cout << "a[" << i << "]=";
28 cin >> a[i];
29 }
30 bubble (a, n);
31 cout << " Vectorul sortat este :";
32 for (i = 0; i < n; i++)
33 cout << a[i] << " ";
34 return 0;
35 }
Exemplu. S a consider am c a sirul de sortat este:
79, 40, 65, 81, 25, 38, 13.
Pasul 1:
79$40 65 81 25 38 13 (prin " $" am indicat o interschimbare)
40 79$65 81 25 38 13
40 65 79 81$25 38 13
40 65 79 25 81$38 13
40 65 79 25 38 81 $13
40 65 79 25 38 13 81
La nalul primului pas, cel mai mare element din sir se a
a pe ultima pozit ie
a sirului. Deci, acesta nu va mai face parte din subsecvent a ce va parcurs a ^ n
continuare.
Pasul 2:
40 65 79$25 38 13
40 65 25 79$38 13
40 65 25 38 79$13
40 65 25 38 13 79
Capitolul 1. Analiza algoritmilor de sortare elementari 17
La nalul acestui pas, cel mai mare element al sub sirului este pozit ionat acum
pe penultima pozit ie a sirului init ial. Prin urmare, acesta nu va mai face parte din
subsecvent a ce va parcurs a ^ n continuare.
Pasul 3:
40 65$25 38 13
40 25 65$38 13
40 25 38 65$13
40 25 38 13 65
La nalul celui de-al treilea pas, num arul 65 se a
a pe pozit ia corect a ^ n sirul
sortat.
Pasul 4:
40$25 38 13
25 40$38 13
25 38 40$13
25 38 13 40
Num arul 40 se a
a ^ n pozit ia corect a ^ n sirul sortat.
Pasul 5:
25 38$13
25 13 38
Elementul 38 este pe pozit ia sa nal a ^ n sirul sortat.
Pasul 6:
25$13
13 25
^In acest moment, am terminat procesul de sortare deoarece sirul este ordonat:
13;25;38;40;65;79;81:
Vericarea corectitudinii: Consider am proprietatea
I1 =fa[j]a[k]; k= 0; : : : ; jg:
S a ar at am c a aceast a proprietate este invariant a pentru ciclul interior fordup a j.
La intrarea ^ n ciclu, ( j= 0), proprietatea este adev arat a, pentru c a a[0]a[0].
Presupunem c a proprietatea este adev arat a la ^ nceputul iterat iei. Dac a a[i]> a[j+
1], atunci se realizeaz a interschimbarea elementelor si acestea vor ^ n ordinea a[j]<
a[j+ 1], iar dac a a[j]a[j+ 1], nu se efectueaz a interschimb ari, dar proprietatea
este adev arat a. A sadar, proprietatea I1 r am^ ane adev arat a la sf^ ar situl iterat iei.
Dup a incrementarea variabilei jreg asim proprietatea I1. Ceea ce se realizeaz a ^ n
ciclul interior este plasarea celui mai mare element din secvent a a[0]; a[1]; : : : ; a [i] pe
pozit ia i.
Capitolul 1. Analiza algoritmilor de sortare elementari 18
Fie proprietatea
I2 =fa[i+ 1]: : :a[n 1];cua[i+ 1]a[j]; j= 0; : : : ; ig:
Proprietatea este invariant a pentru ciclul exterior fordup a i: la intrarea ^ n ciclu,
i=n 1, deci proprietatea este adev arat a ( sir vid), apoi r am^ ane adev arat a ^ n bucl a
conform celor demonstrate anterior, iar la ie sirea din bucl a, i= 0, deci proprietatea
devine I2 =fa[1]: : :a[n 1];cua[1]a[0]g, ceea ce implic a postcondit ia.
Pentru a demonstra c a ambele cicluri sunt nite, consider am funct ia de terminare
pentru ciclul exterior, t(k) =ik, iar pentru ciclul interior, t(k) =ik jk.
Analiza complexit at ii: Num arul de comparat ii nu depinde de propriet at ile da-
telor de intrare, acesta ind
TC(n) =n 1X
i=1i 1X
j=01 =n 1X
i=1i=n(n 1)
2:
Num arul de interschimb ari depinde de repartizarea init ial a a elementelor ^ n sir, deci
vom analiza cazurile extreme. ^In cazul cel mai favorabil num arul de mut ari ale
elementelor ^ n vederea sort arii este TM(n) = 0, iar ^ n cazul cel mai defavorabil se
obt ine TM(n) =3n(n 1)
2:A sadar, 0TM(n)3n(n 1)
2:Cum T(n) =TC(n) +
TM(n))n(n 1)
2T(n)2n(n 1);deciT(n)2(n2).
Algoritmul poate ^ mbun at at it, implic^ and o variabil a boolean a care s a indice
dac a la iterat ia precedent a au existat interschimb ari. Scopul este de a reduce
num arul de comparat ii.
Algoritmul de sortare prin interschimbare (^ mbun at at it) implementat ^ n C++
este urm atorul:
1 # include <iostream >
2 using namespace std;
3 void bubble2 ( int a[], int n)
4 {
5 int i,m;
6 bool schimb ;
7 double aux ;
8 m= n;
9 do
10 {
11 schimb = false ;
12 for(i=0;j <= m -2;j++)
Capitolul 1. Analiza algoritmilor de sortare elementari 19
13 {
14 if(a[i]>a[i +1])
15 {
16 aux=a[i];
17 a[i]=a[i +1];
18 a[i +1]= aux;
19 schimb = true ;
20 }
21 }
22 m= m -1;
23 } while ( schimb ==0) ;
24 }
25 int main () {
26 int n,a[100] , i;
27 cout << " Introduceti dimensiunea vectorului : n=";
28 cin >> n;
29 cout << " Introduceti elementele vectorului :" << endl ;
30 for (i = 0; i < n; i++)
31 {
32 cout << "a[" << i << "]=";
33 cin >> a[i];
34 }
35 bubble2 (a, n);
36 cout << " Vectorul sortat este :";
37 for (i = 0; i < n; i++)
38 cout << a[i] << " ";
39 return 0;
40 }
^In acest caz, num arul de comparat ii ^ n cazul cel mai favorabil este TC(n) =n 1,
iar ^ n cazul cel mai defavorabil este la fel ca ^ n cazul general. ^In ceea ce prive ste
num arul de mut ari, se obt ine aceea si estimare ca ^ n cazul general. Deci, n 1
T(n)2n(n 1). Prin urmare, avem c a T(n)2
(n) siT(n)2O(n2).
Shaker sort
Modic^ and algoritmul de sortare prin interschimbarea elementelor vecine, sort am
elementele unui tablou, astfel ca la ecare pas s a se plaseze pe pozit iile nale c^ ate
dou a elemente: minimul, respectiv maximul din subtabloul parcurs la pasul respec-
Capitolul 1. Analiza algoritmilor de sortare elementari 20
tiv.
Algoritmul shaker sort implementat ^ n C++ este urm atorul:
1# include <iostream >
2using namespace std;
3void shaker (int a[], int n)
4{
5 bool swapped = true ;
6 int s= 0;
7 int e = n – 1;
8 while ( swapped )
9 {
10 swapped = false ;
11 for (int i = s; i < e; ++i)
12 {
13 if (a[i] > a[i + 1])
14 {
15 swap (a[i], a[i + 1]) ;
16 swapped = true ;
17 }
18 }
19 if (! swapped )
20 break ;
21 swapped = false ;
22 –e;
23 for (int i = e – 1; i >= s; –i)
24 {
25 if (a[i] > a[i + 1])
26 {
27 swap (a[i], a[i + 1]) ;
28 swapped = true ;
29 }
30 }
31 ++s;
32 }
33}
34int main ()
35{
36 int n, a[100] , i;
Capitolul 1. Analiza algoritmilor de sortare elementari 21
37 cout << " Introduceti dimensiunea vectorului : n=";
38 cin >> n;
39 cout << " Introduceti elementele vectorului :" << endl ;
40 for (i = 0; i < n; i++)
41 {
42 cout << "a[" << i << "]=";
43 cin >> a[i];
44 }
45 shaker (a, n);
46 cout << " Vectorul sortat este :";
47 for (i = 0; i < n; i++)
48 cout << a[i] << " ";
49 return 0;
50}
De exemplu:
Fie urm atorul sir: 6 ;2;5;3;9;1;3.
Pas 1:
6!2 5 3 9 1 3 (prin !indic am o interschimbare spre dreapta)
2 6!5 3 9 1 3
2 5 6!3 9 1 3
2 5 3 6!9 1 3
Observ am c a 6 este mai mic ca 9, deci mai departe interschimb am cu 9.
2 5 3 6 9!1 3
2 5 3 6 1 9!3
2 5 3 6 1 3 9
La nalul primului pas, cel mai mare element din sir se a
a pe ultima pozit ie.
Pas 2:
2 5 3 6 1 3 9 (prin indic am o interschimbare spre st^ anga)
2 5 3 6 1 3 9
2 5 3 1 6 3 9
2 5 1 3 6 3 9
2 1 3 6 3 9
1 2 5 3 6 3 9
La nalul celui de=al doilea pas, cel mai mic elemente din sir se a
a pe pozit ia
primului indice al sirului.
Pas 3:
1 2!5 3 6 3 9
1 2 5!3 6 3 9
Capitolul 1. Analiza algoritmilor de sortare elementari 22
1 2 3 5!6 3 9
1 2 3 5 6!3 9
1 2 3 5 3 6 9
La nalul acestui pas, num arul 6 se a
a pe pozit ia corect a ^ n sirul sortat.
Pas 4:
1 2 3 5 3 6 9
1 2 3 3 5 6 9
Acum, sirul este deja sortat, dar algoritmul nu stie dac a e nalizat. Deci, algo-
ritmul trebuie s a completeze aceast a trecere integral a, f ar a a interschimba.
1 2 3 3 5 6 9
1 2 3 3 5 6 9.
Debea acum putem spune c a am terminat procesul de sortare.
1.2 Algoritmi de sortare care nu folosesc compararea
^ ntre elemente
1.2.1 Sortarea prin num arare(Counting sort)
Fie un tablou a[0: : : n 1], de dimensiune n, cu elementele din mult imea f0;1;2;
: : : ; mg. Pentru sortarea unui astfel de tablou poate descris un algoritm de sor-
tare de complexitate liniar a, dac a mnu este semnicativ mai mare dec^ at n. Pa sii
algoritmului sunt:
i) se construiet e tabloul f[0: : : m ] al frecvent elor de aparit ie a valorilor ele-
mentelor tabloului a(fireprezint a de c^ ate ori apare valoarea i^ n tabloul
a; i= 0; : : : ; m );
ii) se calculeaz a tabloul frecvent elor cumulate, fc[0: : : m ]; fci=Pi
j=0fj; i=
0; : : : ; m ;
iii) se folose ste tabloul frecvent elor cumulate pentru a contrui tabloul ordonat b.
Counting sort implementat in C++ este urm atorul:
1# include < iostream >
2# include < algorithm >
3using namespace std;
4int getMax (int array [], int size )
5{
6 int max = array [1];
7 for (int i = 2; i <= size ; i++)
Capitolul 1. Analiza algoritmilor de sortare elementari 23
8 {
9 if ( array [i] > max )
10 max = array [i];
11 }
12 return max ;
13}
14void countSort (int *a, int n)
15{
16 int b [100];
17 int max = getMax (a, n);
18 int f [100];
19 for (int i = 0; i <= max; i++)
20 f[i] = 0;
21 for (int i = 1; i <= n; i++)
22 f[a[i ]]++;
23 for (int i = 1; i <= max; i++)
24 f[i] += f[i – 1];
25 for (int i = n; i >= 1; i –)
26 {
27 b[f[a[i]]] = a[i];
28 f[a[i]] -= 1;
29 }
30 for (int i = 1; i <= n; i++)
31 {
32 a[i] = b[i];
33 }
34}
35int main ()
36{
37 int n, i;
38 cout << " Introduceti dimensiunea vectorului : ";
39 cin >> n;
40 int a [100];
41 cout << " Introduceti elementele vectorului :\n";
42 for (i = 1; i <= n; i++)
43 {
44 cout << "a[" << i << "]=";
45 cin >> a[i];
Capitolul 1. Analiza algoritmilor de sortare elementari 24
46 }
47 countSort (a, n);
48 cout << " Vectorul sortat este : ";
49 for (i = 1; i <= n; i++)
50 cout << a[i] << " ";
51 return 0;
52}
De exemplu:
Fie sirul:
1 5 3 3 1 1 2 2 1 2 1 3 5 3
Pas 1. Analiz am frecvent a ec arei aparit ie a numerelor din sirul respectiv.
Num arul 0 nu apare niciodat a ^ n sir, 1 apare de cinci ori ^ n sir, 2 apare de trei
ori, 3 apare de patru ori, 4 nu apare nicodat a si 5 apare de dou a ori.
Pas 2. Se realizeaz a un sir cu frecvent ele analizate:
0 5 3 4 0 2
Algoritmul de sortare prin num arare are timpul de execut ie O(m+n). Dac a
m2O(n) atunci algoritmul are complexitate liniar a. Dac a meste mult mai mare
dec^ at n(de exemplu m2O(n2)), atunci ordinul de complexitate al algoritmului de
sortare este determinat de m.
1.2.2 Radix sort
Fie un tablou a[0: : : n 1] de dimensiune n, cu elemente numere naturale cu cel
mult kcifre.
Algoritmul este bazat pe urm atoarea idee: folosind counting sort, se ordoneaz a
tabloul ^ n raport cu cifra cea mai put in semnicativ a a ec arui num ar, apoi se
sorteaz a ^ n raport cu cifra de rang imediat superior s.a.m.d., p^ an a se ajunge la cifra
cea mai semnicativ a.
Dac a m= 10keste semnicativ mai mare dec^ at n, atunci sortarea prin num arare
nu este ecient a. Dar dac a keste mult mai mic dec^ at n, atunci se poate obt ine un
algoritm de complexitate O(kn).
Algoritmul de sortarea Radix Sort scris sub form a unui cod in C++ este urm atorul:
1# include <iostream >
2using namespace std;
3int getMax (int a[], int n)
4{
5 int max = a [0];
6 for (int i = 1; i < n; i++)
Capitolul 1. Analiza algoritmilor de sortare elementari 25
7 if (a[i] > max )
8 max = a[i];
9 return max ;
10}
11void countSort (int a[], int n, int exp )
12{
13 int b[100] , i, c [10] = { 0 };
14 for (i = 0; i < n; i++)
15 c[(a[i] / exp) % 10]++;
16 for (i = 1; i < 10; i++)
17 c[i] += c[i – 1];
18 for (i = n – 1; i >= 0; i –)
19 {
20 b[c[(a[i] / exp) % 10] – 1] = a[i];
21 c[(a[i] / exp) % 10] – -;
22 }
23 for (i = 0; i < n; i++)
24 a[i] = b[i];
25}
26void radixsort (int a[], int n)
27{
28 int exp , m;
29 m = getMax (a, n);
30 for (exp = 1; m / exp > 0; exp *= 10)
31 countSort (a, n, exp);
32}
33int main ()
34{
35 int n, i;
36 cout << " Introduceti dimensiunea vectorului : ";
37 cin >> n;
38 int a [100];
39 cout << " Introduceti elementele vectorului :";
40 for (i = 0; i < n; i++)
41 {
42 cout << "a[" << i << "]=";
43 cin >> a[i];
44 }
Capitolul 1. Analiza algoritmilor de sortare elementari 26
45 radixsort (a, n);
46 cout << " Vectorul sortat este : ";
47 for (i = 0; i < n; i++)
48 cout << a[i] << " ";
49 return 0;
50}
De exemplu:
Consider am urm atorul tablou:
180;55;85;90;902;34;2;76
Pas 1: ^Incepem s a sort am dup a cel mai mic num ar semncativ al unit at ilor
din tabloul. Obt inem urm atorul tabloul:
180;90;902;2;34;55;85;76.
Oberv am c a 180 apare ^ n fat a lui 90 pentru c a 180 a avut loc ^ naintea lui 90 ^ n
tablou, similar si pentru 902 cu 2 si 55 cu 85.
Pas 2: Apoi, sort am dup a num arul zecilor si obt inem:
902;2;34;55;76;180;85;90.
Observ am c a 902 revine din nou ^ naitea lui 2 pentru c a acesta este implementat
^ n fat a lui 2.
Pas 3: Apoi, sort am dup a num arul sutelor si obt inem:
2;34;55;76;85;90;180;902.
^In acest moment, am terminat procesul de sortare deoarece tabloul este ordonat.
Capitolul 2
Analiza algoritmilor de sortare
bazat i pe metoda
Divide-et-Impera
Divide-et-Impera este o una dintre cele mai cunoscute metode de proiectare a
algoritmilor. Aceast a strategie de proiectare presupune ^ mpart irea programul ^ n cel
put in dou a subprograme independente, ^ ns a acestea sunt asem an atoare cu problema
init ial a. Aceste mici probleme, de regul a sunt rezolvate ^ n mod recursiv si dup a se
regrupeaz a rezultatele pentru a crea o solut ie problemei init iale.
Pentru a rezolva o astfel de problem a trebuie parcur si trei pa si la nivel de recur-
sivitate:
^ mpart irea problemei init iale ^ n mai multe subprobleme, numit divide
rezolvarea subproblemelor ^ n mod recursiv, dar dac a acestea au dimensiuni
foarte mici, atunci cel mai bine e s a le rezolv am ^ n mod uzual. Acest pas
poart a denumirea de impera(conquer)
gruparea rezultatelor astfel ^ nc^ at s a obt iem solut ia la problema init ial a.
Exemplele specice acestei metode sunt algoritmii de parcurgere a arborilor bi-
nari si algoritmul de c autare binar a ^ n mult imi total ordonate. Deoarece descrierea
strategiei este bazat a pe recursivitate , trebuie pus accet pe o prelungire de tipul
problem a7!subproblem a unde dimensiunea devine o variabil a liber a.
Revenim la problema noastr a, adic a aceea a sort arii ^ n ordinea cresc atoare a
unei secvent e de nnumere reale. Ne propunem s a folosim tehnica diviz arii pentru a
elabora alt i algoritmi de sortare. Acet ia sunt: sortarea prin interclasare(Mergesort)
si sortarea rapid a(Quicksort).
27
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 28
Vom enunt a un rezultat important pentru analiza ecient ei algoritmilor
bazat i pe tehnica diviz arii , numit Teorema master . Presupunem c a o problem a
de dimensiune neste descompus a ^ n bsubprobleme de aceea si dimensiune, egal a cu
n=b. De asemenea, presupunem c a doar aprobleme dintre acestea necesit a s a e
rezolvate, ab(a sibsunt constante, a1,b >1). Consider am c a divizarea pro-
blemei init iale ^ n bsubprobleme de dimensiune n=b si compunerea solut iilor acestor
subprobleme au ^ mpreun a costul f(n), iar costul rezolv arii cazului elementar este o
constant a T0. Astfel, timpul de execut ie al algoritmului general satisface recurent a
T(n) =8
<
:T0;dac a nn0
aT(n=b) +f(n);dac a n > n 0:
^In mod evident, ordinul de cre stere al lui T(n) depinde de ordinul de cre stere a lui
f(n) si de valorile constatelor a sib:
Teorema 1. (Teorema master) Dac a f(n)2(nd); d0, atunci
T(n) =8
>>><
>>>:(nd);dac a a < bd
(ndlogn);dac a a=bd
(nlogba);dac a a > bd:
2.1 Sortarea prin interclasare (Mergesort)
Mergesort este un exemplu bun pentru aplicarea tehnicii Divide-et- Impera. Fie
un sir pe care ^ l imp art im ^ n dou a sub siruri de lungimi aproximativ egale. Cele dou a
siruri sunt ordonate recursiv folosind acelea si tehnici de sortare, adic a mergesort .
Apoi rezultatele sunt combinate (interclasate), obt in^ and astfel tabloul init ial sortat.
Prin interclasare se ^ ntelege parcurgerea celor dou a secvent e ordonate si extragerea
de elemente din acestea astfel ^ n^ at s a obt inem sirul init ial sortat. Aceste subsecvent e
sunt parcurse simultan cu dou a contoare si se compar a elementele curente. Cel mai
mic element dintre cele dou a este plasat ^ n sirul nal, iar contorul utlizat pentru
parcurgerea sub sirului din care a fost preluat elementul este incrementat. Procesul
continu a p^ an a c^ and una dintre secvent e a fost preluat ^ n ^ ntregime si elementele care
r am^ an ^ n cealalt a secvent a sunt plasate direct ^ n sirul nal.
Mergesort implementat in C++ este urm atorul:
1# include <iostream >
2using namespace std;
3
4void Merge (int a[], int l, int h, int m)
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 29
5{
6 int i, j, k, temp [50];
7 i = l;
8 k = 0;
9 j = m + 1;
10 while (i <= m && j <= h)
11 {
12 if (a[i] < a[j])
13 {
14 temp [k] = a[i];
15 k++;
16 i++;
17 }
18 else
19 {
20 temp [k] = a[j];
21 k++;
22 j++;
23 }
24 }
25 while (i <= m)
26 {
27 temp [k] = a[i];
28 k++;
29 i++;
30 }
31 while (j <= h)
32 {
33 temp [k] = a[j];
34 k++;
35 j++;
36 }
37 for (i = l; i <= h; i++)
38 {
39 a[i] = temp [i – l];
40 }
41}
42void MergeSort (int a[], int l, int h)
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 30
43{
44 int m;
45 if (l< h)
46 {
47 m = (l + h) / 2;
48 MergeSort (a, l, m);
49 MergeSort (a, m + 1, h);
50 Merge (a, l, h, m);
51 }
52}
53
54int main ()
55{
56 int n, i;
57 cout << " Introduceti dimensiunea vectorul pe care
doriti sa -l sortati : ";
58 cin >> n;
59 int a [100];
60 for (i = 0; i < n; i++)
61 {
62 cout << " Introduceti elementele vectorului " << i + 1
<< ": ";
63 cin >> a[i];
64 }
65 MergeSort (a, 0, n – 1);
66 cout << " Vectorul sortat este : ";
67 for (i = 0; i < n; i++)
68 cout << a[i]<<" ";
69 return 0;
70}
Un exemplu clar care ^ ndeplinet e condit iile acestui algoritm:
Fie vectorul: 9 ;8;10;4;7;5;18;17.
Pasul 1. Se ^ njum at at e ste vectorul, form^ andu-se dou a secvent e:(9, 8, 10, 4) si
(7, 5, 18, 17).
Pasul 2. ^In continuare proced am la fel pentru prima secvent a:(9,8) si (10, 4).
Pasul 3. Proced am la fel pentru prima subsecvent a din prima secvent a:(9), (8).
Pasul 4. Similar si pentru secvent a: (10, 4).
Pasul 5. Am obt inut vectori de dimensiune unu care se interclaseaz a:(9) cu (7)
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 31
si (10) cu (4). Deci, am obt inut secvent a: (3, 8, 9, 10).
Pasul 6. Se procedeaz a analog si pentru secvent a: (7, 5, 18, 17). S i obit inem:
(5, 7, 17, 18).
Pasul 7. Am obt iunut vectorul: 3 ;8;9;10;5;7;17;18.Cu ajutorul interclas arii se
obt ine vectorul sortat: 4 ;5;7;8;9;10;17;18.
Vericarea corectitudini: Pornim de la stabilirea precondit iilor si a postcondit ii-
lor:Pin=fa[stang : : : mijloc ] este cresc ator si a[mijloc +1: : : drept ] este cresc atorg,
iarPout=fa[stang : : : drept ] este cresc atorg. S a presupunem c a, la interat ia k,
indicii ^ n cele dou a p art i ale vectorului sunt asunti, respectiv j. Astfel, vom con-
sidera proprietatea invariant a I=ftemp [k]a[l]; l=i; : : : ; mijloc sitemp [k]
a[l]; l j; : : : ; dreptgpentru prima strunctur a while . Astfel spus, elementul pe care
tocmai l-am copiat ^ n temo [k] este cel mai mic dintre elementele r amase netransfe-
rate din cele dou a p art i ale vectorului 4 a. Acum toate elementele r amase ^ n avor
transferate ^ n temp [k+ 1]; : : : ; temp [drept stang ], pentru c a vectorul temp este
completat la st^ anga. Deci, la ^ ncheierea algoritmului merge , va vericat a condit ia
temp [k]temp [l];8k < l , ceea se ^ nseamn a c a, dup a copierea elementelor din temp
^ na, tabloul va corect sortat de la stang ladrept .
Pentru a demonstra c a invariantul este corect, ne reamintim c a, atunci c^ and
este apelat algoritmul de interclasare merge , cele dou a subsecvent e a lui a, de la
stang lamijloc si de la mijloc ladrept , sunt deja sortate. Deci, a[i]a[l]; l=
i+ 1; : : : ; mijloc sia[j]a[l]; l=j+ 1; : : : ; drept . A sadar, a[i] sia[j] sunt cele
mai mici elemente r amase netransferate. Dac a a[i]a[j], atunci temp [k] va primi
valoarea a[i] si deci va adev arat a relat ia a[i]a[l]; l=j; : : : ; drept . Asem anator
este si pentru cel alt caz. Aceast a metod a este stabil a, dar are necesit a un spat iu de
memorie suplimentar ^ n etapa de interclasare.
Apelul algorimului pentru un vector ade dimensiune nse va face prin mergesort
(a;0; n 1).
Demonstrat ia corectitudinii algoritmului recursiv se face prin induct ie: cazul
n= 1 este corect, pentru c a un singur vector este considerat deja sortat. Presupunem
c a algoritmul mergesort sorteaz a corect orice vector de lungime strict mai mic dec^ at
n. Presupunem c a algoritmul funct ioneaz a pentru un vector de dimensiune n. Astfel,
algoritmul mergesort se apeleaz a pentru doi subvectori de lugimen
2
sin n
2
.
Conform ipotezei inductive, aceste dou a subtablouri vor sortate corect si, cum
algoritmul de interclasare este corect, rezult a c a, dup a apelul acetuia, va rezulta un
tablou corect sortat.
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 32
Analiza complexit at ii: Ne focaliz am pe comparat iile si transferurile de elemente.
Consider am c a T(n) este num arul de prelucr ari efecutate de c atre algoritmul de
sortare prin interclasare si Tmerge (n) num arul de prelucr ari efectuate de algoritmul de
interclasare. Presupunem c a dimensiunea problemei este o putere a lui 2. Din punct
de vedere al comparat iilor si trasferurilor de elemente, sortarea prin interclasare
a unui element nu necisit a mult timp, adic a T(1) = 0. Pentru n > 1, num arul
maxim de operat ii de baz a efectuate ^ n algoritmul de interclasare este de ordinul
(n). Deci, Tmerge2(n) ^ n cel mai defavorabil caz. Avem urm atoarea relat ie de
recurent a privit a din punctul de vedere al comparat iilor si transferurilor de elemente:
T(n) =8
<
:2Tn
2
+ (n);dac a n >1
0;dac a n= 1;
pentru timpul de execut ie al algoritmului mergeSort , ^ n cel mai defavorabil caz.
Aplic am Teorema master ,a=b= 2; d= 1 si a=bd, rezult a c a T(n)2(nlogn),
^ n cel mai defavorabil caz. Deci, putem spune c a T(n)2(nlogn).
2.2 Sortarea rapid a (Quicksort)
A fost inventat de Hoare ^ n 1962 si se bazeaz a de asemenea pe tehnica Divide-
et-Impera, dar ^ n plus are la baz a si metoda interschimb arii. Spre deosebire de
mergesort , partea iterativ a a algoritmului este pentru a construi subcazuri, nu de
a combina solut iile lor. Este considerat unul dintre cei mai rapizi si mai utilizat i
algoritmi de sortare p^ an a ^ n acest moment.
Quicksort are o list a de sortat pe care o ^ mparte ^ n dou a subliste deoarece sunt
mai u sor de sortat. Pentru a rezlva o astfel de problem a trebuie sa urm arim urm atorii
pa si:
Alegerea unui element al listei pe care ^ l vom numi pivot si astfel am ^ mpart it
lista ^ n dou a subliste
Elementele mai mici dec^ at pivotul se reordoneaz a ^ n a sa fel ^ nc^ at s a e plasate
^ naintea pivotului, iar elementele mai mari s a e dup a pivot. F ac^ and aceast a
partit ie putem spune c a pivotul se a
a ^ n pozit ia sa nal a
Se sorteaz a cele dou a p at i prin tehnica recursiv a, p^ an a c^ and ^ n subliste r am^ ane
doar un singur element.
Astfel, putem spune acum c a lista init ial a a fost sortat a corect.
Quicksort partit ioneaz a lista init ial a. Fie un tablou a, partit ionarea ^ nseamn a
aranjarea elementelor vectorului init ial astfel ^ nc^ at elementele a
ate la st^ anga s a e
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 33
mai mici sau egale cu a[s] si elementele a
ate la dreapta de a[s] s a e mai mari
sau egale cu acesta. Deci, a[0]; : : : ; a [s 1]a[s] sia[s+ 1]; : : : ; a [n 1]a[s].
Elementul a[s] este pivotul. ^In contiunuare, cele dou a subtablouri sunt sortate
^ n mod independent prin apeluri recursive ale algoritmului. S a remarc am c a la
quicksort partit ionarea este prelucrarea cea mai important a a algoritmului, deoa-
rece aceata presupune identicarea pivotului , adic a a acelui element a[s] pentru care
a[k]a[s];cuk < s sia[k]a[s];cuk > s . Aceast a metod a de sortare se nume ste
sortarea prin partit ionare . Dac a nu exist a un pivot, atunci strategia este s a cre-
eze unul. Sunt mai multe strategii de alegere a pivotului. Dar, vom discuta despre
pivotul selectat elementul subtabloului drept, deci pivot =a[stang ] si presupunem
c a algoritmul de partit ionare este apelat pentru subtabloul a[stang : : : drept ]. Se
porne ste din ambele capete ale sale si se compar a elementele cu pivotul. ^Ins a par-
curgerea de la st^ anga la dreapta se realizeaz a de la al doilea element. Astfel, pentru
aceasta vom folosi un indice i. Pentru c a dorim ca elementele mai mici dec^ at pi-
vot s a e ^ n partea st^ anga a subtabloului, se sare peste elementele mai mici dec^ at
pivotul si se opret e la primul element mai mare sau egal cu acesta, a[i]. Invers,
parcurgerea se ^ ncepe cu ultimul element al subtabloului si folosim un indice j.
^In acest caz, dorim ca elementele mai mari dec^ at pivotul s a e aranjate ^ n partea
dreapt a a subtabloului si parcurgerea sare peste elementele mai mari dec^ at pivotul
si se opre ste atunci c^ and primul element ^ nt^ alnit este mai mic sau egal cu acesta,
a[j]. Dup a realizarea acestor parcurgeri se opresc si avem trei situat ii posibile s a
apar a: dac a i < j , atunci se interschimb a elementele a[i] sia[j] si se reia parcurgerea
increment^ and indicele i si decrement^ and indicele j; dac a i > j , atunci vom obt ine
partit ionarea subtabloului dup a interschimbarea pivotului cu elementul a[i]; dac a
i=j, atunci i=j=s si am obt inut partit ionarea. Dar, ultimele dou a cazuri pot
scrise ^ mpreun a, interschimb^ and pivotul cu a[j], pentru ij.
Se aplic a procedeul ^ n continuu de la st^ anga si de la dreapta pivotului, p^ an a c^ and
aceste subliste sunt sucient de mici, adic a se reduc la un singur element.
Quicksort implementat in C++ este urm atorul:
1# include < iostream >
2using namespace std;
3void schimb (int *x, int *y)
4{
5 int variabila ;
6 variabila = *x;
7 *x = *y;
8 *y = variabila ;
9}
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 34
10int Partitie (int a[], int l, int h)
11{
12 int pivot , index , i;
13 index = l;
14 pivot = h;
15 for (i = l; i < h; i++)
16 {
17 if (a[i] < a[ pivot ])
18 {
19 schimb (&a[i], &a[ index ]);
20 index ++;
21 }
22 }
23 schimb (&a[ pivot ], &a[ index ]);
24 return index ;
25}
26int RandomPivotPartition (int a[], int l, int h)
27{
28 int pvt , n, temp ;
29 n = rand ();
30 pvt = l + n % (h – l + 1);
31 schimb (&a[h], &a[pvt ]);
32 return Partitie (a, l, h);
33}
34int QuickSort (int a[], int l, int h)
35{
36 int pivindex ;
37 if (l < h)
38 {
39 pivindex = RandomPivotPartition (a, l, h);
40 QuickSort (a, l, pivindex – 1);
41 QuickSort (a, pivindex + 1, h);
42 }
43 return 0;
44}
45int main ()
46{
47 int n, i;
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 35
48 cout << " Introduceti dimensiunea vectorului pe care
doriti sa -l sortati : ";
49 cin >> n;
50 int a [100];
51 for (i = 0; i < n; i++)
52 {
53 cout << " Introduceti elementul vectorului " <<i+1<<":
";
54 cin >> a[i];
55 }
56 QuickSort (a, 0, n – 1);
57 cout << " Vectorul sortat este : ";
58 for (i = 0; i < n; i++)
59 cout <<a[i]<<" ";
60 return 0;
61}
S a observ am mai bine ^ n urm atorul exemplu cum se realizeaz a metoda quicksort .
S a consider am urm atorul vector de sortat: 6 ;4;2;10;9;3;5;7.
Conform algoritmului de partit ionare, pivot = 6,astfel c a vectorul va partit ionat
^ n dou a subtablouri: unul a c arui elemente mai mici sau egale cu 6, iar cel alalt va
avea elementele mai mari sau egale cu 5. Pornim cu doi indici i sijce vor
deplasat i spre mijlocul vectorului, p^ an a c^ and se returneaz a pozit ia de partit ionare
s, iar pivotul este interschimbat cu a[s]:
6 4 2 10 9 3 5 7 (pivot=6)
i j
6 4 2 10 9 3 5 7
i j
Elementele a[i] sia[j] vor interschimbate, rezult^ and vectorul:
6 4 2 5 9 3 10 7
i j
6 4 2 5 9 3 10 7
i j
6 4 2 5 3 9 10 7
j i
Pentru c a am ajuns ^ n situat ia ij, se interschimb a pivortul cu a[s], unde
s=j= 4 este pozit ia de partit ionare:
3;4;2;5;6;9;10;7
S a remarc am c a toate elementele mai mici dec^ at pivotul sunt la st^ anga aces-
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 36
tuia, iar cele mai mari, la dreapta. Se aplic a recursiv algoritmul de sortare pentru
sub sirurile a[0: : :3] =f3;4;2;5g sia[5: : :7] =f9;10;7g:
3 4 2 5 (pivot =3)
i j
3 4 2 5
i j
3 2 4 5
j i .
Se interschimb a pivotul cu a[s], unde s=j= 1 este pozit ia de partit ionare:
2;3;4;5
Se aplic a recursiv algoritmul de sortare pentru sub sirurile a[0: : :0] =f2g si
a[2: : :3] =f4;5g. Primul caz este elementar, sirul format dintr-un singur element
ind implicit sortat.
4 5 (pivot =3)
i j
4 5
j i
Se returneaz a pozit ia de interschimbare, s=j= 2.Apoi se apeleaz a recursiv
algoritmul de sortare pentru sub sirurile: a[2: : :1] sia[3: : :3]. Primul caz este cores-
punz ator unui sub sir vid, iar al doilea este un caz elementar.
Apelul algoritmului de sortare pentru subsecvent a a[5: : :7] =f9;10;7g:
9 10 7 (pivot =9)
i j
Elementele a[i] sia[j] vor interschimbate, rezult^ and vectorul:
9 7 10
i j
Indicii vor iar a si deplasat i,
9 7 10
j i
Se interschimb a pivotul cu elementul a[s] si se returneaz a pozit ia de interschim-
bare, s=j= 6.
7;9;10
Se apeleaz a recursiv algoritmul de sortare pentru sub sirurile: a[5: : :5] sia[7: : :7].
Ambele sunt cazuri elementare. Algoritmul se opre ste.
Vericarea corectitudinii: Pentru a verica corectitudinea algoritmului de par-
tit ionare, identic am precondi- t ia si postcondit ia: Pin=fstang < dreptg, iar
Pout=fa[k]a[s] pentru k=stang; : : : ; s 1 sia[k]a[s] pentru k=s+
1; : : : ; dreptg. Fie proprietatea I=fdac a i < j; atunci a[k]pivot; k =stang; : : : ; i;
iara[k]pivot pentru k=j; : : : ; drept; iar dac a ij;atunci a[k]pivot pentru
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 37
k=stang; : : : ; i 1;iara[k]pivot pentru k=j+ 1; : : : ; dreptg. Cum i < j , pro-
prietatea Ieste invariant a ^ n raport cu structura repetitiv a while , iar dup a ultima
interschimbare a pivotului cu elementul a[s], cus=j, se obt ine postcondit ia. Pen-
tru c a avem condit iile de oprire din cele dou a structuri repeat , asta asigur a c a indicii
i sijr am^ an ^ ntre limitele stang sidrept .
Corectitudinea algoritmului recursiv se face prin induct ie: cazul c^ and n= 1 este
corect, deoarece un vector cu un singur element este considerat sortat. Presupunem
c a algoritmul quicksort sorteaz a corect orice vector de lungime strict mai mic a dec^ at
n. Presupunem c a se apleaz a algoritmul pentru un vector de lungime n. Astfel,
obit inem partit ia s. Acum vom verica c a oricare ar dou a elemente de indici i si
jsunt ^ n ordinea corect a: dac a i < js 1 sau s+ 1i < j; atunci a[i]a[j],
conform ipotezei inductive; dac a i < s < j , atunci a[i]pivota[j], datorit a
faptului c a algoritmul de partit ionare este corect.
Analiza complexit at ii: Consider am drept operat ii de baz a doar comparat iile,
deoarece ^ n algoritmul de partit ionare, num arul acestora este mai mare, ^ n general
dec^ at num arul interschim-
b arilor.
Cazul cel mai defavorabil se obt ine atunci c^ and la ecare partit ionare se obt ine
una din subprobleme de dimensiune 1. Deoarece operat ia de partit ionare necesit a
O(q p) comparat ii, rezult a c a pentru acest caz num arul de comparat ii este O(n2).
Acest rezultat este oarecum surprinz ator, av^ and ^ n vedere c a numele metodei este
"sortarea rapid a". A sa cum vom vedea, ^ ntr-o distribut ie normal a, cazurile pentru
care quicksort execut a n2comparat ii sunt rare, fapt care conduce la o complexitate
medie foarte bun a a algoritmului. ^In contiunuare determin am num arul mediu de
comparat ii. Presupunem c a q+ 1 p=nlungimea secvent ei si c a probabilitatea
ca pivotul xs a e al k-lea element este1
n. Rezult a c a probabilitatea obt inerii
subproblemelor de dimensiune k p=i 1 siq k=n ieste1
n.^In procesul de
partit ionare, un element al tabloului,pivotul, este comparat cu toate celelalte, astfel
c a sunt necesare n 1 comparat ii. Acum num arul mediu de comparat ii se calculeaz a
prin urm atoarea formul a:
Tmediu (n) =8
>><
>>:(n 1) +1
nnX
i=1(Tmediu (i 1) +Tmediu (n i));dac a n1
1 ;dac a n= 0:
Rezolv am aceast a recurent a. Avem:
Tmediu (n) = (n 1) +2
n(Tmediu (0) ++Tmediu (n 1))
nTmediu (n) =n(n 1) + 2( Tmediu (0) ++Tmediu (n 1)).
Capitolul 2. Analiza algoritmilor de sortare bazat i pe metoda Divide-et-Impera 38
Trecem pe n^ nn 1:
(n 1)Tmediu (n 1) = ( n 1)(n 2) + 2( Tmediu (0) ++Tmediu (n 2)).
Sc adem:
nTmediu (n) = 2( n 1) + ( n+ 1)Tmediu (n 1).
^Imp art im prin n(n 1) si rezolv am recurent a obt inut a:
Tmediu (n)
n+1=Tmediu (n 1)
n+2
n 1 2
n(n+1)
=Tmediu (n 1)
n 1+2
n+2
n+1
2
(n 1)n+2
n(n+1)
=: : :
=Tmediu (0)
1+2
1++2
n+1
2
12++2
n(n+1)
= 1 + 2
1
1+1
2++1
n+1 2
1
11++1
n(n+1)
:
Deoarece 1 +1
2++1
n=O(log2n) si seriaP1
k(k+1)este convergent a, rezult a
c aT(n) =O(nlog 2n).
Prin urmare, complexitatea medie a algoritmului quicksort esteO(nlog 2n).
Amintim o metod a mai ecient a de a alege pivotul (alegerea aleatoare a pivotului,
metoda "medianei celor 3 valori"- ^ n care se alege elementul mijlociu ca m arime,
dintre trei elemente alese la ^ nt^ amplare din vector etc.), utilizarea algoritmului de
sortare prin insert ie direct a pentru subtablourile de dimensiune mic a (^ ntre 5 si 15
elemente) sau renunt area la soratarea subtabloului de dimensiune mic a si ^ ncheierea
algoritmului de sortare prin insert ie direct a a^ ntregului tablou ce este aproape sortat,
modicarea algoritmului de partit ionare (de exemplu, partit iona- rea vectorului ^ n
trei p art i: elementele mai mici dec^ at pivotul ^ ntr-o parte, cele egale cu pivotul ^ n
cea de-a doua parte si cele mai mari ^ n cea de-a treia parte).
Un dezavantaj al algoritmului quicksort este faptul c a nu este stabil.
Capitolul 3
Aplicat ii practice
Aplicat ia 1. La sf^ ar situl lunii iunie la proba de 150 m vitez a din cadrul unei
competit ii de atletism particip a 9 participant i de sex feminin reprezent^ and ecare
cate un club sportiv. Primele 3 ^ n clasament, ^ n funct ie de timpul cel mai mic
de sosire, vor recrutate s a fac a parte din echipa nat ional a a t arii. Presupunem
c a acestea nu depinde de rezultatul alteia, deci sunt independente si urmeaz a o
repartit ie uniform a de 10.5 secunde si 11.5 secunde. Determinat i timpii celor 3
sportive.
39
Bibliograe
[1] T.H. Cormen, C.E. Leiserson, R.L. Rivest, Introducere ^ n Algoritmi , Computer
Libris Agora, Cluj-Napoca, 2000 (traducere).
[2] D. Lucanu, M. Craus, Proiectarea Algoritmilor , Ed. Polirom, 2008.
[3] Cristian A.Giumale – Introducere in analiza algoritmilor .
[4] Anany Levitin Introduction to The Design and Analysis of Algorithms-3rd ed ,
Pearson.
[5] R azvan Andonie, Ilie G^ arbacea, Algoritmi Fundamentali O perspectiv a
C++ ,Computer Libris Agora, Cluj-Napoca, 1995 (traducere).
40
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Lucrare de licent a [605833] (ID: 605833)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
