Sunteți pe pagina 1din 21

40

2. Algoritmi de sortare (clasici)



n acest capitol vom prezenta civa algortimi de sortare (ordonare) dintre cei mai reprezentativi
i mai des utilizai.

2.1. Sortare prin selecie direct
Aceast metod se bazeaz pe urmtorul principiu, de exemplu pentru ordonare cresctoare:
1. Se selecteaz termenul cu cea mai mic valoare.
2. Se schimb acesta cu primul termen (a
i
).
Apoi se repet operaiile cu cele n-1 elemente rmase, i aa mai departe pn rmne un singur termen
(cel mai mare). Deci, pentru sortarea cresctoare a unui ir, algoritmul este urmtorul:
for (i = 1; i < n-1; i++)
{
* se determin indicele celui mai mic element, k, dintre a[i], . . . ,a[n];
* se interschimb a[i] cu a[k];
}
Iat ntreg programul care sorteaz prin selecie direct:
/* sel_dir.c - programul ordoneaza un vector prin selectie directa,
utilizand pentru aceasta determinarea minimului si a pozitiei lui,
la fiecare iteratie si realizand o singura inversiune intre acest
minim si valoarea a[i], corespunzatoare iteratiei curente;
se determina si numarul de comparatii si inversiuni realizate */
#include<stdio.h>
#include <conio.h>
int ncomp=0, ninv=0; /* numar de comparatii si inversiuni */
void sort_sel_direct ( double a[], int n )
/* functia sorteaza un vector prin metoda de selectie directa */
{
double x;
int i, j, k;
for ( i = 0 ; i < n-1 ; ++i )
{
k = i; /* initializare indice, k, si elementul minim, x */
x = a[i];
for ( j = i+1 ; j < n ; ++j, ncomp++ ) /* determinare minim si indicele lui din sir */
if (a[j] < x)
{
k = j;
x = a[k];
}
a[k] = a[i]; /* interschimbare minim cu primul din subsirul sursa, */
a[i] = x; /* adica cu primul din subsirul neordonat */
ninv++;
}
}
void main(void)
{
double sir[100];
41
int ne,i, nl=0;
clrscr();
printf("Numar elemente:");
scanf("%d",&ne);
for(i=0;i<ne;i++) /* citirea elementelor sirului de ordonat */
{
printf("sir(%d)=",i+1);
scanf("%lf",& sir[i]);
}
sort_sel_direct(sir,ne); /* ordonarea sirului prin selectie directa */
for(i=0;i<ne;i++) /* afisarea sirului ordonat */
{
printf(" sir(%2d)=%5.1lf",i+1,sir[i]);
nl++; /* actualizare contor numar de valori afisate pe o linie */
if ( nl % 5 == 0 ) /* daca s-au afisat 5 valori pe o linie se trece pe */
printf("\n"); /* o linie noua */
}
printf("\n");
printf("Ordonarea s-a realizat prin %d comparatii si %d inversiuni\n",
ncomp, ninv);
}


2.2. Sortare prin inserare direct.
Aceast metod este utilizat de juctorii de cri. Elementele irului (deci, crile de joc) sunt
mprite n dou subiruri:
- un subir destinaie a
1
, a
2
, a
3
, . . ., a
i-1
, care este ordonat pe msura crerii lui, i
- un subir surs a
i
, a
i+1
, a
i+2
, . . ., a
n
;
Iniial irul destinaie, deci cel ordonat, este format doar din primul element al irului, a
1
. La
fiecare pas (iteraie), ncepnd cu i = 2, elementul i din irul surs este luat din acesta i transferat n
irul destinaie prin inserare pe poziia corespunztoare, dup compararea cu elementele irului
destinaie.
irul destinaie este parcurs de la dreapta la stnga, deoarece la fiecare comparaie, dac
elementul de inserat, cel din irul surs, nu trebuie inserat pe poziia respectiv n irul destinaie,
elementul cu care s-a comparat, din irul destinaie, este deplasat cu o poziie la dreapta pentru a face
loc noului element, de introdus n irul destinaie. Din cazurile particulare, adic situaiile n care noul
element trebuie inserat pe prima sau pe ultima poziie, rezult, n mod evident, aceast condiie.
Deci algoritmul este urmtorul:
for ( i = 1; i < n ; i++){
x = a [i]; /* elementul curent de inserat; */
se insereaz x pe poziia corespunztoare n a
1
, a
2
, a
3
, . . ., a
i-1
,
parcurgnd acest subir de la dreapta la stnga
}
Programul pentru sortarea prin inserare direct este prezentat n continuare.

/* ins_dir.c prog. ordonare cresc. sir, utilizand metoda de inserare directa, adica:
se considera un sir ordonat, format initial din primul element din sirul initial,
si un sir neordonat, format din restul elementelor sirului, din care se iau pe
42
rand celelalte elemente si li sa cauta, de la dreapta la stanga, pozitia in
sirul ordonat, construit tot in sirul initial. */
#include <stdio.h>
#include <conio.h>
int ncomp=0, nins=0; /* numar de comparatii si inserari/ deplasari */
void sort_ins_direct (double a[], int n)
{/* functia de sortare prin inserare directa */
double x;
int i, j;
for (i=1; i<n; ++i)
{
x=a[i]; /* elementul curent de inserat */
j=i-1; /* dimensiunea sirului destinatie */
while (x < a[j] && j >= 0)
{ /* se cauta pozitia de inserare */
a[j+1]=a[j];
j--;
ncomp++; nins++;
}
a[j+1]=x; /* inseare element curent */
}
}
void main()
{
double sir[100];
int ne, i;
clrscr();
printf("Numar elemente:");
scanf("%d", &ne);
for(i=0; i<ne; i++) /* citirea elementelor sirului de sortat */
{
printf("sir(%d)= ", i+1);
scanf("%lf", &sir[i]);
}
sort_ins_direct (sir, ne); /* ordonarea sirului prin inserare directa */
printf("\n Sirul ordonat:\n");
for(i=0; i<ne; i++) /* afisare sir ordonat */
{
printf(" sir(%d)=%5.2lf ", i+1, sir[i]);
if ( (i+1) %5 == 0 ) /* daca s-au afisat 5 valori din sir pe o linie */
printf("\n"); /* se trece pe o linie noua */
}
printf("\n");
printf("Ordonarea s-a realizat prin %d comparatii si %d deplasari\n",
ncomp, nins);
}


