Sunteți pe pagina 1din 74

+

Algoritmi și tehnici de
programare

Cursul 6
+
Cuprins

◼ Sortare rapidă

◼ Sortarea Shell

◼ Sortare prin numărare

◼ Sortarea Heap

◼ Sortare pe grupe

◼ Sortare pe baza cifrelor

◼ Pointeri la funcții
+
Sortarea rapidă

◼ QuickSort sau Sortarea rapidă este o metodă eficientă de


sortare a unui tablou, descoperită în 1960 de programatorul
britanic C.A.R. Hoare.

◼ Algoritmul
de sortare rapidă, ca de altfel şi algoritmul de
sortare prin interclasare, se bazează pe “divide şi
stăpâneşte”.

◼ In medie, efectuează O(n log n) comparații pentru a


sorta n elemente. În cazul cel mai defavorabil,
efectuează O(n^2) comparații.
+
Sortarea rapidă

◼ Pentruun subşir A[p…u].


◼ Divide: Şirul A[p..u] este împărţit (rearanjat) în doua subşiruri nevide
A[p..q] şi A[q + 1..u], astfel încât fiecare element al subşirului A[p..u] să
fie mai mic sau egal cu orice element al subşirului A[q + 1..u]. Indicele q
este calculat de procedura de partiţionare.
◼ Stăpâneşte: Cele două subşiruri A[p..q] şi A[q + 1..u] sunt sortate prin
apeluri recursive ale algoritmului de sortare rapidă.
◼ Combina: Deoarece cele două subşiruri sunt sortate pe loc, nu este
nevoie de nici o combinare, şirul A[p..u] este ordonat.
+
Sortarea rapidă

◼ Algoritmul:

◼ 1. Se alege o valoare pivot. Se ia valoarea elementului din mijloc ca


valoare pivot, dar poate fi oricare altă valoare, care este în intervalul valorilor
sortate, chiar dacă nu este prezentă în tablou.

◼ 2. Partiţionare. Se rearanjează elementele în aşa fel încât, toate elementele


care sunt mai mari decât pivotul merg în partea dreaptă a tabloului. Valorile
egale cu pivotul pot sta în orice parte a tabloului. În plus, tabloul poate fi
împărţit în părţi care nu au aceeaşi dimensiune (nu sunt egale).

◼ 3. Se sortează amândouă părţile. Se aplică recursiv algoritmul de sortare


rapidă în partea stângă şi în partea dreaptă.
+
Sortarea rapidă

◼ Dorim să sortăm şirul

◼ {4, 6, 1, 3, 5, 2, 7} folosind sortarea rapidă.


+
Sortarea rapidă

Valoarea pivot = mijloc=3; i=0; j=6

4 6 1 3 5 2 7
+
Sortarea rapidă

v[0]>mijloc i nu creşte (i=0)


v[6]>mijloc j scade (j=5)
i<=j
V[0]>mijloc>v[5] interschimbăm 4 cu 2
creşte i (i=1), scade j (j=4)

4 6 1 3 5 2 7

2 6 1 3 5 4 7
+
Sortarea rapidă

v[1]>mijloc i nu creşte (i=1)


v[4]>mijloc j scade (j=3)
i<=j
v[1]>mijloc>=v[3] interschimbăm 6 cu 3
creşte i (i=2), scade j (j=2)

2 6 1 3 5 4 7

2 3 1 6 5 4 7
+
Sortarea rapidă

v[2]<mijloc i creşte (i=3), v[2]<mijloc j nu scade (j=2)


i nu este mai mic decât j (3>2)
i>j se opreşte partiţionarea

2 3 1 6 5 4 7
+
Sortarea rapidă

Vector sortat

