Sunteți pe pagina 1din 9

Curs 4

Algoritmi de Sortare (1)

Sortarea ese o operatie foarte des întilnîtă în rezolvarea unei probleme prin metode algoritmice. Din
acest motiv algoritmii de sortare constituie o clasa extrem de importantă care merită o atentie
specială iar o analiză a celor mai cunoscuti algoritmi de sortare este utilă si necesară.

Insertion Sort (sortare prin inserare)

Algoritmul Insertion Sort considera ca în pasul k, elementele A[1÷k-1] sunt sortate, iar elementul k
va fi inserat, astfel încât, dupa aceasta inserare, primele elemente A[ ÷k] sa fie sortate.
Pentru a realiza inserarea elementului k în secventa A[1÷k-1], aceasta presupune:
· memorarea elementului intr-o varibila temporara;
· deplasarea tuturor elementelor din vectorul A[1÷k-1] care sunt mai mari decât A[k], cu o
poziie la dreapta (aceasta presupune o parcurgere de la dreapta la stânga);
· plasarea lui A[k] în locul ultimului element deplasat.

insertion_sort(A,n)
{
for (k=2; k<= n; k++)
{
temp = A[k];
i=k-1;
while (i>=1 && A[i] > temp)
{
A[i+1] = A[i];
i=i-1;
}
A[i+1] = temp;
}
}

Complexitatea:

Cazul cel mai dafavorabil: situatia în care deplasarea (la dreapta cu o pozitie în vederea înserarii) se
face pâna la începutul vectorului, adica sirul este ordonat descrescator.
Exprimarea timpului de lucru:
T(n) = 3·(n - 1) + (1 + 2 + 3+ ... + n - 1) = 3(n-1) + 3n (n - 1)/2
Rezulta complexitatea: T(n) = O(n2) ¾ functie polinomiala de gradul II.

Observatie: Când avem mai multe bucle imbricate, termenii buclei celei mai interioare dau gradul
polinomului egal cu gradul algoritmului.

Bucla cea mai interioara ne da complexitatea algoritmului.


n
 i  O ( n2 )
i 1

1
Buble Sort (sortare prin interschimbare)

Buble_sort (A,n)
// A[1..n] - seceventa de sortat
{
flag=1;
k=n;
while (flag != 0 && k >= 1)
{
k=k-1;
flag=0;
for (i=1; i<=k; i++)
if ( A[i] > A[i+1] )
{
interchange ( A[i], A[i+1] );
flag=1;
}
}
}

Complexitatea: Evident O(n2)

Shell Sort (sortare prin metoda Shell)


Sortarea se face asupra unor subsecvente care devin din ce in ce mai mari pina la dimensiunea n.
Fiecare subsecventa I este determinata de un h i numit increment.
Incrementii satisfac conditia : ht > ht-1 > … > h2 > h1

Fie hi = h. Avem urmatoarele subsecvente:

A[1], A[1+h], A[1+2h], … A[1+kh];


A[2], A[2+h], A[2+2h], …
…………….
A[h], A[h+h], A[1+2h], …

Exemplu :

h=4 1 5 7 8 3 12 9 4 12
|____________________________|_____________________________|
|____________________________|
|____________________________|
|____________________________|

h=3 1 5 7 8 3 12 9 4 12
|_____________________|_____________________|
|_____________________|_____________________|
|_____________________|_____________________|

h=1 1 5 7 8 3 12 9 4 12
|_______|_______|_______|_______|_______|______|_______|_______|

Shell_sort (A,n,h,t);

2
// A[1..n] - seceventa de sortat
// h[1..t] - incrementii h t > ht-1 > … >h1=1
{
for ( s=t; s>=1; s--)
{
h=h[s];
for ( j=h+1; j<=n; j++)
{
x=A[j];
i=j-h;
while (i>0 && x <A[i] )
{
L[i+h]=L[i];
i=i-h;
}
L[i+h]=x;
}
}
}

Complexitatea: Greu de esitimat. Se presupune (din analiza rezultatelor experimentale) ca ar fi n 1,2.

Radix Sort

Se presupune ca cheile de sortare sint numere zecimale. Fiecare element din secventa de sortat are k
cifre d1d2…dk

radix_sort (A,n,k)
// se preupune ca numerele zecimale sint reprezentate in baza d<=10
// seceventa se afla intr-o coada globala gq unde q[i], i=1,…,k sint cozi
{
for ( i=1; i<=d; i++ )
q[I]=F;
for ( i=k; i>=1; i-- )
{
while ( ! isempty(gq) )
{
x=del(gq);
d[i]=cifra i din x;
add (q[d[i]],x);
}
for ( i=1; i<=d; i++ )
while ( ! isempty(q[i]) ) //adauga coada q[i] la cooda globala gq
add (gq,del(q[i])
}
for ( i=1; i<=n; i++ )
x[i]=del[gq];
}

Complexitatea: O(nk)

Heap_Sort
Definitie:
Se numeste arbore heap un arbore binar T = (V, E) cu urmatoarele proprietati:
1) $ functia key : V ® R care asociaza fiecarui nod o cheie.
2) " un nod v Î V cu degree(v) > 0 (nu este nod terminal), atunci:
key(v) > key(left_child(v)), daca $ left_child(v)
key(v) > key(right_child(v)), daca $ right_child(v)
(Pentru fiecare nod din arbore, cheia nodului este mai mare decât cheile descendentilor).