43
2.3. Sortare prin inserare binar
O metod mai rapid pentru determinarea poziiei de inserare a elementului curent din irul
surs (a
i
) n irul destinaie, pentru algoritmul anterior este cutarea acestei poziii prin metoda binar.
Aceast cutare binar const n a mpri irul destinaie n dou subiruri de dimensiuni egale
(deci se njumtete irul destinaie) i se continu progresiv aceast operaie pe subirul destinaie
cruia i aparine elementul de inserat, pn se identific aceast poziie. Vom nota cu s respectiv d
indicii de nceput (stnga) i sfrit (dreapta) ai subirului n care se continu cutarea, iar cu m vom
nota indicele elementului din mijlocul subirului.

/* ins_bin.c - prog. de ordonare cresc. sir, utilizand metoda de inserare binara,
adica: se considera un sir ordonat, format initial din primul element din
sirul initial, si un sir neordonat, format din restul elementelor sirului,
din care se iau pe rand celelalte elemente si li se cauta, de la dreapta
la stanga, pozitia in sirul ordonat, construit tot in sirul initial.
Cautarea se face utilizand metoda "binara". */
#include<stdio.h>
#include <conio.h>
int ncomp=0, nins=0; /* numar de comparatii si inserari/deplasari */
/* functia de sortare prin inserare binara */
void sort_ins_binara ( double a[], int n )
{
double x;
int i, j, s, d, m;
for (i=1; i<n; ++i)
{
x=a[i]; s=0; d=i-1;/* initializari: x-elementul de inserat */
while (s <= d) /* s, d-limitele stanga/dreapta pentru subsir dest */
{ncomp++;
m=(s+d)/2; /* mijlocul subsirului */
if (x < a[m]) /* in functie de apartenenta elementului x */
d=m-1; /* la subsirul din stanga sau dreapta */
else /* se stabileste noua limita din stanga/dreapta */
s=m+1; /* a noului subsir, in care se cauta */
} /* dupa determinarea pozitiei de inserare a elem.*/
for (j=i-1; j>=s; --j, nins++) /* se deplaseaza tot sirul cu */
a[j+1]=a[j]; /* o pozitie la dreapta, de la pozitia */
a[s]=x; /* de inserare pana la sfarsit, dupa care */
/* se insereaza noul element, x */
}
}
void main(void){
double sir[100]; int ne,i;
clrscr();
printf("Numar elemente:");
scanf("%d", &ne);
for(i=0; i<ne; i++) /* citire sir de ordonat */
{
printf("sir(%d)= ", i+1);
44
scanf("%lf", &sir[i]);
}
printf("Sirul ordonat este: \n");
sort_ins_binara(sir , ne); /* apelul functiei de sortare binara */
for (i=0;i<ne;i++) /* afisare sir ordonat */
{
printf(" sir (%2d) = %lf \n", i+1, sir[i]);
if ((ne+1)%4==0) /* tiparesc cate 4 valori pe linie */
printf("\n");
}
printf("\n");
printf("Ordonarea s-a realizat prin %d comparatii si %d deplasari\n", ncomp, nins);
}


2.4. Sortare prin metoda inversiunilor (metoda bulelor)
Aceast metod const n compararea primei valori cu toate celelalte din vector, i
interschimbarea (inversarea) lor dac este cazul, adic dac nu sunt ordonate. Dup prima parcurgere
valoarea minim (sau maxim, depinde de sortarea dorit, cresctoare sau descresctoare) va ajunge n
prima poziie. La parcurgerea urmtoare se consider subirul ncepnd de la al doilea element care va
fi comparat i inversat, dac este cazul, cu elementele rmase (n-2). La ultima parcurgere se vor
compara ultimele dou elemente din ir, deci n total sunt n-1 parcurgeri. Deci algoritmul este
urmtorul:
for ( i = 1; i < n ; i++ )
for ( j = n; j >= i ; i-- )
if (a[j-1] > a[j])
{x = a[j-1];
a[j-1] = a[j];
a[j] = x;}

n continuare este prezentat ntregul program ce ordoneaz un ir de valori utiliznd aceast
metod.
/* sortbule.c - programul ordoneaza un sir de valori prin
metoda inversiunilor (bulelor) - forma clasica */
#include <stdio.h>
#include <conio.h>
int ncomp=0, ninv=0; /* numar de comparatii si inversiuni/ deplasari */
void sort_met_bulelor (double a[], int n)
/* functia sorteaza un vector prin metoda inversiunilor (bulelor) */
{double x;
int i, j;
for (i=1; i<n; i++)
for (j=n-1; j>=i; j--, ncomp++)
if (a[j-1] > a[j])
{
x=a[j-1];
a[j-1]=a[j];
a[j]=x;
45
ninv++;
}
}
void main(){
double sir[100];
int ne,i, nl=0;
clrscr();
printf("Numar elemente:");
scanf("%d", &ne);
for(i=0; i<ne; i++) /* citirea elementelor sirului de ordonat */
{printf("sir(%d)=",i+1);
scanf("%lf", &sir[i]);
}
sort_met_bulelor (sir, ne); /* ordonarea sirului prin selectie directa */
for(i=0; i<ne; i++) /* afisarea sirului ordonat */
{
printf("sir(%2d)=%5.1lf ", i+1, sir[i]);
nl++; /* actualizare contor numar de valori afisate pe o linie */
if (nl % 5 == 0) /* daca s-au afisat 5 valori pe o linie */
printf("\n"); /* se trece pe o linie noua */
}
printf("\n");
printf("Ordonarea s-a realizat prin %d comparatii si %d deplasari\n",
ncomp, ninv);
}