1 2 3 4 5 6 7
+
Sortarea rapidă
❑ i = p; j = u; m=mijloc
pasul curent:
❑ daca a[i] < m atunci i = i+1
❑ daca a[j] > m atunci j = j-1
❑ daca a[i] > m > a[j] si i < j atunci
▪ schimba(a[i], a[j])
▪ i = i+1
▪ j = j-1
pasul curent se execută atât timp cât i <= j.
+
Sortarea rapidă
void poz(int v[], int stanga, int dreapta, int *s, int *d)
{
int i, j, mijloc, aux; // variabilele
i = stanga; // initializarea indicilor
j = dreapta;
mijloc = v[(stanga + dreapta) / 2]; // initializarea variabilei - pivot
while (i <= j)
{
while (v[i] < mijloc) // apropierea i-ului de mijloc
i = i + 1;
while (v[j] > mijloc) // apropierea j-ului de mijloc
j = j - 1;
if (i <= j) //conditia efectuarii operatiei de interschimbare
{
aux = v[i];
v[i] = v[j]; // operatia de interschimbare
v[j] = aux;
i = i + 1;
j = j - 1;
}
}
*s = i; *d = j;
}
+
Sortarea rapidă
void quick(int *v, int p, int u)
{
int i,j;
if (p<u)
{
poz(v, p, u, &i,&j);
if (p<j) quick(x, p, j);
if (i<u) quick(x, i, u);
}
}
+
Sortarea Shell
◼În 1959 D.L. Shell a propus un algoritm de ordonare bazat pe
metoda de sortare prin inserţie directă.

◼ Este un algoritm cu o performanţă îmbunătăţită deoarece face


comparaţii între elemente distanţate din vector.

◼ Algoritmul sortează mai întâi prin inserţie subvectori obţinuţi din


vectorul iniţial prin extragerea componentelor aflate la o distanţă inc
una de cealaltă, distanţă care se numeşte increment.

◼ Repetând procedeul pentru incremenţi din ce în ce mai mici şi, în


final, pentru incrementul 1, se obţine vectorul sortat.

◼ Timpul este de ordinul O(n^2) – in cel mai nefavorbail caz


+
Sortarea Shell
◼ Fie vectorul:
◼ 32 95 16 82 24 66 35 19 75 54 40

◼ Prima iteraţie se face cu salt 5.


◼ Pentru a putea face deplasări spre stânga cu câte 5 poziţii vom porni de
la elementul al 6-lea din şir, adică 66.
◼ Încercăm să îl deplasăm pe 66 cu 5 poziţii spre stânga, dar vedem că
acolo este 32, un număr mai mic.

◼ Trecem la 35.
◼ Încercăm să îl deplasăm cu 5 poziţii spre stânga şi vedem că acolo se
află 95, un număr mai mare.
◼ Ca urmare facem efectiv deplasarea, iar 95 trece în locul lui 35.
◼ Obţinem:

◼ 32 35 16 82 24 66 95 19 75 54 40
+
Sortarea Shell
◼ Se consideră un şir descrescător de numere naturale, numite incremenţi
dintre care ultimul, incn, este 1:

◼ inc1 > inc2 > … > inci > inci+1 > … > incn = 1.

◼ (1) Se porneşte cu incrementul inc1. Acesta poate să fie iniţial jumatate


din lungimea vectorului. (Ulterior fiecare increment se înjumătăţeşte.)
◼ (2) La pasul iterativ m se consideră incrementul incm . Se sortează prin
inserţie directă subvectorii obţinuţi din vectorul iniţal luând elemente aflate
la distanţa incm, adică subvectorii:

◼ v[1], v[incm+1],
◼ v[2], v[incm+2],

◼ v[incm], v[2incm].

◼ Apoi se reia pasul (2) pentru incrementul incm+1= incm/2.


◼ Deoarece ultimul increment este incn=1, ultimul pas iterativ n se reduce
la sortarea prin inserţie directă, deci vectorul va fi sortat.
+
Sortarea Shell
Un vector este numit k-sortat dacă, luînd în considerare elementele
din k în k, acestea sînt sortate.

◼ dacă pentru un astfel de vector se realizează apoi o l-sortare, cu o valoare


l< k, vectorul va fi în continuare k-sortat, dar și l-sortat;

◼ folosind în continuare valori din ce în ce mai mici pentru k, ultima valoare


fiind 1, vectorul va fi complet sortat;

◼ la ultima iterație (pentru valoarea 1) se realizează de fapt o sortare prin


inserție obișnuită.
+
Sortarea Shell
void sort_shell(double v[],int l)
{
int i, j, inc; inc2 =inc1/2 ; inc3 =inc2/2…..
double a;
for(inc=l/2;inc>0;inc=inc/2)
for(i=inc; i<l;i++)
for(j=i-inc;(j>=0)&&(v[j]>v[j+inc]);j=j-inc)
{
a=v[j];
v[j]=v[j+inc];
v[j+inc]=a;
}
}

Apel:
int n;
double x[100]
sort_shell(x,n);
+
Sortare prin numărare