3
Observatie: De obicei functia cheie reprezinta selectia unui subcâmp din câmpul de date memorate în
nod.

Generare Heap

Generare Heap prin inserari repetate

heap_gen_1 (A,V, n)
// A[1..n] - seceventa de sortat
// V ¾ vectorul ce contine reprezentarea heap-ului;
// N ¾ numarul de noduri din heap,
{
N = 1 //se considera pentru început un heap cu un singurelement,
//dupa care toate celelalte elemente vor fi inserate în acest heap
for ( i = 2; i <= n; i++ )
insert(V, N, A[i]);
}

insert(V, N, a)
// V ¾ vectorul ce contine reprezentarea implicita a heap-ului;
// N ¾ numarul de noduri din heap,
// ambele sunt plasate prin referinta (functia insert le poate modifica);
// a ¾ atomul de inserat;
// 1) In reprezentarea implicita: V [N + 1] = a ; N = N + 1
// În continuare se reorganizeaza structura arborelui astfel încît sa-si pastreze structura de heap.
// 2) Se utilizeaza interschimbarile. Comparatii:
// Se iau 2 indici: child = N si
// parent = [N/2]
// Se compară V[child] cu V[parent]
// Interschimbare daca V[child] nu este mai mic decât V[parent]
// 3) Înaintare în sus: child = parent
// parent = [child/2]
// 4) Se reia pasul 2) pâna când nu se mai face interschimbarea.
{
N = N+1;
V[N] = a ;
child = N ;
parent = [N/2] ;
while (parent £ 1)
{
if key(V [child]) > key(V [parent])
{
interchange (V [child],V [parent]);
child = parent;
parent = [child/2];
}
else break; // se paraseste bucla Û parent = 0
}
}

Complexitatea:
Complexitatea operatiei insert:

4
În cazul cel mai defavorabil se parcurge o ramura care leaga un nod terminal de radacina. Rezulta,
complexitatea este data de adâncimea arborelui. Daca N este numarul de noduri din arbore, 2 k £ N £
2k+1 , si adâncimea arborelui este k+1.

2k - 1 < N £ 2k+1 - 1 Þ k = [log2N]


| |
| |
| |
nr. de noduri nr. de noduri
ale arborelui ale arborelui
complet de complet de
adâncime k adâncime k+1

k £ log2N < k + 1 Þ adâncimea arborelui este k = [log 2N].


Deci complexitatea este O(log N).

Complexitatatea algoritmului heap_gen_1

Se fac n-1 operatii insert în heap-uri cu dimensiunea N £ n


Rezulta complexitatea acestor operatii nu depaseste O(n×log n). Facem un studiu pentru a vedea
daca nu cumva ea este mai mica decît O(n×log n).

Cazul cel mai defavorabil este situatia în care la fiecare inserare se parcurge o ramura completa. De
fiecare data inserarea unui element se face adaugând un nod la ultimul nivel. Pentru nivelul 2 sunt
doua noduri. La inserarea lor se va face cel mult o retrogradare (comparatie).

nivelul 2 : 2 noduri 1 comparatie


nivelul 3 : 4 noduri 2 comparatii
nivelul 4 : 8 noduri 3 comparatii
--------------------------------------------------
nivelul i : 2i-1 noduri i-1 comparatii

Considerând un arbore complet (nivel complet) Þ n = 2k - 1 Þ numarul total de comparatii pentru


toate nodurile este T(n) de la nivelul 2 la nivelul k. Vom calcula:
k
T (n)   (i  1)  2 i 1
i 2
k 1
Sa aratam: T ( n)   i  2 i
i 1
cu tehnica T ( n )  2  T ( n)  T ( n) . Asadar:

k 1 k 1 k 1 k 1
T (n)  2   i  2 i   i  2 i   i  2 i 1   i  2 i 
i 1 i 1 i 1 i 1
k 1
 1  2  2  2  3  2 ...( k  2)  2
2 3 4
 ( k  1)  2 k  1  2 1  2  2 2  3  2 3 ...( k  1)  2 k 1 
k 1 k 1
 2  ( k  1)  2 k   2 i  ( k  1)  2 k  2  2 0  2 1   2 i 
i 2 i 0

 ( k  1)  2  1  (2  1)  ( k  2)  2  2
k k k

Rezulta: T (n)  ( k  2)  2 k  2  ( k  2)  (2 k  1)  k  2  2  n  ( k  2)  k
iar: k  log 2 (n  1) ,
din n  2k  1
Rezulta: T (n)  n  (log 2 (n  1)  2)  log 2 (n  1)
------------------------

5
termen dominant

Facându-se majorari, rezulta complexitatea O(n×log n) pentru Heap_Gen_1.