Dezavantajele acestei metode:
- indiferent de ordinea din ir acesta este parcurs tot de n-1 ori, de fiecare dat de la i
la n, cu i parcurgnd intervalul 2,n-1, cu toate c dac la o parcurgere (deci pentru
un i oarecare) nu a aprut nici o modificare (inversiune), nu mai trebuie reluat
parcurgerea pentru urmtorul i.
- se poate memora poziia pn la care subirul format (obinut) este ordonat, deci i-ul
la care a avut loc ultima inversiune. Dac se memoreaz aceast valoare a lui i ntr-o
variabil k , la care s-a fcut ultima inversiune, rezult, n mod evident, c subirul
format din elementele a
1
, a
2
, a
3
, . . ., a
k
, este ordonat, deci nu mai este necesar s fie
parcurs i la urmtoarele treceri.
- ultima observaie se refer la urmtorul fapt:
- dac valoarea minim se afl pe ultima poziie, iar restul irului este ordonat, dintr-o
singur parcurgere se va ordona ntregul ir;
- dac ns pe prima poziie se afl vaoarea maxim, iar restul irului este ordonat,
vor fi necesare n-1 parcurgeri ale irului pentru a-l ordona.
- din aceast ultim observaie se constat c direcia de parcurgere a irului poate
scurta numrul de parcurgeri ale irului i numrul de comparaii, deci poate scurta
durata de ordonare. De aici rezult i a 3-a mbuntire a acestui algoritm:
alternarea direciilor de parcurgere la etape succesive.

Algoritmul rezultat (cu aceste mbuntiri) este denumit ShakerSort i este prezentat n continuare.

46
d=n-2; s=0; k=d; /* initializari pentru limita din stanga (s), dreapta (d) */
do /* iar k reprezint pozitia la care s-a realizat ultima inversiune */
/* ncomp, ninv numar de comparatii, inversiuni realizate */
{for (i=s; i<=d; i++, ncomp++) /* parcurgerea de la stnga la dreapta */
if (v[i]>v[i+1]) /* dac este cazul se inverseaza elementele neordonate */
{t=v[i];
v[i]=v[i+1];
v[i+1]=t;
k=i; /* se reine pozitia ultimei inversiuni */
ninv++;}
d=k-1; /* se modifica corespunzator limita din dreapta */
for (i=d; i>=s; i--, ncomp++) /* parcurgerea de la dreapta la stnga */
if (v[i]>v[i+1]) /* dac este cazul se inverseaza elementele neordonate */
{t=v[i];
v[i]=v[i+1];
v[i+1]=t;
k=i; /* se reine pozitia ultimei inversiuni */
ninv++;}
s=k+1; /* se modifica corespunzator limita din stanga */
}while(s <= d);

Acest algoritm este mai rapid dect sortrile prin metoda inserrii directe sau cel al seleciei.

Sortarea unui vector, ce conine indicii vectorului iniial (de sortat)
n cazul n care datele care trebuie ordonate sunt reprezentate pe foarte muli octei, cum este
cazul unor structuri largi, atunci este de preferat, indiferent de algoritmul de sortare utilizat, s nu se
realizeze inversiunile (ordonarea) ntre componentele vectorului respectiv (tab[], ca n exemplul
urmtor), deoarece vor necesita mult timp de execuie pentru inversarea lor, ci s se ordoneze un vector
ce conine indicii componentelor din vectorul iniial (index[]); binneles, comparaiile necesare
ordonrii se vor face ntre valorile cheie (info) dup care se face ordonarea, avnd, ns, ca indice
indicele din tabloul de indici care se ordoneaz (adresare indirect). Iniial acest tablou (index) va
conine valorile indicilor tabloului de ordonat (de fapt aceste valori sunt iniial 0, 1, 2, 3, ). Indiferent
de metoda de sortare utilizat ordonarea se va face dup o anumit cheie (informaie coninut n
componentele tabloului, n exemplul urmtor aceasta este numit medie) asupra tabloului de indici
(index), care va fi utilizat ca indici pentru tabloul iniial (tab[index[j]]). Se va utiliza o adresare
indirect, adic indicii tabloului ce trebuie sortat vor fi luai din tabloul de indici (tab[index[j]]), iar
sortarea se va face asupra acestuia i nu direct asupra tabloului iniial. Deci, n final, tabloul iniial, cel
de sortat (tab), va rmne neschimbat, dar tabloul cu indici (index), ai tabloului de ordonat, va fi
ordonat dup valorile (cmpurile) pe care acesta le selecteaz din tabloul iniial. Vom exemplifica
aceast metod de ordonare, des utilizat, a tabloului de indici asociat tabloului de valori, utiliznd
algoritmul de sortare shakersort.
/* sortindx.c - se sorteaza indecsii unui tablou de tip structura,
care contine date pentru un numar de studenti (nume, prenume si media)
asociat acestui tablou definim un tablou cu indecsii tabloului de studenti;
vom sorta studentii dupa medii prin intermediul tabloului de indecsi,
iar sortarea indicilor se va face utiliznd algoritmul ShakerSort */
#include <stdio.h>
#define DIM 100
47
#include <conio.h>
struct tip_struct
{
char nume_prenum [DIM];/* numele si prenumele unui student*/
float medie;
};