◼ Sortarea prin numărare (Counting Sort) este o metodă


de sortare a vectorilor care se bazează pe utilizarea
unui vector de frecvență.

◼ Acest algoritm de sortare poate fi extrem de eficient în


anumite situații, în funcție de intervalul de valori al
elementelor vectorului.

◼ Timpul este de ordinul O(n)


+
Sortare prin numărare

◼ O varianta pentru metoda sortării prin numărare pentru un vector


v constă în:
◼ găsirea pentru fiecare element v[i], a numărului de elemente
din vector, mai mici ca el;
◼ numerele obţinute sunt memorate într-un vector num;
◼ elementele vectorului de sortat v, sunt iniţial atribuite vectorului
temp.
◼ pe baza vectorului num, elementele lui temp vor fi aranjate în
vectorul v.
+
Sortare prin numărare
◼ Vrem să sortăm urmatorul şir:
◼ v= (8, 1, 3, 15, 5)

◼ Elementele lui v le atribuim lui temp:


◼ temp= (8, 1, 3, 15, 5)

◼ Pentru fiecare element v[i] numărăm câte elemente sunt mai mici
ca el, aceste numere reţinându-le în vectorul num:
◼ num=(3, 0, 1, 4, 2) se reconstitue vect V astfel:
◼ v[num[0]]=temp[0]; v[num[1]]=temp[1]...

◼ obţinându-se vectorul v sortat (1, 3, 5, 8, 15)

◼ Dezavantajul metodei constă în utilizarea a doi vectori de lucru,


deci utilizarea unui spaţiu de memorie suplimentar
+
Sortare prin numărare
void sort_numarare(int *v, int l)
{
int i,j,*num;
int *temp;
temp=(int*)malloc(l*sizeof(int));
num=(int*)malloc(l*sizeof(int)); Apel:
for(i=0;i<l; i++) int n;
num[i]=0; int *x;
for(i=0;i<l; i++) sort_numarare(x,n);
temp[i]=v[i];
for(i=0;i<l-1;i++)
for(j=i+1;j<l; j++)
if(v[j]<v[i])
num[i]=num[i]+1;
else num[j]=num[j]+1;
for(i=0;i<l;i++)
v[num[i]]=temp[i];
free(temp);
free(num);
}
+
Sortarea Heap

◼ Mai este cunoscuta sub denumirea de “sortare prin metoda


ansamblelor”

◼ Algoritmul se aseamana, in unele privinte, cu sortarea prin selectie

◼ Timpul este de ordinul O(n * log n)

◼ Sortare Heap
◼ Aranjare heap
◼ Construire heap
+
Sortarea Heap

◼ La fiecare pas, cel mai mic element din tablou este gasit si mutat
in spatele tabloului, fiind ignorat de pasii urmatori, care vor continua
pe restul tabloului

◼ Diferenta fata de sortarea prin selectie este ca pasii urmatori ai


algoritmului vor depune un efort mai mic (chiar mult mai mic) pentru a
depista minimul din tabloul ramas

◼ Fiecare pas al algoritmului are darul de a usura sarcina pasilor ce


urmeaza, ceea ce duce la performanta foarte buna a algoritmului
+
Sortarea Heap

◼ Gasirea minimului din tablou, operatie ce are loc la fiecare pas,


se bazeaza pe aducerea tabloului la forma de ansamblu

◼ Un ansamblu este un sir vi (i = 1 … n) care indeplineste


urmatoarele conditii pentru fiecare i:
◼ vi ≤ v2·i

◼ vi ≤ v2·i+1

◼ Evident, pentru valori ale lui i mai mari decat n/2 nu se pune
problema indeplinirii conditiilor de mai sus
+
Sortarea Heap

◼ Orice tablou poate fi transformat usor intr-un arbore binar


Index: 1 2 3 4 5 6 7 8 9
v: v1 v2 v3 v4 v5 v6 v7 v8 v9

v1

v2 v3

v4 v5 v6 v7

v8 v9
+
Sortarea Heap

◼ Daca tabloul era ansamblu, se observa ca arborele binar obtinut indeplineste


urmatoarea conditie: “fiecare nod are cheia mai mare sau egala cu a tatalui
sau”.

◼ Astfel, v2 si v3 sunt mai mari sau egale cu v1, iar v4 si v5 sunt mai mari sau
egale cu v2, s. a. m. d.