Generare Heap prin retrogradari repetate

Construim heap-ul de jos în sus (de la dreapta spre stânga). Cele mai multe noduri sunt la baza, doar
nodurile din vârf parcurg drumul cel mai lung.

II

noduri terminale

Elementele V[i+1,n] îndeplinesc conditia de structura a heap-ului:


" j >i avem: V[j] > V[2*j] , daca 2*j £ n
V[j] > V[2*j +1] , daca 2*j + 1 £ n

Algoritmul consta în adaugarea elementului V[i] la structura heap-ului. El va fi retrogradat la baza


heap-ului (prelucrare prin retrogradare):

heap_gen_2 (A,V, n)
// A[1..n] - seceventa de sortat
// V ¾ vectorul ce contine reprezentarea heap-ului;
{
for (i = 1; i <= n, i++)
V[i]=A[i];
for (i = [n/2]; i >= 1; i-- )
retrogradare(V,n, i);
}

retrogradare(V, n, i)
{
parent = i ;
child = 2*i ; // fiu stânga al lui i
while (child £ n)
{

6
if ( child+1 £ n && key(V[child+1]) > key(V[child] )
child = child + 1 ;
if key(V[parent]) < key(V[child])
{
interchange(V[parent], V[child]);
parent = child ;
child = 2*parent ;
}
else break;
}
}

În aceasta situatie, vom avea:

Complexitatea

Fie un arbore complet cu n = 2k - 1. Cazul cel mai defavorabil este situatia în care la fiecare
retrogradare se parcurg toate nivelele:

nivel k : nu se fac operatii


nivel k-1 : 2k-2 noduri o operatie de comparatie
nivel k-2 :2 k-3 noduri 2 operatii
------------------------------------------------------------------
nivel i : 2i-1 noduri k-i operatii
-------------------------------------------------------------------
nivel 2 : 21 noduri k-2 operatii
nivel 1 0
: 2 noduri k-1 operatii

k 1
Se aduna, si rezulta: T ( n)   ( k  i )  2i 1
i 1

Tehnica de calcul este aceeasi: T (n)  2  T (n)  T (n)

k 2 k 2
T ( n)   ( k  i )  2 i   ( k  i )  2 i 1  ( k  1)  2 1  ( k  2)  2 2  ( k  3)  2 3 ...3  2 k  3  2  2 k  2 
i 1 i 1
k 3 k 3
 ( k  1)  2  ( k  2)  2  ( k  3)  2 ...2  2
0 1 2 k 3
 22 k 2
 ( k  1)   2  2
1 k 1
 ( k  1)  2   2 1 
0

i 1 i 0
k 1 k 2 k 2 k 2
2 2  2  ( k  1)  2  (2  1)  k  1  3  2  k 1

Rezulta: T ( n )  3  2 k  2  k  1  3  ( 2 k  1)  k  1

T ( n )  3  n  log2 ( n  1)  1
-------
termen dominant

Rezulta complexitatea O(n) pentru heap_gen._2

Algoritmul Heap Sort

heap_sort (A,n)

7
// A[1..n] - seceventa de sortat
{
heap_gen(A, V, n); N = n;
for ( i = n; i >= 2; i-- )
{
N=I;
A[i] = remove(V, N) ;
}
}

Aceasta procedura sorteaza un vector A cu N elemente: transforma vectorul A într-un heap si


sorteaza prin extrageri succesive din acel heap.
partea sortata
heap i a vectorului

max min

Procdura remove

remove(V, N)
// V ¾ vectorul ce contine reprezentarea implicita a heapu-lui;
// N ¾ numarul de noduri din heap
// ambele sunt plasate prin referinta (functia remove le poate modifica);
// se scoate elementul cel mai mare care este radacina heap-ului; se initializeaza cei 2 indici;
// se reorganizeaza structura arborilor: se ia ultimul nod de pe nivelul incomplet si-l aduc în nodul
// radacina, si aceasta valoare va fi retrogradata pîna când structura heap-ului este realizata.
// parent = max(parent, lchild, rchild).
// Exista trei cazuri:
// 1. conditia este îndeplinita deodata;
// 2. max este în stânga Þ retrogradarea se face în stânga;
// 3. max este în dreapta Þ retrogradarea se face în dreapta.
{
a = V[1];
V[1] = V[N];
N = N-1;
parent = 1;
child = 2;
while (child £ N)
{
if child+1 £ N and key(V[child+1]) > key(V[child])
child= child+1;
if key (V[parent]) < key(V[child])
{
interchange(V[parent], V[child]);
parent= child;
child= 2*parent;
}
else break;
}
return(a);
}

Complexitatatea algoritmului heap_sort

8
Complexitatea algoritmului Heap_Sort_2 este determinata de functiile remove ce nu pot fi aduse la
complexitate < O(log n). Astfel: Heap_Sort_2 = O(n) + O(n·log n)
---------------
termen ce determina complexitatea

Rezulta complexitatea alg. Heap_Sort = O(n·log n)

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