void main(void)
{
struct tip_struct stud [100];/* consideram un tablou de studenti */
int ind[100]; /* vectorul ce contine indecsii tabloului initial, si ce
va fi ordonat in functie de valorile vectorului de ordonat "tab" */
int i, ns;
float f;
void ShakerSort (struct tip_struct tab[], int index[], int n);
clrscr();
printf("Numarul de studenti:");
scanf("%d", &ns);
f=0.0; fflush(stdin);
for (i=0; i<ns; i++) /* citire date studenti: nume, prenume si media */
{
printf("Numele si prenumele studentului %d: ", i+1);
gets(stud[i].nume_prenum);
printf("Media notelor sale: ");
scanf("%f", &f);
stud[i].medie=f;
while (getchar() != '\n');
}
for (i=0; i<ns; i++) /* initializarea tabloului de indici */
ind[i] = i;
ShakerSort (stud, ind, ns); /* sortarea vectorului */
printf("Lista ordonata cu numele / prenumele si media studentilor\n");
for (i=0; i<ns; i++)
printf("%s %.2f\n", stud[ind[i]].nume_prenum,
stud[ind[i]].medie);
}
void ShakerSort (struct tip_struct tab[], int index[], int n)
{
int i, s, d, k, t;
d=n-2; s=0; k=d;
do {
for (i=s; i<=d; i++)
if (tab[index[i]].medie < tab[index[i+1]].medie)
{
t=index[i];
index[i]=index[i+1];
index[i+1]=t;
k=i;
}
48
d=k-1;
for (i=d; i>=s; i--)
if (tab[index[i+1]].medie > tab[index[i]].medie)
{
t=index[i];
index[i]=index[i+1];
index[i+1]=t;
k=i;
}
s=k+1;
} while (s <= d);
}

Pentru testare se poate simplifica programul considernd, pentru aceasta, un vector de valori
numerice i n acest caz dispare, din exemplul anterior, cmpul info, deci comparaiile respective vor fi
de forma urmtoare: if (tab[index[i]] < tab[index[i+1]]), respectiv if (tab[index[i+1]] >
tab[index[i]]).


2.5. Sortare prin metoda Shell
Ordonarea unui vector utiliznd algoritmul Shell (Donald Shell) are avantajul c se accelereaz
ordonarea prin parcurgerea vectorului cu un pas variabil, pentru a reduce dezordinea irului .
Fiecare etap realizeaz inversiuni (n sensul ordonrii), ntre elementele aflate la o anumit
distan (pas). Pasul iniial este egal cu jumtate din numrul de elemente, n/2; la fiecare parcurgere
cu un anumit pas, se iniializeaz o variabil logic, care e modificat doar dac apar inversiuni, cel
puin ntre dou elemente .
Dac la sfritul unei etape variabila logic a fost modificat, deci au existat inversiuni, se reia
parcurgerea cu acelai pas, pn cnd nu se mai realizeaz inversiuni, deci variabila logic nu mai
este modificat. Astfel dup fiecare etap numerele mai mici se apropie de nceputul vectorului i cele
mari de sfritul su.
Dup fiecare parcurgere, cnd nu au mai aprut inversiuni, se reia parcurgerea cu un pas
njumtit. Algoritmul i-a sfrit cu etapa n care pasul, prin njumtiri succesive, devine 1, ceea ce
asigur i convergena algoritmului (se ajunge la varianta mbuntit a metodei bulelor).
/* SortShel .c - programul ordoneaza un vector cu metoda Shell */
#include <stdio.h>
#include <conio.h>
#define N_MAX 100
typedef double VECTOR [N_MAX];
int ncomp=0, ninv=0; /* numar de comparatii, inversiuni */
void Sort_Shell (VECTOR v, int dim){
int i, pas, inv;
double a;
pas = dim;
while (pas >1 )
{
pas=pas/2 ;
do
{
49
inv=0 ; /* fals */
for (i=0; i < dim-pas; i++)
{ncomp++;
if (v[i] > v[i+pas])
{
a=v[i+pas];
v[i+pas]=v[i];
v[i]=a;
inv=1;
ninv++;
}
}
} while (inv);
}
}
void main (void)
{VECTOR sir, a;
int n, i, j , inv ;
clrscr();
printf ("Numarul de elemente ale sirului (<=100): " ) ;
scanf ("%d", &n) ;
for (i=0; i<n; ++i)
{
printf ("sir[%2d]= ", i+1);
scanf ("%lf", &sir[i]) ;
}
Sort_Shell(sir, n);
printf("\nVectorul ordonat este:\n");
for (i=0; i<n; ++i){
printf ("sir[%2d]=%5.2lf ", i+1, sir[i]);
if ((i+1)%4==0)
printf("\n");
}
printf("\nSortarea s-a realizat dupa %d comparatii si %d inversiuni\n", ncomp, ninv);
getch();
}