◼ Dar v1 este radacina arborelui binar, ceea ce inseamna ca v1 trebuie sa fie


elementul minim al tabloului

◼ Deci intr-un ansamblu, elementul minim se afla intotdeauna pe prima pozitie

◼ In cadrul algoritmului HeapSort, daca la fiecare pas aducem tabloul pe care


lucram la forma unui ansamblu inseamna ca am localizat in acelasi timp si
minimul din tablou
+
Sortarea Heap

◼ Aducerea unui tablou la forma de ansamblu se face urmarind situatii cum este
cea descrisa mai jos:

Index: 1 … i … 2·i 2·i+1 … n


v: v1 … vi … v2·i v2·i+1 … vn

◼ Daca nu este indeplinita una din conditiile vi ≤ v2·i si vi ≤ v2·i+1 atunci se va


interschimba vi cu minimul dintre v2·i si v2·i+1
◼ Elementele astfel interschimbate vor indeplini conditia de ansamblu
◼ Pentru o eficienta cat mai mare, urmarirea acestui gen de situatii trebuie
facuta de la dreapta la stanga, in caz contrar fiind nevoie de reveniri repetate
chiar si dupa ce o situatie de neconcordanta a fost rezolvata
+
Sortarea Heap

◼ Vom studia, pas cu pas, modul in care un tablou oarecare poate fi


transformat in ansamblu

◼ Aceasta transformare se va aplica la fiecare pas in cadrul algoritmului


HeapSort, pe un tablou din ce in ce mai mic (deoarece dupa fiecare pas,
primul element al tabloului, care este elementul minim, va fi eliminat,
algoritmul continuand pe restul tabloului)

◼ Pentru simplitate, vom lucra pe reprezentarea sub forma de arbore a


tabloului:
Index: 0 1 2 3 4 5 6 7 8
v: 9 5 1 8 6 4 3 7 2
+
Sortarea Heap
9

5 1

8 6 4 3

7 2

◼ Problema se pune numai pentru noduri neterminale


◼ Localizam cel mai de jos nod neterminal, si in caz ca sunt mai multe astfel de noduri,
il consideram pe cel mai din dreapta – acesta este 8
◼ Cum 8 are fiii 7 si 2 si este mai mare decat ambii, se va interschimba cu cel mai mic
dintre ei, adica cu 2
+
Sortarea Heap
9

5 1

2 6 4 3

7 8

◼ Urmatorul nod neterminal este 1 (nodul 5 este pe acelasi nivel, dar il alegem
intotdeauna pe cel mai din dreapta in astfel de cazuri)
◼ Nodul 1 este mai mic decat fiii sai, deci nu va face obiectul vreunei interschimbari
◼ Trecand la nodul 5, acesta nu indeplineste conditiile, ca atare va fi interschimbat cu
cel mai mic fiu al sau, anume 2
+
Sortarea Heap
9

2 1

5 6 4 3

7 8

◼ Inainte de a trece la noul nod neterminal, verificam ca ultimul nod interschimbat (5)
sa indeplineasca conditia referitoare la fiii sai (7 si 8) – se observa ca o indeplineste
◼ Noul nod neterminal este 9
◼ Acesta nu indeplineste conditiile, fiind mai mare si decat 2 si decat 1, ca atare, 9 va
fi interschimbat cu cel mai mic, deci cu 1
+
Sortarea Heap
1

2 9

5 6 4 3

7 8

◼ S-ar putea ca ultimul nod interschimbat (9) inca sa nu indeplineasca conditiile


referitoare la fiii sai, in noua sa locatie
◼ 9 fiind mai mare si decat 4 si decat 3, se va interschimba cu 3 (cel mai mic)
◼ Astfel de interschimbari repetate vor avea loc pana cand 9 ajunge pe un nivel pe care
fiii sai sunt mai mari sau egali cu el (sau pe un nivel unde nu mai are fii)
+
Sortarea Heap
1

2 3

5 6 4 9

7 8

◼ 9 a ajuns pe un nivel terminal (nu mai are fii) deci nu mai continuam in jos
◼ In acest moment, tabloul a ajuns la forma de ansamblu, fiecare nod avand cheia
mai mica sau egala decat cheile fiilor sai
◼ Cel mai mic element al tabloului a ajuns pe post de radacina
◼ Interschimbam radacina cu ultimul element al tabloului, adica 1 cu 8
+
Sortarea Heap
8

2 3

5 6 4 9

7 1

◼ Elementul minim (1) se elimina si se adauga la un tablou auxiliar, initial vid, care
va contine la final elementele sortate

◼ Acesta a fost primul pas al algoritmului de sortare HeapSort

◼ Vom studia inca un pas al algoritmului


+
Sortarea Heap

◼ Situatia actuala este prezentata mai jos:


8

2 3

5 6 4 9

7 1
Index: 0 1 2 3 4 5 6 7 8
Tablou auxiliar: 1 - - - - - - - -
+
Sortarea Heap
8

2 3

5 6 4 9

◼ Nodurile neterminale considerate sunt, in ordine: 5, 3, 2 si 8

◼ Datorita pasului anterior, nodurile 5, 3 si 2 indeplinesc conditiile referitoare la fiii lor


(pasii anteriori au usurat sarcina pasului curent)

◼ Nodul 8 nu indeplineste conditiile, deci va fi interschimbat cu 2


+
Sortarea Heap
2

8 3

5 6 4 9

◼ Nici in noua locatie, 8 nu indeplineste conditiile, ca urmare va fi interschimbat


cu 5
+
Sortarea Heap
2

5 3

8 6 4 9

◼ Nici in noua locatie, 8 nu indeplineste conditiile, ca urmare va fi interschimbat


cu 7
+
Sortarea Heap
2

5 3

7 6 4 9

◼ 8 nu mai are fii, deci ne oprim aici

◼ Tabloul a devenit ansamblu, cel mai mic element din tablou ajungand pe post de radacina

◼ Interschimbam radacina cu ultimul element al tabloului, adica 2 cu 8


+ 42

Sortarea Heap
8

5 3

7 6 4 9

◼ Elementul minim (2) se elimina si se adauga la tabloul auxiliar

◼ Acesta a fost al doilea pas al algoritmului de sortare HeapSort


+
Sortarea Heap

◼ Situatia actuala este prezentata mai jos:


8

5 3

7 6 4 9

2
Index: 0 1 2 3 4 5 6 7 8
Tablou auxiliar: 1 2 - - - - - - -
+
Sortarea Heap

◼ Repetand algoritmul de transformare a tabloului in ansamblu si eliminand


dupa fiecare pas elementul minim obtinut (radacina arborelui), vom obtine in
tabloul auxiliar elementele ordonate

◼ La fiecare pas, tabloul scade cu un element

◼ De asemenea, se poate observa ca la fiecare pas, in afara de radacina


arborelui, toate celelalte elemente indeplinesc deja conditia de ansamblu
datorita pasului anterior

◼ Rezulta ca sarcina fiecarui pas nou este mult usurata de activitatea


pasului/pasilor precedenti, ceea ce face ca algoritmul HeapSort sa fie foarte
performant
+
Sortarea Heap - implementare
void schimba(int a[], int i, int j)
{ int aux;
aux= a[i]; a[i] = a[j]; a[j] = aux;
}
void aranjeaza(int a[], int n, int k)
{
int fiuStanga, fiuDreapta, pozMax;
fiuStanga = 2 * k + 1;
fiuDreapta = 2 * k + 2;
pozMax = fiuStanga;

if ((fiuStanga < n) && (a[k] < a[fiuStanga])) pozMax = fiuStanga;


else pozMax = k;
if ((fiuDreapta < n) && (a[fiuDreapta] > a[pozMax]))
pozMax = fiuDreapta;
if (pozMax != k) {
schimba(a, k, pozMax);
aranjeaza(a, n, pozMax);
}
}
+
Sortarea Heap - implementare
void makeHeap(int a[], int n)
{
for (int i = n / 2; i >= 0; i--)
aranjeaza(a, n, i);
}

void heapSort(int a[], int n)


{ makeHeap(a, n);
while (n > 1) {
schimba(a, 0, n - 1);
n--;
aranjeaza(a, n, 0);
}

}
Exemplu de apel:
double *v; int n;
heapSort(v,n);
+
Sortare pe grupe (bucket sort)
◼ Bucket Sort sau sortare compartimentată este un algoritm folosit pentru sortarea unui
vector cu n elemente ale cărui valori sunt uniform distribuite pe un interval [a,b].

◼ Acest interval este împărţit în m compartimente de mărimi egale, apoi se parcurge