2.6. Sortare partajat (QuickSort)
Acest algoritm mai este denumit i algoritm de sortare rapid (QuickSort), autorul su fiind
Hoare.
Metoda are la baz faptul c interschimbrile ar fi de preferat s se realizeze peste distane ct
mai mari pentru a accelera sortarea, fiind astfel mult mai eficient.
Algoritmul este urmtorul: se alege un termen la ntmplare (de obicei cel din mijloc, pe care-l
vom nota x); parcurgem (scanm) vectorul de la stnga la dreapta pn se gsete un termen a[i] > x i
apoi se parcurge invers, de la dreapta la stnga, pn se gsete un termen a[j] < x. Acum se schimb
ntre ei cei doi termeni i se continu aceast scanare i interschimbare pn cnd cele dou direcii
de scanare se ntlnesc undeva la mijloc.
50
Rezultatul dup aceast parcurgere este un ir care conine n partea stng, fa de x, elemente
mai mici dect x, iar partea dreapt conine elemente mai mari dect x; x acioneaz ca o santinel
pentru ambele direcii de scanare. Deci irul este mprit n dou dup valoarea lui x n dou partiii,
una cu valori mai mici dect x, iar cealalt cu valori mai mari.
S considerm acelai exemplu numeric, adic vectorul (format din 8 numere):
36, 57, 13, 46, 88, 19, 76, 8, pentru care alegem santinela 46
parcurgndu-l de la stnga la dreapta gsim 57 > 46 (i=2), iar de la dreapta la stnga gsim 8<46,
(j=8), pe care le vom interschimba:
36, 8, 13, 46, 88, 19, 76, 57,
n continuare, de la stnga la dreapta vom ajunge la 46 (i=4), iar de la dreapta la stnga vom ajunge la
19 (j=6), care se vor interschimba:
36, 8, 13, 19, 88, 46, 76, 57,
i apoi se ajunge la 88 (i=5) i la 46 (j=4) i ntruct i>j nu mai are loc interschimbarea, se termin
prima scanare (partiie), adic avem n prima jumtate valori mai mici dect 46, iar n cea de-a doua
mai mari (sau egale cu santinela). Se continu procesul de scanare i partajare n cei doi subvectori, (1,
4), respectiv (5, 8), n acelai mod.
Scopul nostru nu este de a gsi partiii ale vectorului ci de a-l sorta. Dup prima scanare se reia
procesul de scanare asupra celor dou partiii i apoi asupra partiiilor astfel obinute, i aa mai departe
pn cnd o partiie const dintr-un singur element, moment n care se va obine sortarea vectorului.
Deci funcia QuickSort este recursiv i este descris n continuare.

QuickSort ( int s, int d)
{ /* limitele partiiei la stnga (s) i la dreapta (d) */
i = s; j=d; /* iniializarea celor dou scanri (I) i respectiv(j) */
x = a [(s +d)/2 ]; /* santinela din mijlocul partiiei curente */
do {
while (a[i] < x) i++;
while (a[j] > x) j--;
if (i <= j) /* se schimb ntre ele cele dou elemente */
{
t = a[i]; a[i] = a[j]; a[j] = t;
i++; j--;
}
}
while (i <= j); /* i se continu pn se ntlnesc cele dou scanri */
/* s-a mprit irul iniial n dou subiruri (partiii), primul cu elemente mai mici dect x, iar cel dex,
iar cel de-al doilea cu ele,ente mai mari */
if ( s < j ) QuickSort ( s , j ); /* dup care se continu scanarea celor dou partiii */
if ( i < d ) QuickSort (i , d ); /* astfel obinute, pn se apeleaz o partiie de 1 element */
}
iar apelul funciei se va face sub forma: QuickSort ( 1 , n ).
ntreg programul de ordonare utiliznd algoritmul QuickSort este prezentat n continuare.

/* QuickSor.c - sortarea unui vector utilizand algoritmul QuickSort */
#include<stdio.h>
#include<conio.h>
#define DIM 100
typedef int VECTOR[DIM];
51
int ncomp=0, ninv=0; /* numar de comparatii, inversiuni */
void cit_vect(int n, VECTOR v)
{int i;
for(i=0; i<n; i++)
{printf("v[%d]=", i+1);
scanf("%d", &v[i]);}
}
void scrie_vect(int n, VECTOR v)
{int i;
for(i=0; i<n; i++)
printf("v[%d]=%d\n", i+1, v[i]);
}
void QuickSort(VECTOR a, int s, int d)
{int i, j, m, x, temp;
i=s;
j=d;
m=(s+d)/2;
x=a[m];
do {while (a[i]<x) i++, ncomp++;
while (a[j]>x) j--, ncomp++;
if (i<=j)
{temp=a[i];
a[i]=a[j];
a[j]=temp;
i++;
j--;
ninv++;
}
}while(i<=j);
if(s < j) QuickSort(a, s, j);
if(i < d) QuickSort(a, i, d);
}
void main(void)
{int n;
VECTOR v;
clrscr();
printf("Programul ordoneaza (sorteaza) un vector cu alg. QuickSort.\n");
printf("Dimensiune vector, n=");scanf("%d", &n);
cit_vect(n, v);
QuickSort(v, 0, n-1);
printf("\nVectorul ordonat este:\n");
scrie_vect(n, v);
printf("Sortarea s-a realizat dupa %d comparatii si %d inversiuni\n",
ncomp, ninv);
getch();
}

52
Transformarea acestui algoritm ntr-unul iterativ const n pstrarea unei liste a cererilor de
partiionare ce trebuie s fie realizate. Dup fiecare pas (scanare), dou taskuri de partiionare trebuie
rezolvate (unul pentru partiia stnga i altul pentru partiia dreapta). Doar unul dintre ele poate fi atacat
direct de iteraia urmtoare (i anume partiia stnga), cellalt (partiia dreapta) fiind pus n stiv (adic
n acea list). Lista aceasta a cererilor este obinut n ordine invers, deci lista este tratat ca o stiv.
Fiecare cerere este reprezentat simplu printr-un index stnga i unul dreapta ce specific limitele
partiiei ce trebuie partiionate ulterior.