vectorul şi fiecare element este aşezat în compartimentul corespunzător.

◼ Paşii acestui algoritm sunt următorii:


◼ Crearea unui vector de compartimente, iniţial vide
◼ Parcurgerea vectorului şi plasarea fiecăreivalori în compartimentul corespunzător
◼ Sortarea fiecărui compartiment (prin diferite metode de sortare)
◼ Concatenarea valorilor din fiecare compartiment

◼ Necesită un tablou auxiliar B[0..9] de liste înlănțuite.


+
Sortare pe grupe - algoritm
ORDONARE-PE-GRUPE(A)

◼ n = lungime[A]

◼ pentru i = 1, n executa

◼ insereaza A[i] în lista B[[nA[i]]]

◼ pentru i = 0, n-1 executa

◼ sorteaza lista B[i] folosind sortarea prin insertie

◼ concateneaza în ordine listele B[0], B[1],...,B[n - 1]

Exemplu:
(a) Tabloul de intrare A[1::10].
(b) Tabloul B[0::9] al listelor (reprezentând grupele)
sortate dupa linia a cincea a algoritmului. Grupa i
cuprinde valorile din intervalul [i=10; (i + 1)=10).
Iesirea sortata consta dintr-o concatenare în ordine a
listelor B [0], B [1] ; :::;B [9].
+
Sortare pe baza cifrelor (radix sort)

◼ Radix Sort este un algoritm de sortare care ţine cont de cifre individuale ale elementelor
sortate.

◼ Există două tipuri de astfel de sortare: LSD (least significant digit) şi MSD (most significant
digit). LSD procesează reprezentările dinspre cea mai puţin semnificativă cifră spre cea mai
semnificativă, iar MSD invers.

◼ O versiune simplă a radix sort este cea care foloseşte 10 cozi (câte una pentru fiecare cifră
de la 0 la 9). Aceste cozi vor reţine la fiecare pas numerele care au cifra corespunzătoare
rangului curent. După această împărţire, elementele se scot din cozi în ordinea crescătoare
a indicelui cozii (de la 0 la 9), şi se reţin într-un vector (care devine noua secvenţă de
sortat).

◼ Exemplu:
◼ Secvenţa iniţială:
◼ 170, 45, 75, 90, 2, 24, 802, 66
◼ 170, 045, 075, 090, 002, 024, 802, 066
+
Sortare pe baza cifrelor (radix sort)

◼ Numere sunt introduse în 10 cozi (într-un vector de 10 cozi), în funcţie


de cifrele de la dreapta la stânga fiecărui număr.
◼ Cozile pentru prima iteraţie vor fi:
◼ 0: 170, 090
◼ 1: nimic
◼ 2: 002, 802
◼ 3: nimic
◼ 4: 024
◼ 5: 045, 075
◼ 6: 066
◼ 7 - 9: nimic

◼ Noua secvenţă de sortat:


◼ 170, 090, 002, 802, 024, 045, 075, 066
+
Sortare pe baza cifrelor (radix sort)

◼ Numere sunt introduse în 10 cozi (într-un vector de 10 cozi), în funcţie


de cifrele de la dreapta la stânga fiecărui număr.
◼ Cozile pentru a doua iteraţie vor fi:
◼ 0: 002, 802
◼ 1: nimic
◼ 2: 024
◼ 3: nimic
◼ 4: 045
◼ 5: nimic
◼ 6: 066
◼ 7: 170, 075
◼ 8: nimic
◼ 9: 090

◼ Noua secvenţă de sortat:


◼ 002, 802, 024, 045, 066, 170, 075, 090
+
Sortare pe baza cifrelor (radix sort)

◼ Numere sunt introduse în 10 cozi (într-un vector de 10 cozi), în funcţie


de cifrele de la dreapta la stânga fiecărui număr.
◼ Cozile pentru a treia iteraţie vor fi:
◼ 0: 002, 024, 045, 066, 075, 090
◼ 1: 170
◼ 2 - 7: nimic
◼ 8: 802
◼ 9: nimic

◼ Vector sortat:
◼ 002,024, 045, 075, 090, 170, 802
+
Sortari - complexitate

Algoritm de
Cel mai bun caz Cazul obișnuit Cel mai rău caz
sortare

Bule 𝑂 𝑛 𝑂 𝑛2 𝑂 𝑛2

Selectie 𝑂 𝑛2 𝑂 𝑛2 𝑂 𝑛2

Inserare 𝑂 𝑛 𝑂 𝑛2 𝑂 𝑛2

Shell 𝑂 𝑛 ≥ 𝑂 𝑛 ∙ log 𝑛 𝑂 𝑛2

Interclasare 𝑂 𝑛 ∙ log(𝑛) 𝑂 𝑛 ∙ log(𝑛) 𝑂 𝑛 ∙ log(𝑛)

Heap 𝑂 𝑛 ∙ log(𝑛) 𝑂 𝑛 ∙ log(𝑛) 𝑂 𝑛 ∙ log(𝑛)

Quick 𝑂 𝑛 ∙ log(𝑛) 𝑂 𝑛 ∙ log(𝑛) 𝑂 𝑛2

Prin numărare 𝑂 𝑛 𝑂 𝑛 𝑂 𝑛

Bucket 𝑂 𝑛+𝑘 𝑂 𝑛+𝑘 𝑂 𝑛2

Radix 𝑂 𝑑∙𝑛 𝑂 𝑑∙𝑛 𝑂 𝑑∙𝑛


+
Sortari - clasificare

Tehnicile de sortare se pot clasifica în mai multe


feluri în funcţie:

• de complexitate;
• de cantitatea de memorie necesară;
• de dificultate.
+
Sortari - clasificare
După cantitatea de memorie utilizată:
a) metode directe (sortează vectorul în spaţiul său de
memorie)
b) metode indirecte (necesită vector auxiliar)

După spaţiul de memorie utilizat:


a) metode interne (ordonează tablouri în memoria internă)
b) metode externe (ordonează sau interclasează fişiere din
memoria externă)

După ordinul de complexitate:


a) metode liniare
b) metode pătratice
c) metode n log2 n
+
Sortari - clasificare

O altă clasificare poate fi făcută după dificultatea


algoritmilor implicaţi:

a) metode simple - se bazează pe algoritmi de dificultate


redusă (inserţie, selecţie, bubblesort).
b) metodele avansate - se bazează pe algoritmi puţin mai
complicaţi, care necesită mici “artificii” pentru a realiza
ordonarea (quicksort, sortarea prin interclasare, heap-
sort), dar care sunt mai eficiente decât cele directe.
+
Pointeri la funcții

◼ Putem declara pointeri către orice tip de date, atât


către tipurile standard, cât şi către tipurile pe care
le definim noi.

◼ Înplus, limbajul C/C++ ne permite să declarăm pointeri


la funcţii.

◼ Numele unei funcţii reprezintă adresa de memorie


la care începe funcţia. Numele functiei este, de fapt, un
pointer la funcţie.
+
Pointeri la funcții

◼ Se poate stabili o corespondenţă între variabile şi funcţii


prin intermediul pointerilor la funcţii.

◼ Ca şi variabilele, aceşti pointeri:


• pot primi ca valori funcţii;
• pot fi transmişi ca parametrii altor funcţii;
• pot fi intorşi ca rezultate de către funcţii.
+
Pointeri la funcții

◼ Ladeclararea unui pointer către o funcţie trebuiesc


precizate toate informaţiile despre funcţie, adică:
• tipul funcţiei;
• numărul de parametrii;
• tipul parametrilor care ne vor permite să apelăm indirect
funcţia prin intermediul pointerului.
+
Pointeri la funcții

◼ Sintaxa de definire: asemănătoare sintaxei de definire a


funcţiilor.

◼ În forma generală, o funcţie se declară astfel:

tip_returnat nume_functie(lista_parametri);

◼ Un pointer la funcţie se declară:

tip_returnat (*nume_variabila_pointer_funcție)(lista_parametri);
+
Pointeri la funcții

◼ Apelul unei funcţii prin intermediul unui pointer are


forma:

(*pf)(listă_parametri_actuali);
+
Pointeri la funcții
#include <stdio.h>
/* Definim doua functii cu doi parametri de tip int si care returneaza un int. */

int o_functie(int a, int b)


{
int calc;
printf("O functie cu doi parametri de tip int: %d si %d.\n", a, b);
calc = a + b;
return calc;
}

int alta_functie(int c, int d)