void QuickSortit (VECTOR a, int n)
/* Sortara partajata (QuickSort) realizata iterativ */
{int s, d, i, j, is, x, t;
struct st_stiva {int s, d;}; /* element stiva: pozitia stanga, dreapta */
struct st_stiva stiva[DIM]; /* stiva ce contine lista de cereri partitie */
is=1; stiva[is].s=0; stiva[is].d=n-1; d=n-1; /* initializari */
do{ s=stiva[is].s; d=stiva[is].d; is--; /* partitia curenta de realizat*/
do {i=s; j=d; /* este luata din varful stivei */
x=a[(s+d)/2]; /* santinela */
do {
while (a[i]<x) i++, ncomp++;
while (a[j]>x) j--, ncomp++;
if (i<=j)
{t=a[i];
a[i]=a[j];
a[j]=t;
i++; j--;
ninv++;
}
} while(i<=j);
if (i<d){ /* depun in stiva cererea de a sorta */
is++; /* partitia dreapta */
stiva[is].s=i;
stiva[is].d=d;
}
d=j; /* se continua sortarea partitiei stanga, (s, d) */
} while (s<d); /* pana are un singur element (s==d) */
}while (is>0); /* se reia sortarea pentru partitiile stanga */
} /* ramase nerezolvate, in stiva */


2.7. Sortare prin metoda HeapSort
Arbori de selectie (heap)
Metoda de sortare prin selecie direct se bazeaz pe selecia repetat a ultimei chei dintre n
elemente, apoi dintre n-1 elemente rmase, etc. Pentru a gsi cea mai mic cheie dintre n elemente sunt
necesare n-1 comparaii, iar gsirea ei ntre n-1 elemente are nevoie de n-2 comparaii, etc. Aceast
sortare se poate mbunti prin reinerea de la fiecare scanare de mai mult informaie dect
identificarea unui singur element, cel mai mic. De exemplu, cu n/2 comparaii se poate determina cheia
mai mic pentru fiecare pereche de elemente, dintre cele n elemente, apoi cu alte n/4 comparaii se
poate determina cheia cea mai mic pentru fiecare pereche ale cheilor determinate anterior, i aa mai
53
departe. Astfel cu n-1 comparaii se poate construi arborele de selecie, rezultat n urma algoritmului
descris anterior, de exemplu pentru cheile 36, 57, 13, 46, 88, 19, 76, 8, astfel:














Cel de-al doilea pas const acum n a cobor n jos de-a lungul cii marcate de cea mai mic
cheie, eliminarea ei succesiv i nlocuirea ei printr-un ptrat gol.














Acum putem elimina din arbore urmtoarea cheie, cea mai mic dintre cele rmase.
Dup n astfel de pai de selecie, arborele este gol (plin cu ptrate goale) i procesul de sortare este
terminat.














8
13 8
13 36 19 8
57 13 46 36 88 19 76 8

13
13 36 19
57 13 46 36 88 19 76



13
13 19
13 36 19 76
57 13 46 36 88 19 76
54

De notat c fiecare din cei n pai de selecie necesit numai log
2
n comparaii. Deci, ntregul
proces de selecie va necesita numai n*log
2
n operaii elementare, pe lng cei n pai necesari pentru
construirea arborelui. Este o mbuntire semnificativ fa de selecia direct (care necesit n
2
pai) i
chiar fa de metoda Shellsort (care necesit n
1.2
pai).
n continuare vom cuta o reprezentare a arborelui cu n elemente n n elemente de memorare, n
loc de 2n-1 uniti cum a fost prezentat anterior. Aceast metod este cunoscut sub numele de
Heapsort, dat de imventatorul su, J. Williams.
Un heap este definit ca o secven de chei:
h
l
, h
l+1
,. . . , h
r

astfel nct h
i
h
2i

h
i
h
2i+1

pentru toi i = l, . . ., r/2
Dac un arbore binar este reprezentat ca un vector, ca n figura urmtoare, atunci el urmrete
acest arborii de selecie din figurile urmtoare, ce reprezint heap-uri, i n particular elementul h
l
al
heap-ului este cel mai mic element (h
l
= min (h
l . . .
h
n
)).




























Heap cu 7 elemente


h
1
h
2
h
3

h
5
h
4
h
6
h
7

h
9
h
10
h
11
h
8
h
12
h
13
h
14
h
15

h
1
46 8
88 57 19 13
55











Cheia 36 se cerne prin heap

S presupunem c un heap cu elementele h
l+1
,. . . , h
r
este dat pentru anumite valori l i r, i un
nou element x trebuie s fie adugat pentru a realiza heap-ul extins, h
l
,. . . , h
r
. Pentru heap-ul iniial h
1
,.
. . , h
7
, extindem heap-ul la stnga, de exemplu, cu un element h
l
=36 (figura anterioar, cea cu h
l
n
vrful arborelui).
Un nou heap este obinut punnd, mai nti x n capul structurii arborelui i apoi lsndu-l s se
cearn n jos de-a lungul cii cu comparanzi mai mici, care n acelai timp se vor muta n sus. Pentru
exemplul anterior, valoarea 36 este mai nti interschimbat cu 8, apoi cu 13, i apoi formeaz arborele
anterior.
Vom formula algoritmul de cernere astfel: i, j sunt perechi de indici ce denot elementele ce
trebuie interschimbate pe durata fiecrui pas de cernere. Un mod simplu de a construi un heap a fost
sugerat de Floyd. El utilizeaz algoritmul de cernere descris n continuare.
Se construiete vectorul iniial i apoi se face cernerea dup regula enunat anterior (i=l, j=2i):
- se salveaz elementul i, a[i] -> x;
- ct timp j <= r (deci nu s-a parcurs tot vectorul, se reia secvena urmtoare):
- dac j < r, se determin indicele j al valorii minime dintre a[j] i a[j+1];
- dac x <= a[j], nseamn c x (a[i]) este minimul dintre a[j] i a[j+1] i s-a terminat
determinarea minimului (nu se mai cerne n jos);
- dac x > a[j], atunci se depune minimul la a[i] <- a[j] i se continu pe ramura
respectiv a arborelui, i=j, j=2i (se continu cernerea n jos);
- se pune n a[i] <-x (se cerne n jos valoarea mai mare).
void cerne (double a[], int l, int r)
{int i,j;
double x;
i=l; j=2*i; x=a[i]; /* x este valoarea ce se cerne prin arbore/ vector */
while (j<=r){
if (j<r) /* nu s-a ajuns la capat */
if (a[j]>a[j+1]) /* j indice valoare mai mica din */
j++; /* perechea (2*i, 2*i + 1) */
if (x<a[j]) /* daca x e mai mic decat valoarea respectiva */
goto gata; /* se pune in pozitia i, a[i]<a[2i], si a[i]<a[2i+1] */
a[i]=a[j]; /* daca nu, se muta val. mai mica a[j] la a[i] */
i=j; j=2*i; /* si se continua cernerea pe nivelul urmator */
} /* noul i devine j (vechiul i), iar j=2i, dar numai daca j<r */
gata: a[i]=x;
}
8

46 13
88 57 19 36
56

Se consider un vector h
l
,. . . , h
n
, n care este evident, elementele h
n/2
,. . . , h
n
formeaz deja un
heap, deoarece nu exist doi indici i, j, astfel nct j=2i (sau j=2i+1). Aceste elemente formeaz ceea
ce poate fi considerat ca ultimul rnd al arborelui binar asociat (figura cu arborele binar h
l
. . .h
n
), ntre
care nu exist nici o relaie de ordonare. Heap-ul este acum extins spre stnga, unde n fiecare pas un
nou element este inclus i poziionat adecvat, printr-o cernere. Acest proces este ilustrat n figura
(tabela) de construire a unui heap.
Pentru a genera un heap de n elemente h
1
,. . . , h
r
, se reia operaia de cernere pentru toate
elementele h
1
,. . . , h
r/2
, ncepnd de la l=r/2 spre stnga i terminnd cu l=l (descris de ):
l = (n % 2) + 1;
while ( l > 1 ) {
l--;
cerne (l, n);
}
Dup generarea acestui heap, este clar c pe prima poziie se afl valoarea minim.

36 57 13 46 88 19 76 8 , x=46, l=4
36 57 13 8 88 19 76 46 , x=13, l=3
36 57 13 8 88 19 76 46 , x=57, l=2
36 8 13 57 88 19 76 46 , x=57, l=4
36 8 13 46 88 19 76 57 , x=36, l=1
8 36 13 46 88 19 76 57

Dup aceast etap avem pe prima poziie, h
1
, elementul minim (sau maxim, n funcie de
semnul comparaiei). Pentru a sorta vectorul se interschimb acest element cu ultimul i se reia
algoritmul pentru vectorul h
1
,. . . , h
r-1
; dup care obinem minimul din vectorul rmas n h
1
, l
interschimbm cu h
r-1
i relum operaia pe h
1
,. . . , h
r-2
, i aa mai departe, pn cnd se ordoneaz
ntreg vectorul (r=1). Metoda este avantajoas pentru valori mari ale lui n.
Pentru exemplul anterior, sortarea utiliznd acest algoritm, continu astfel:

36 57 13 46 88 19 76 8
19 36 57 46 88 76 13 8
36 46 57 76 88 19 13 8
46 76 57 88 36 19 13 8
57 76 88 46 36 19 13 8
76 88 57 46 36 19 13 8
88 76 57 46 36 19 13 8
88 76 57 46 36 19 13 8

Dac se dorete ordonarea cresctoare, aceasta poate fi uor realizat prin schimbarea relaiilor
de ordonare n funcia de cernere. n continuare este prezentat ntregul program pentru ordonarea
cresctoare a unui vector.
/* HeapSMax.c - Ord. descresc., prin selectie Heapsort:
construim Heap-ul, arborele de selectie, memorat intr-un
vector, dupa care se interschmba prima valoare (maximul)
cu ultima si in vectorul ramas (n-1) se construieste
din nou Heap-ul, s.a.m.d. */
#include<stdio.h>
57
#include <conio.h>
void cerne (double a[], int l, int r)
{int i,j;
double x;
i=l; j=2*i; x=a[i]; /* x ce se cerne prin arbore/ vector */
while (j<=r){if (j<r) /* nu s-a ajuns la capat */
if (a[j]<a[j+1]) /* j-ind. val. mai mare din */
j++; /* perechea (2*i, 2*i + 1) */
if (x>a[j]) /* daca x > valoarea respectiva (j) */
goto gata; /* altfel, se muta */
a[i]=a[j]; /* valoarea mai mare a[j] la a[i], si se */
i=j; j=2*i; /* continua cernerea pe nivelul urmator */
} /* noul i devine j (vechi i), iar j=2i, daca j<r */
gata: a[i]=x; /* pune x in poz. i, a[i]<a[2i], a[i]<a[2i+1] */
}
void heapsort(double a[], int n){
int i, l, r;
double x;
l=(n/2)+1; r=n;
while (l>1){ l--;
cerne(a,l,r);
for( i = 1 ; i <= n ; ++i ){
printf (" %0.lf ", a[i]);
if (i%10==0)
printf("\n");
}
printf("\nApasa o tasta pentru continuare program!\n");
getch();
}
while(r>1) {x=a[1];
a[1]=a[r];
a[r]=x;
r--;
cerne(a,l,r);
for( i = 1 ; i <= n ; ++i ){
printf (" %0.lf ", a[i]);
if (i%10==0)
printf("\n");
}
printf("\nApasa o tasta pentru continuare program!\n");
getch();
}
}
void main(void)
{double sir[1000];
int ne, i;
clrscr();
printf("HeapSort pentru ordonare crescatoare vector.\n");
printf ("Numarul de elemente: ");
58
scanf ("%d",&ne);
for ( i = 1 ; i <= ne ; ++i)
{printf (" sir(%d)=", i);
scanf ("%lf", &sir[i]);
}
heapsort(sir,ne);
for( i = 1 ; i <= ne ; ++i ){
printf (" %0.lf ", sir[i]);
if (i%10==0)
printf("\n");
}
printf("\nPROGRAM TERMINAT! Apasa o tasta-iesire!!!\n");
getch();
}