{
int calc;
printf("Alta functie cu doi parametri de tip int: %d si %d.\n", c, d);
calc = c * d;
return calc;
}
+
Pointeri la funcții
void main()
{
/* Definim un pointer la functii cu doi parametri de tip int si care returneaza int. */

int (*un_pointer) (int y, int z);


int rez;

/* Atribuim pointerului adresa primei functii. */

un_pointer = &o_functie;

/* Invocam prima functie direct. */

rez=o_functie(3, 9);
printf("rez=%d\n", rez);
+
Pointeri la funcții
/* Invocam prima functie prin intermediul pointerului. In loc de numele functiei
folosim operatorul *. */

rez=(*un_pointer) (4, 16);

printf("rez=%d\n", rez);

un_pointer = &alta_functie;

rez=alta_functie(5, 25);

printf("rez=%d\n", rez);

rez=(*un_pointer) (6, 36);

printf("rez=%d\n", rez);
}
+
Pointeri la funcții

◼ Funcția care sortează elementele unui vector

◼ Tipul
sortării se stabilește printr-o funcție care este trimisă
ca parametru funcției de sortare

◼ void sorteaza(float v[], int n, int (*dir)(int, int));


+
Pointeri la funcții

int asc(int a, int b) int desc(int a, int b)


{ {
if (a > b) return 1; if (a < b) return 1;
else return 0; else return 0;
} }
+
Pointeri la funcții
void schimba(int *a, int *b)
{
int aux = *a; *a = *b; *b = aux;
}

void sorteaza(int v[], int n, int (*pf)(int, int))


{
for (int i=0; i < n -1; i++)
{
for (int j = i+1; j < n; j++)
{
if ((*pf)(v[i], v[j])==1)
{
schimba(&v[i], &v[j]);
}
}
}
}
+
Pointeri la funcții

 Apel

void main()

{
int x[10], n;
...
sorteaza(x, n, asc);
sau
sorteaza(x, n, desc);

}
+
Pointeri la funcții.
Metoda bisectiei.
◼ Metoda bisecţiei pentru rezolvarea unei ecuaţii
transcendenete

a sol b
+
Pointeri la funcții

float fct(float x) float fct1(float x)


{ {
return sin(x) - 3 * log(x); return 3*x*x+4*cos(x);
} }
+
Pointeri la funcții

void bisectie(float a, float b, long n, float eps, float(*f)(float), int *


cod, float *sol)
{
*cod = 0;
if ((*f)(a)*(*f)(b) <= 0)
while ((n) && (*cod == 0))
{
*sol = (a + b) / 2;
if ((*f)(*sol) == 0) *cod = 1;
if (fabs(a - b) <= eps) *cod = 2;
else if ((*f)(*sol)*(*f)(a)<0) b = *sol;
else a = *sol;
n--;
}
}
+
Pointeri la funcții

int bisectie1(float a, float b, long n, float eps, float (*pf)(float),


float* sol)
{
if (n > 0)
{
*sol = (a + b) / 2;
if ((*pf)(*sol) == 0) return 1;
if (fabs(a - b) <= eps) return 2;
else
if ((*pf)(*sol) * (*pf)(a) < 0)
return bisectie1(a, *sol, n - 1, eps, pf, sol);
else
return bisectie1(*sol, b, n - 1, eps, pf, sol);
}
+
Pointeri la funcții

void main()
{
float a, b, eps, x;
int cod;
long n;
float(*functie)(float);
printf("captele intervalului");
scanf("%f %f", &a, &b);
printf("eroarea admisa:");
scanf("%f", &eps);
printf("numarul maxim de iterati:");
scanf("%li", &n);
functie = fct;//functie = fct1;
bisectie(a, b, n, eps, functie, &cod, &x);
if (cod == 0) printf("nu se poate calcula solutia aproximativa");
else printf("solutia aproximativa este:%5.2f", x);
/*cod = bisectie1(a, b, n, eps, functie, &x);
if (cod == 0) printf("nu se poate calcula solutia aproximativa");
else printf("solutia aproximativa este:%5.2f", x);*/
}
+
Bibliografie

◼ I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, Programarea calculatoarelor. Ştiinţa învăţării
unui limbaj de programare, Teorie şi aplicaţii, Ed. ASE,
2003

◼ I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, M. Mircea, Programarea calculatoarelor.
Algoritmi în programare, Ed. ASE Bucureşti, 2007

◼ C. Uscatu, M. Popa, L. Bătăgan, C. Silvestru, Programarea


Calculatoarelor. Aplicații, Ed. ASE, 2012

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