2.8. Sortare prin interclasare (MergeSort)
Algoritmul de sortare, a unui vector cu n elemente, prin interclasare se bazeaz pe mprirea
vectorului n doi vectori care, odat sortai, se interclaseaz. Conform principiului divide et impera
problema este descompus n alte dou subprobleme de acelai tip i dup rezolvarea lor soluiile se
combin (interclaseaz). Vectorul de sortat este divizat n subvectori, prin njumtiri succesive, ct
timp lungimea acestora este mai mare de 2. Evident, un subvector de lungime 1 este sortat, iar un
subvector de lungime 2 necesit cel mult interschimbarea celor dou valori. Subvectorii sortai sunt
interclasati succesiv, n ordinea invers divizarii, obinnd n final vectorul sortat.
Deoarece interclasarea necesit un vector auxiliar, sortarea propriu-zis va fi precedat de
alocarea unei zone tampon de aceeai lungime cu vectorul de sortat. Pentru a sorta un tablou de n= 2
k

elemente, presupunnd c descompunerea este totat, acest spaiu este de:
2*(2
k-1
+2
k-2
+ . . . + 2 + 1) = 2*2
k
= 2*n
Pentru a evita copierea rezultatului unei interclasri din vectorul auxiliar n cel de sortat i a
reduce astfel numrul de operaii, vectorul iniial i cel auxiliar pot fi utilizai alternativ ca surs i,
respectiv, rezultat al operaiei de interclasare.
Algoritmul rezultat este urmtorul:
MergeSort (v, p, q):
if (q-p <= 1){
if (v[p]>v[q]){
aux=v[p];
v[p]=v[q];
v[q]=aux;
}
}
else{ m=(p+q)/2;
MergeSort(v, p, m);
MergeSort(v, m+1, q);
Interclasare(v, p, q, m);}

Interclasarea se va realiza n modul urmtor: considerm indicele i este cel al elementului la
care s-a ajuns n primul vector (a, m elemente), indicele j este cel al elementului la care s-a ajuns n al
59
doilea vector (b, n elemente), iar indicele k este cel al elementului care urmeaz s fie scris n cel de-al
treilea vecor (c).
while (i<m && j<n)
if (a[i] < b[j])
c[k]=a[i];
i++; k++;
else
c[k]=b[j];
j++; k++;
vectorul rmas (dup ce cellalt a fost parcurs n totalitate) se va copia n rezultat (c)

S considerm urmtorul exemplu numeric.
0 1 2 3 4 5 6 7 8 9 10
- indicii valorilor din vectorul iniial
12 8 6 10 3 7 15 9 15 3 2 acesta este irul iniial de ordonat, m=10/2=5
8 12 recursiv se ajunge la m=1, sorteaz irul (0,1)
6 8 12 interclaseaz primele dou iruri ((0,1) cu (2))
3 10 7 m=4, sorteaz irul (3,4); restul nemodificat
3 7 10 interclaseaz cele dou iruri ((3,4) cu (5))
3 6 7 8 10 12 interclaseaz primele dou iruri ((0,2) cu (3,5))
9 15 15 m=7, sorteaz irul (6,7); restul nemodificat
9 15 15 interclaseaz cele dou iruri ((6,7) cu (8))
2 3 sorteaz irul (9,10); restul nemodificat
2 3 9 15 15 interclaseaz cele dou iruri ((6,8) cu (9,10))
2 3 3 6 7 8 9 10 12 15 15 interclaseaz cele dou iruri ((1,5) cu (6,10))

Valorile care nu se modific de la o iteraie la alta nu au mai fost copiate n tabel pentru a
simplifica urmrirea transformrilor succcesive (valoarea unei celule albe este cea care se afl pe
vertical n prima celul cu o valoare).
ntreg programul care realizeaz ordonarea utiliznd algoritmul MergeSort este urmtorul:
/* MergeSor.c - Ordonare utilizand metoda MergeSort (interclasare) */
#include <stdio.h>
#include <conio.h>
#define DIM 20
void MergeSort (int v[], int p, int q){
int m, aux;
void Interclasare(int [], int, int, int);
if (q-p <= 1){
if (v[p]>v[q]){
aux=v[p];
v[p]=v[q];
v[q]=aux;
}
}
else{
m=(p+q)/2;
MergeSort(v, p, m);
60
MergeSort(v, m+1, q);
Interclasare(v, p, q, m);
}
}
void Interclasare (int v[], int p, int q, int m){
int i=p, j=m+1, k=0;
int *u=(int *) malloc (DIM * sizeof(int)); // alocare memorie pentru vect. u
while (i<=m && j<=q){
if (v[i] <= v[j]){
u[k]=v[i];
i++; k++;
}
else {u[k]=v[j];
j++; k++;
}
}
if (i <= m)
for (j=i; j<=m; j++){ u[k]=v[j];
k++;
}
else
for (i=j; i<=q; i++){ u[k]=v[i];
k++;
}
k=0;
for (i=p; i<=q; i++){ v[i]=u[k];
k++;
}
free(u); // eliberarea spatiului alocat pentru vectorul u
}
void scrie_vector(int vect[], int d){
int i;
for (i=0; i<d; i++)
printf("%5d,", vect[i]);
}
void main()
{int vector[]={10, 5, 6, 12, 3, 7, 15, 12, 9, 4, 3};
int ne=11;
clrscr();
MergeSort(vector, 0, ne-1);
scrie_vector(vector, ne);
printf("\nApasati o tasta pentru a termina programul!\n");
getch();
}