Documente Academic
Documente Profesional
Documente Cultură
New Microsoft Office Word Document
New Microsoft Office Word Document
mergesort
#include <iostream>
#include<iomanip>
using namespace std;
int a[100],n;
void mijloc(int s, int d, int &m)
{
m=(s+d)/2;
}
void refa(int s, int d, int m)
{
int i=s, j=m+1, k=1, b[100];
while(i<=m&&j<=d)
{
if(a[i]>a[j])
{
b[k]=a[j]; j++;
}
else
{
b[k]=a[i];
i++;
}
k++;
}
while(i<=m)
{
b[k]=a[i];
k++;
i++;
}
while(j<=d)
{
b[k]=a[j];
k++;
j++;
}
k=1;
for(i=s;i<=d;i++,k++)
a[i]=b[k];
}
void merge(int s, int d)
{
int m;
if(s<d)
{
mijloc(s,d,m);
merge(s,m);
merge(m+1,d);
refa(s,d,m);
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
merge(1,n);
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
cout<<setprecision(n,3);
return 0;
}
2. quicksort
#include <iostream>
using namespace std;
int a[10];
void schimba( int &x, int &y)
{
int aux=x;
x=y;
y=aux;
}
void pivot(int s, int d, int &m)
{
int i=s,j=d, pi=0,pj=1;
while(i<j)
{
if(a[i]>a[j])
{
schimba(a[i],a[j]);
schimba(pi,pj);
}
i=i+pi;
j=j-pj;
}
m=i;
}
void quick( int s, int d)
{
int m;
if(s<d)
{
pivot(s,d,m);
quick(s,m-1);
quick(m+1,d);
}
}
int main()
{
int n,i;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
quick(1,n);
for( int i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
3. pliere vector
#include <iostream>
using namespace std;
int a[100],n;
void pliere(int i, int j)
{
int m;
if(i==j) cout<<a[i]<<" ";
else
{
m=(i+j)/2;
if((j-i+1)%2==0)
{
pliere(i,m);
pliere(m+1,j);
}
else{
pliere(i,m-1);
pliere(m+1,j);
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
pliere(1,n);
return 0;
}
4. sortari
Lucarea de laborator nr. 7
Sortare si algoritmi de sortare
1. Introducere
Sortarea este
o
metoda (un
algoritm),
prin
intermediul careia se poate ordona o anumita clasa de
obiecte concrete sau abstracte, dupa unul sau mai multe
criterii impuse iar cautarea este o metoda, prin
intermediul careia, se poate cauta, dupa criterii
precizate, pentru regasire un anumit obiect concret sau
abstract intr-o multime ordonata sau nu de obiecte
concrete sau abstracte
Ca exemple se pot da :
sortarea
(ordonarea)
crescatoare
sau
descrescatoare a unui sir de numere reale si
cautarea unui numar sau a mai multor numere in
acest sir de numere
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
if (este_sortat(v, n))
cout<<"\n vectorul dat este ordonat";
else
cout<<"\n vectorul dat nu este ordonat";
cout<<"\n continuati ?(d/n):";
cin>>r;
}
}
Tehnica prin care se atribuie unei variabile logice
valoarea true sau false, in functie de anumite conditii
intalnite intr-un algoritm, poarta numale de tehnica
fanionului. In functia de testare a ordonarii, variabila
fanion, sortat,
se
initializeaza
cu
valoarea true (adevarat)
ca
si
cand
elementele
vectorului ar fi ordonate crescator. In urma compararii a
oricaror doua elemente vecine ale unui vector pot
aparea urmatoare doua posibiltati :
a.
Cele doua elemente respecta proprietatea
pe care o au elementele unui vector ordonat.
Dintr-o singura comparare nu se poate spune
nimic vector in ansamblul sau.
b.
Elementul din stanga nu este mai mic decat
vecinul sau din dreapta. In acest caz se poate
afirma cu certitudine ca sirul nu este ordonat.
Ca atare, la intrarea in functie vom presupune ca
sirul este apriori ordonat (fanionul sortat = true). Daca
pe parcursul compararii tuturor elementelor vecine, vom
gasi cel putin doua elemente care nu sunt asezate in
ordine, atunci se va putea afirma ca presupunerea
initiala a fost falsa si ca vectorul, in ansamblul sau, este
neordonat.
Daca
la
finalul
compararii
tuturor
elementelor vecine variabila fanion sortat va avea
aceeasi valoare pe care a primit-o inainte de
parcurgerea vectorului (true), ceea ce implica faptul ca
nu s-a gasit nici macar doua elemente vecine care nu
sunt asezate in ordinea ceruta, atunci se va putea
spune, cu certitudine, ca vectorul este ordonat.
Algoritmul de sortare cu bule a unui vector :
Algoritmul de sortare cu bule este, de fapt, o extensie a
algoritmului de testare a ordonarii unui vector. Atunci
cand se gasesc doua elemente vecine care nu respecta
proprietatea pe care o au oricare doua elemente vecine
dintr-un vector ordonat, pe langa modificarea variabilei
fanion la valoare fals, se va proceda la interschimbarea
celor doua elemente intre ele. Daca la sfarsitul
parcurgerii vectorului s-a efectuat cel putin o
interschimbare atunci nu se poate spune cu certitudine
ca vectorul este ordonat si va trebui sa se reia procedeul
de comparare, pana cand variabila fanion, dupa o
parcurgere completa, va ramane cu valoarea true cu
care a fost initializata inainte de inceperea compararilor,
fapt ce ne arata cu certitudine ca nu s-a efectuat nicio
interschimbare si ca vectorul este ordonat.
Descrierea in pseudocod a algoritmului functiei de
sortare cu bule
functie sortare_bule (v, n)
inceput
repeta
inceput
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
sortat = adevarat
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
pentru i=0 la n-2 executa
// daca cel putin doua elemente vecine nu sunt in
ordinea ceruta se interschimba elementele
// si variabila fanion sortat primeste valoarea
fals
daca v[i]>v[i+1] atunci
inceput
aux = v[i]
v[i] = v[i+1]
v[i+1] = v[i]
sortat = fals ;
sfarsit
sfarsit
cat timp (sortat = fals)
sfarsit.
Descrierea algoritmului sortarii cu bule in C :
void sortare_bule (float v[], int n)
{
bool sortat ; int i ; float aux ;
// bucla de testare cu interschimbare
do
{
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
bool sortat = true
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=0 ; i<n-1 ; i++)
// daca cel putin doua elemente vecine nu sunt
in ordinea ceruta
// se interschimba elementele intre ele si
variabila fanion sortat primeste valoarea fals
if (v[i]>v[i+1])
{
aux = v[i] ; v[i] = v[i+1] ; v[i+1] = aux ;
sortat = false ;
}
}
while ( !sortat) ;
}
Exemplul 2. Se considera orice vector v cu n elemente
reale. Oricare ar fi vectorul furnizat, sa se ordoneze
crescator, utilizandu-se sortarea cu bule
#include <iostream.h>
#include <conio.h>
# define nmax 20
int nr_comparari = 0; //contor nr comparari
int nr_treceri = 0; //contor nr treceri
void sortare_bule (float v[], int n)
{
// declararea variabilelor locale
bool sortat; int i; float aux;
// bucla de comparare a elementelor vecine
do
{
nr_treceri++;//incrementare contor treceri
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
sortat = true;
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=0 ; i<n-1 ; i++)
{
nr_comparari++; //incrementare contor comparari
// daca cel putin doua elemente vecine nu sunt in
ordinea ceruta variabila fanion sortat primeste valoarea
fals
if (v[i]>v[i+1])
{
aux = v[i]; v[i] = v[i+1];
v[i+1] = aux;
sortat = false ;
}
}
}
while (!sortat);
}
void main ()
{
float v[100] ; int n,i ;char r='d' ;
while (r=='d')
{
// citirea dimensiunii vectorului
do
{
cout<<"\n dati numarul de componente ale
vectorului [0,"<<nmax<<"]=" ;
cin>>n;
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// apelarea functiei de sortare cu bule
sortare_bule (v, n);
cout<<"\n vectorul ordonat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// afisarea numarului de comparari si treceri prin
vector
cout<<"\n
sortarea
s-a
facut
cu
"<<nr_comparari<<" comparari"
<<" si in "<<nr_treceri<<" treceri";
// initializarea numarului de comparari si
treceri
nr_comparari = 0;
nr_treceri = 0;
cout<<"\n continuati ?(d/n):";
cin>>r;
}
}
Dandu-se cazul cel mai defavorabil, vectorul v = (5,
4, 3, 2, 1), ordonat descrescator, pentru ordonarea sa
crescatoare s-au efectuat 5 parcurgeri efectuandu-se 20
de comparari, iar pentru cazul cel mai favorabil, vectorul
v = (1, 2, 3, 4, 5), deja ordonat s-au efectuat 1
parcurgere si 4 comparari..
Sa analizam ce se intampla la o parcurgere a
elementelor vectorului. Presupinem ca cel mai mare
element se afla pe pozitia j, cu j < n-1. Cand se ajunge sa
se compare acest element cu vecinul sau aflat de pe
pozitia j + 1 se constata ca nu sunt asezate in ordine,
deoarece v[j] > v[j+1], si se va produce, conform
algoritmului,
o
interschimbare.
Dupa
aceasta
nr_comparari++;
//
incrementare
contor
comparari
// se compara doua cate doua elementele
vecine pentru orice i din intervalul [0, n-1 - i]
if (v[j]>v[j+1])
{
// daca cel putin doua elemente vecine nu
sunt in ordinea ceruta
// se interschimba elementele intre ele
aux = v[j] ; v[j] = v[j+1] ; v[j+1] = aux ;
}
}
}
}
void main ()
{
float v[100] ; int n,i ;char r='d' ;
while (r=='d')
{
// citirea dimensiunii vectorului
do
{
cout<<"\n dati numarul de componente ale
vectorului [0,"<<nmax<<"]=" ;
cin>>n;
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// apelarea functiei de sortare cu bule
sortare_optima1_bule (v, n);
cout<<"\n vectorul ordonat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// afisarea numarului de comparari si treceri prin
vector
sortat = false ;
}
j++ ; // incrementare contor treceri
}
while ( !sortat) ;
}
Un program complet se poate scrie, utilizand
aceasta implementare, asemanator cu exemplele
anterioare.
Algoritmul este eficient in cazul in care avem un sir deja
sortat in care au fost inserate cateva elemente la stanga
elementelor din vectorul initial sortat
In cazul sirului v = (1, 9, 6, 2, 3, 4, 5, 7) se evidentiaza
urmatoarele treceri:
Prima trecere : v = (1, 6, 2, 3, 4, 5, 7, 9)
A doua trecere: v = (1, 2, 3, 4, 5, 6, 7, 9)
A treia trecere detecteaza ca sirul este sortat
Daca avem de sortat un sir in care s-au adaugat
elemente la dreapta unui alt sir deja sortat atunci
algoritmul presupune mai multe treceri. Daca avem de
sortat sirul v = (1, 3, 5, 6, 8, 9, 10, 0) atunci vom avea
urmatoarele treceri :
Prima trecere :
sirul v = (1, 3, 5, 6, 8, 9, 0, 10)
A doua trecere: sirul v = (1, 3, 5, 6, 8, 0, 9, 10)
A treia trecere:
sirul v = (1, 3, 5, 6, 0, 8, 9, 10)
A patra trecere: sirul v = (1, 3, 5, 0, 6, 8, 9, 10)
A cincea trecere: sirul v = (1, 3, 0, 5, 6, 8, 9, 10)
A sasea trecere: sirul v = (1, 0, 3, 5, 6, 8, 9, 10)
A saptea trecere: sirul v = (0, 1, 3, 5, 6, 8, 9, 10)
A opta trecere gaseste sirul sortat v = (0, 1, 3, 5, 6, 8,
9, 10)
In exemplul de mai sus, desi am adaugat un
singur element, pe 0, la dreapta unui alt sir sortat
algoritmul are nevoie de opt treceri. Se observa, daca
am schimba sensul parcurgerii, ca ar fi nevoie numai de
o singura trecere pentru ordonare descrescatoare.
In concluzie, atunci cand stim ca avem de
sortat un sir deja ordonat in care s-au inserat la
intamplare cateva elemente se pot alterna sensurile de
parcurgere a sirului
sortat = fals
sfarsit
sfarsit
primul = primul + 1 // incrementarea pozitiei
primului element
sfarsit
cat timp (sortat = fals)
sfarsit.
Descrierea algoritmului in C :
void sortare_alternanta_bule (float v[], int n)
{
bool sortat ; int i; float aux ;
int primul = 0; // contorul pozitiei primului element
int ultimul = n-1; // contorul pozitiei ultimului
element
// bucla de testare cu interschimbare
do
{
// parcurgerea sirului de la stanga la dreapta
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
bool sortat = true
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=primul ; i<ultimul; i++)
// daca cel putin doua elemente vecine nu sunt
in ordinea ceruta
// se interschimba elementele intre ele si
variabila fanion sortat primeste valoarea fals
if (v[i]>v[i+1])
{
aux = v[i] ; v[i] = v[i+1] ; v[i+1] = aux ;
sortat = false ;
}
ultimul -- ; // decrementare contor ultimei pozitii
// parcurgerea sirului de la dreapta la stanga
if ( !sortat)
{
sortat = true;
for (i=ultimul ; i>primul; i--)
nr_treceri++;
cout<<"\n
vectorul
dupa
trecerea
"<<nr_treceri<<':';
for (k = 0; k <= n - 1; k++) cout<<v[k]<<'
';
}
}
while (!sortat);
}
void main ()
{
float v[100] ; int n,i ;char r='d' ;
// redirectarea iesirii stdout intr-un fisier text, fisout
// in scopul listarii ulterioare a rezultatelor
// if((freopen("fisout","wt",stdout))==NULL)
// {
// printf("\n eroare de deschidere");
//
exit(1);
// }
while (r=='d')
{
// citirea dimensiunii vectorului
do
{
cout<<"\n dati numarul de componente ale
vectorului [0,"<<nmax<<"]=" ;
cin>>n;
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// apelarea functiei de sortare cu bule
sortare_alternanta_bule (v, n);
cout<<"\n vectorul ordonat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
v[3]=5
v[4]=6
v[5]=7
v[6]=1
v[7]=8
vectorul dat:2 3 4 5 6 7 1 8
vectorul dupa trecerea 1:2 3 4 5 6 1 7 8
vectorul dupa trecerea 2:1 2 3 4 5 6 7 8
vectorul dupa trecerea 3:1 2 3 4 5 6 7 8
vectorul ordonat:1 2 3 4 5 6 7 8
sortarea s-a facut cu 6 comparari si in 3 treceri
continuati ?(d/n):d
Sortarea unui vector anterior sortat in care s-au inserat
aleator componentele 9 si 7
dati numarul de componente ale vectorului [0,20]=8
v[0]=1
v[1]=2
v[2]=3
v[3]=9
v[4]=7
v[5]=4
v[6]=5
v[7]=6
vectorul dat:1 2 3 9 7 4 5 6
vectorul dupa trecerea 1:1 2 3 7 4 5 6 9
vectorul dupa trecerea 2:1 2 3 4 7 5 6 9
vectorul dupa trecerea 3:1 2 3 4 5 6 7 9
vectorul dupa trecerea 4:1 2 3 4 5 6 7 9
vectorul ordonat:1 2 3 4 5 6 7 9
sortarea s-a facut cu 7 comparari si in 4 treceri
continuati ?(d/n):n
Algoritmul de sortare cu bule a unui sir cu parcurgerea
alternata a sirului pana la ultima interschimbare
Algoritmului anterior i se poate aduce o ultima
imbunatatire, daca se tine cont de pozitia (indexul) la
care are loc ultima interschimbare. Daca la o parcurgere,
de la stanga la dreapta sau de la dreapta la stanga,
ultima interschimbare are loc de pe pozitiile p si p+1,
respectiv p si p-1, atunci se poate spune ca elementele
v[i] = v[i-1]
v[i-1] = v[i]
sortat = fals
t = i; //retinerea
indexului
ultimei
interschimbari
sfarsit
sfarsit
primul = t // prima pozitie de comparat =
primul index in care s-a produs o intyerschimbare
sfarsit
cat timp (sortat = fals)
sfarsit.
Descrierea algoritmului in C :
void sortare_alternanta_bule (float v[], int n)
{
bool sortat ; int i; float aux ;
int primul = 0; // contorul pozitiei primului element
int ultimul = n-1; // contorul pozitiei ultimului
element
// bucla de testare cu interschimbare
do
{
// parcurgerea sirului de la stanga la dreapta
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
bool sortat = true
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=primul ; i<ultimul; i++)
// daca cel putin doua elemente vecine nu sunt
in ordinea ceruta
// se interschimba elementele intre ele si
variabila fanion sortat primeste valoarea fals
if (v[i]>v[i+1])
{
aux = v[i] ; v[i] = v[i+1] ; v[i+1] = aux ;
sortat = false ;
t = i; //retinerea indexului ultimei
interschimbari
}
stnga este mai mic sau egal dect cel mai mic element
din subvectorul din dreapta atunci ntregul vector este
de fapt sortat.
Implementarea algoritmului:
void sortareRapida(int v[], int st, int dr)
{
int m = partitioneaza(v, st, dr);
if (st<m)
{
sortareRapida(v, st, m);
}
if ((m+ l)<dr)
{
sortareRapida(v, m + 1, dr);
}
}
"Munca" este fcut de fapt de procedura de
partiionare. Exist mai multe moduri n care putem
mpri vectorul, dar urmtorul algoritm, inventat, de R.
Sedgevvick, pare a fi cel mai bun: se pornete de la
ambele capete, folosind doi indicatori, i pentru partea
stng, j pentru partea din dreapta, cutndu-se
elementele care ar trebui s fie n cealalt parte; n
momentul n care se gsesc elementele se verific dac i
< j; dac da se interschimb cele dou elemente i se
continu algoritmul, altfel algoritmul ntoarce valoarea
lui j; element de referin se consider ca fiind primul
element din vector.
Descrierea algoritmului:
Urmtorul algoritm partiioneaz vectorul v[st..dr]
n doi subvectori v[st..m] i v[m+l..dr] astfel nct toate
elementele din primul subvector s fie mai mici sau
egale dect oricare element din cel de-al doilea
subvector, la final ntorcnd valoarea lui m.
P1. Se iniializeaz elementul de referin x cu v[st], i
cu st i j cu dr.
P2. Dac v[i] >= x se trece la pasul P5. Altfel se
continu cu pasul urmtor.
P3. Se incrementeaz i.
P4. Se continu cu pasul P2.
O
s
ilustrm
funcionarea
algoritmului
cu
urmtorul exemplu:
Partiionarea 1: Prin partiionarea irului: 7 13 100 1 54
87 5 13 44 99 89 2 9 6 35 88 se obin urmtoarele
partiii: 6 2 5 1 i : 54 87 100 13 44 99 89 13 9 7 35 88
Partiionarea 2. Prin partiionarea irului: 6 2 5 1 se
obin partiiile: 1 2 5 si 6
Partiionarea 3: Prin partiionarea irului: 1 2 5 se
obin partiiile: 1 i 2 5
Partiionarea 4. Prin partiionarea irului 2 5 se obin
partiiile: 2 i 5
Partiionarea 5. Prin partiionarea irului: 54 87 100 13
44 99 89 13 9 7 35 88 se obin partiiile:
35 7 9 13 44 13 i 89 99 100 87 54 88
Partiionarea 6. Prin partiionarea irului: 35 7 9 13 44
13 se obin partiiile:13 7 9 13 i 44 35
Partitionarea 7. Prin partitionarea irului: 13 7 9 13 se
obin partiiile: 13 7 9 i 13
Partitionarea 8. Prin partitionarea irului: 13 7 9 se obin
partiiile: 9 7 i 13
Partitionarea 9. Prin partitionarea irului: 9 7 se obin
partiiile: 7 i 9
Partitionarea 10. Prin partitionarea irului: 44 35 se
obin partiiile: 35 i 44
Partitionarea 11. Prin partitionarea irului: 89 99 100 87
54 88 se obin partiiile: 88 54 87 i 100 99 89
Partiionarea 12. Prin partiionarea irului: 88 54 87 se
obin partiiile: 87 54 i 88
Partiionarea 13. Prin partiionarea irului: 87 54 se
obin partiiile: 54 i 87
Partiionarea 14. Prin partiionarea irului: 100 99 89 se
obin partiiile: 89 99 i 100
Partiionarea 15. Prin partiionarea irului: 89 99 se
obin partiiile:89 i 99
n final se obine irul ordonat crescator: 1 2 5 6 7 9 13
13 35 44 54 87 88 89 99 100
In cel mai ru caz, partiionarea produce o regiune
cu un singur element. Cel mai bun caz se obine atunci
cnd vectorul este mprit n dou pri egale. Timpul
mediu este mai apropiat de cel mai bun caz, adncimea
arborelui de partiionare fiind aproximativ egal cu
log2n, iar pentru fiecare nivel sunt comparate toate
}
}
}
}
Aceast variant este mai compact, dar mult mai
lent, nlocuinclu-se o singur instruciune de atribuire
cu trei instruciuni similare.
Algoritmul, n prima sa variant, efectueaz pentru
fiecare i, n-i-1 comparaii i cel mult o interschimbare.
Deci avem (n - 1) + (n - 2) + ... + 2 + 1 = n * ( n - 1) / 2
comparaii
i maxim n- 1 interschimbri. Avantajul acestui algoritm,
comparativ cu sortarea cu bule, const n faptul c
efectueaz puine deplasri de date.
2.2.4.
Sortarea Heap
Aceast sortare este o mbuntire a seleciei directe.
Este evident c un algoritm de selectare a maximului
dintr-un vector de n elemente trebuie s efectueze cel
puin n - 1 comparaii. Dar acest lucru este valabil doar
pentru prima etap, n urmtoarele etape ne putem
folosi de informaiile acumulate n etapele anterioare.
Imaginai-v o structur ierarhic care are n vrf un
singur element. Acest element are dou "ajutoare" care
au rangul mai mic dect al su, acestea au la rndul lor
cte dou ajutoare de rang mai mic i aa mai departe.
Fiecare element din aceast structur, are doi
subordonai, iar aceti subordonai au un rang mai mic
dect superiorul lor direct. Excepie fac elementele de
pe ultimul nivel, care nu au nici un subordonat, i
elementele de pe penultimul nivel, care pot s aib doar
un singur subordonat. De exemplu, fie urmtoarea
structur:
100
99
87
88
89
9
35
13
44
54
13
2
7
6
5
Este evident c cel mai mare element este cel din vrful
ierarhiei. Dac scoatem acest element, pentru a-l nlocui
trebuie s comparm subordonaii direci, avansnd cel
{
interschimba(v[index], vflargest]);
heapify(v, iargest, n);
}
}
Deoarece n C/C++ primul element dintr-un
vector are indexul 0, spunem c acesta formeaz un
HEAP dac pentru orice i avem v[i] v[2*i + 1] i v[i]
v[2:i:i -l- 2], cu condiia ca toi indecii s fie n intervalul
[0, n - 1].
Fiindc elementele din dreapta lui index formeaz un
HEAP trebuie s comparm elementul de pe aceast
poziie cu elementele de pe poziiile (2*index. + 1) i
(2*index + 2), cu condiia ca cel puin unul dintre
acestea s fie n vector. Dac nici una dintre aceste
poziii nu face parte din vector sau dac elementul de pe
poziia index este mai mare sau egal cu celelalte dou
elemente, procedura se ncheie. Altfel se interschimb
elementul de pe poziia index cu elementul cel mai mare
dintre celelalte dou. In urma acestei interschirnbri nu
mai avem certitudinea c elementele din intervalul
[largest, n - 1] mai formeaz un HEAP, unde largest este
poziia pe care s-a aflat cel mai mare element nainte de
interschimbare. Dar elementele din intervalul [largest +
1, n - 1] formeaz un HEAP, deoarece nu am operat nicio
modificare n acest interval. De aceea, trebuie s
aplicm aceeai procedur pentru elementul aflat pe
poziia largest.
In continuare o s folosim aceast procedur pentru
a transforma vectorul ntr-un HEAP:
void construieteHeap(int v[], int n)
{
for (int i = (n-1)/2; i >= 0; i--)
{
heapify(v, i, n);
}
}
Incepem cu elementul aflat la jumtatea vectorului
deoarece elementele din dreapta lui nu mai pot avea
"subordonai". Dup ce am transformat vectorul ntr-un
HEAP, sortarea este foarte simpl: primul element, care
temp = v[i];
for (k = i - 1; k>=j; k--)
{
v[k+ 1] = v[k];
}
v[j] = ternp;
}
}
}
Poriunea de cod:
j=0;
while (v[j] < v[i])
{
j++;
}
determin poziia n care trebuie inserat elementul v[i]
n irul ordonat v[0..i 1]. Dup ce se determin aceast
poziie, dac trebuie deplasat elementul de pe poziia i,
se pune elementul v[i] deoparte:
temp = v[i];
Acum trebuie s i se fac loc lui v[i] pentru a-1 insera pe
poziia j. Pentru aceasta se deplaseaz toate elementele
din intervalul [j, i - 1], ncepnd cu cel mai din dreapta
element:
for(k = i- 1; k>=j; k-)
{
v[k+l] = v[k];
}
Dup care se insereaz elementul pe care l-am pus
deoparte:
v[j] = temp;
Algoritmul se repet pentru toate elementele din
intervalul [1, n - 1]. Cel mai defavorabil timp se obine n
cazul n care vectorul este sortat descresctor, n acest
caz avnd cele mai multe deplasri . Cel mai bun timp
este obinut dac vectorul este deja sortat.
2.2.5.
Sortarea Shell
Cea mai costisitoare operaie n cazul sortrii prin
inserie direct o constituie deplasarea elementelor la
dreapta pentru a face loc elementului care trebuie
inserat. De aceea avem nevoie de un mecanism prin
coinbina(v, s, m, d);
}
Descrierea algoritmului :
Urmtorul algoritm primete vectorul v[st..dr] cu
elementele din subvectorii v[st...m], v[m+l..dr] ordonate
cresctor i are ca efect combinarea celor doi subvectori
astfel nct ntreg vectorul, de la st la dr, s fie ordonat
cresctor, folosind un vector temporar.
P1. Se efectueaz urmtoarele iniializri: i = st, j = m +
1 i k = 0.
P2. Dac i > m sau j > dr se continu, cu pasul P11,
altfel se continu cu pasul P3.
P3. Dac v[i] > v[j] se continu cu pasul P7, altfel se
continu cu pasul P4.
P4. Se efectueaz atribuirea temp[k] = v[i].
P5. Se incrementeaz i.
P6. Se continu cu pasul P9.
P7. Se efectueaz atribuirea temp[k] = v[j].
P8. Se incrementeaz j.
P9. Se incrementeaz k.
P10. Se continu cu pasul P2.
P11. Dac i > m se continu cu pasul P16, altfel se
continu cu pasul P12.
P12. Se efectueaz atribuirea temp[k] = v[i].
P13. Se incrementeaz i.
P14. Se incrementeaz k.
P15. Se continu cu P11.
P16. Dac j > dr se continu cu pasul P21, altfel se
continu cu pasul P17.
P17. Se efectueaz atribuirea temp[k] = v[j].
PI8. Se incrementeazj.
P19. Se incrementeaz k.
P20. Se continu cu P16.
P21. Se copiaz coninutul vectorului temp n v,
ncepnd cu adresa lui v[st] i algoritmul se ncheie.
Descrierea algoritmului in C :
Varianta C++ aloc memorie pentru vectorul temporar la
nceputul funciei, memorie care va fi eliberat la final.
Copierea vectorului se face cu ajutorul funciei niemcpy,
care este varianta cea mai bun pentru copierea zonelor
de memorie disjuncte:
void combina(int v[], int s, int m, int d)
{
int i = s; int. j = m + 1;
int *temp = new injd - s + 1]; int k = 0;
while ((i <= ni) && (j <= d))
{
if(v[i] < v[j])
{
temp[k] = v[i];
}
else
{
temp[k] = v[j];
j++;
}
k++;
}
for ( ; i <= m; i++)
{
temp[k] = v[i]; k++;
}
for ( ; j <= d; j++)
{
temp[k] = v[j];
k++;
}
memcpy(&v[s], temp, (d - s + 1) * sizeof(int));
delete temp;
}
Algoritmul este simplu: se ncepe cu capetele din
stnga ale subvectorilor, i i j fiind indicatori ctre ceie
mai mici elemente din subvectori care nu au fost nc
mutate n vectorul temporar. Ct timp ambii subvectori
mai au elemente care nu au fost nc mutate se va muta
n vectorul temporar cel mai mic dintre v[i] i v[j], dup
care se incrementeaz indexul corespunztor. Indexul k
{
temp[k] = v[i];
k++;
}
for ( ; j <= d; j++)
{
temp[k] = v[j];
k++;
}
memcpy(&v[s], ternp, (d - s + 1) * sizeof(int));
delete temp;
}
Acum n cel mai favorabil caz, cnd unul din
subvectori are toate elementele mai mici dect oricare
dintre elementele celui de-al doilea subvector, se
efectueaz 3/2n comparaii. Dar n cel mai defavorabil
caz, cnd se iau elemente pe rnd din fiecare subvector,
avem 3n comparaii, pentru fiecare 2 elemente mutate
efectundu-se 6 comparaii: de cte 2, ori (i <= m) i (j
<= d), apoi (v[i] < v[j]) i (v[i] >= v[j]). Primele
comparaii le putem reduce uor: atunci cnd oricare
dintre cicluri interioare trebuie s se termine fiindc s-a
terminat unul dintre subvectori, atunci este evident c
trebuie s se termine i ciclul exterior. Acest lucru se
poate face printr-un salt n afara ciclului exterior,
deoarece nu avem nicio instruciune care s "ias" din
dou cicluri. Este de asemenea evident c ultimele dou
comparaii sunt complementare: dac una va fi
adevrat cealalt va fi fals i invers. De aceea cel deal doilea ciclu poate s nceap cu mutarea elementului
din cel de-al doilea subvector, cu condiia ca primul ciclu
s se fi terminat cu gsirea unui element mai mare n
primul subvector:
do
{
temp[k] = v[j] ;
j++;
k++;
if(j>d)
{
goto out;
}
}
while((v[i]>=v[j]));
Dar dup ce se execut o dat cel de-a! doilea ciclu
i se ncheie cu gsirea unui element mai mare dect
primul element din primul subvector este clar care este
elementul care trebuie mutat. De aceea pentru a putea
scrie cele dou cicluri simetric, ncepem cu selectarea
elementelor din cel de-al doilea subvector:
void combina3(int v[], int s, int m, int d)
{
int i = s;
int j = m + 1;
int *temp = new int[d - s + 1];
int k = 0;
while (v[i] >= vj]])
{
temp[k] = v[j];
j++;
k++;
if (j > d)
{
goto out;
}
}
while (true)
{
do
{
temp[k] = v[i];
i++;
k++;
if (i > m)
{
goto out;
}
}
while (v[i] < v[j]);
do
{
temp[k] = v[j];
j++;
k++;
if(j>d)
{
goto out;
}
}
while(v[i] >= v[j]);
}
out:
for ( ; i <= in; i++)
{
temp[k] = v[i];
k++;
}
for( ;j <=d;j++)
{
temp[k]
v[j];
=
'
k++;
}
memcpy(&v[s], temp, (d - s + 1) * sizeof(int));
delete temp;
}
In cazul cel mai favorabil, de exemplu cnd
elementele din ce! de-al doilea subvector sunt mai mici
toate dect oricare element din primul subvector, avem
2 comparaii pentru a muta fiecare element din cel de-al
doilea subvector. Dup care avem cte o comparaie
pentru a muta un element din primul subvector. innd
cont c fiecare subvector are n/2 elemente ajungem la
3/2n comparaii. Dac elementele sunt distribuite astfel
nct trebuie s lum cte un element din fiecare
subvector avem 2 comparaii pentru fiecare element,
deci un total de aproximativ 2n comparaii. Dar aceast
implementare este puin cam lung i nestructurat,
folosind instruciunea goto. Dac revenim la prima
variant vedem c defectul acesteia const n aceea c
dup ce se incrementeaz i sau j se verific dac nu s-au
int i = s; int j = m + 1;
int *temp = new int[d - s + 1];
int k = 0;
while (true)
{
if(v[i]<v[j])
{
temp[k] = v[i];
i++;
k++;
if(i>m)
{
break;
}
}
else
{
temp[k] = v[j];
k++;
if(j>d)
{
break;
}
}
}
for ( ; i <= m; i++)
{
temp[k] = v[i];
k++;
}
for (; j <= d; j++)
{
ternpjk] = v[j];
k++;
}
memcpy(&v[s], temp, (d - s + 1) * sizeof(int));
delete temp;
}
Este evident c pentru fiecare element mutat,
atunci cnd ambii subvectori mai au elemente, se
efectueaz 2 comparaii, dup care fiecare element, se
efectueaz o singur comparaie. Se mut n primul ciclu
inceput
// daca cel putin doua elemente vecine nu sunt in
ordinea ceruta variabila fanion sortat primeste valoarea
fals
daca v[i]>v[i+1] atunci sortat = fals ;
sfarsit
returneaza sortat
sfarsit.
Descrierea algoritmului in C :
bool este_sortat (float v[], int n)
{
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
bool sortat = true
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=0 ; i<n-1 ; i++)
{
// daca cel putin doua elemente vecine nu sunt in
ordinea ceruta variabila fanion sortat primeste valoarea
fals
if (v[i]>v[i+1]) sortat = false ;
}
return sortat ;
}
Exemplul 1. Se considera orice vector v cu n elemente
reale. Oricare ar fi vectorul furnizat, utilizandu-se
algoritmul de testare sortare cu bule, sa se determine
daca, este ordonat crescator.
#include <iostream.h>
#include <conio.h>
# define nmax 20
bool este_sortat (float v[], int n)
{
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
bool sortat = true;int i;
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=0 ; i<n-1 ; i++)
{
// daca cel putin doua elemente vecine nu sunt in
ordinea ceruta variabila fanion sortat primeste valoarea
fals
if (v[i]>v[i+1]) sortat = false ;
}
return sortat ;
}
void main ()
{
float v[100] ; int n,i ;char r='d' ;
while (r=='d')
{
// citirea dimensiunii vectorului
do
{
cout<<"\n dati numarul de componente ale
vectorului [0,"<<nmax<<"]=" ;
cin>>n;
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
if (este_sortat(v, n))
cout<<"\n vectorul dat este ordonat";
else
cout<<"\n vectorul dat nu este ordonat";
cout<<"\n continuati ?(d/n):";
cin>>r;
}
}
Tehnica prin care se atribuie unei variabile logice
valoarea true sau false, in functie de anumite conditii
intalnite intr-un algoritm, poarta numale de tehnica
if (v[i]>v[i+1])
{
aux = v[i]; v[i] = v[i+1];
v[i+1] = aux;
sortat = false ;
}
}
}
while (!sortat);
}
void main ()
{
float v[100] ; int n,i ;char r='d' ;
while (r=='d')
{
// citirea dimensiunii vectorului
do
{
cout<<"\n dati numarul de componente ale
vectorului [0,"<<nmax<<"]=" ;
cin>>n;
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// apelarea functiei de sortare cu bule
sortare_bule (v, n);
cout<<"\n vectorul ordonat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// afisarea numarului de comparari si treceri prin
vector
cout<<"\n
sortarea
s-a
facut
cu
"<<nr_comparari<<" comparari"
<<" si in "<<nr_treceri<<" treceri";
//
initializarea
numarului
de
comparari
si
treceri
nr_comparari = 0;
nr_treceri = 0;
cout<<"\n continuati ?(d/n):";
cin>>r;
}
}
Dandu-se cazul cel mai defavorabil, vectorul v = (5,
4, 3, 2, 1), ordonat descrescator, pentru ordonarea sa
crescatoare s-au efectuat 5 parcurgeri efectuandu-se 20
de comparari, iar pentru cazul cel mai favorabil, vectorul
v = (1, 2, 3, 4, 5), deja ordonat s-au efectuat 1
parcurgere si 4 comparari..
Sa analizam ce se intampla la o parcurgere a
elementelor vectorului. Presupinem ca cel mai mare
element se afla pe pozitia j, cu j < n-1. Cand se ajunge sa
se compare acest element cu vecinul sau aflat de pe
pozitia j + 1 se constata ca nu sunt asezate in ordine,
deoarece v[j] > v[j+1], si se va produce, conform
algoritmului,
o
interschimbare.
Dupa
aceasta
interschimbare, cel mai mare element, se afla pe pozitia
j + 1. La pasul urmator se compara elementul de pe
pozitia j + 1 cu cel de pe pozitia j + 2 si, deoarece nu
sunt aranjate in ordine, se va produce o noua
interschimbare.
Dupa
prima
parcurgere
exista
certitudinea ca cel mai mare element din vector se va
afla pe ultima pozitie, pe pozitia n 1. Dupa o noua
parcurgere al doilea element ca marime din vector se va
pozitiona pe penultima pozitie, acolo unde-i este locul.
Acest tip de pozitionare este, de fapt, motivul pentru
care algoritmul este numit sortarea cu bule, cele mai
mari elemente ies la suprafata in ordinea descrescatoare
a marimii lor .
In cazul cel mai defavorabil, cand vectorul
initial este ordonat descrescator, vor exista n 1
parcurgeri ale vectorului, Tinand seama ca dupa fiecare
parcurgere i, cel mai mare element va fi pozitionat pe
pozitia n i, la urmatoarea parcurgere ar fi inutila
compararea unui element cu elementele vectorului
aflate pe pozitiile mai mari ca n 1 i, deoarece acestea
{
// citirea dimensiunii vectorului
do
{
cout<<"\n dati numarul de componente ale
vectorului [0,"<<nmax<<"]=" ;
cin>>n;
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// apelarea functiei de sortare cu bule
sortare_optima1_bule (v, n);
cout<<"\n vectorul ordonat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// afisarea numarului de comparari si treceri prin
vector
cout<<"\n sortarea s-a facut cu "<<nr_comparari<<"
comparari"
<<" si in "<<nr_treceri<<" treceri";
// initializarea numarului de comparari si treceri
nr_comparari = 0;
nr_treceri = 0;
cout<<"\n continuati ?(d/n):";
cin>>r;
}
}
Dandu-se cazul cel mai defavorabil, vectorul v = (5,
4, 3, 2, 1), ordonat descrescator, pentru ordonarea sa
crescatoare s-au efectuat 4 parcurgeri efectuandu-se 10
de comparari, iar pentru cazul cel mai favorabil, vectorul
v = (1, 2, 3, 4, 5), deja ordonat s-au efectuat tot 4
parcurgeri si 10 comparari.. In raport cu prima varianta
a algoritmului se constata o eficientizare la cel de-al
doilea
algoritm
corespunzator
cazului
cel
mai
defavorabil.
Aceasta implementare a algotitmului de
sortare cu bule, are o parte buna, deoarece tine seama
de faptul ca dupa o parcurgere i elementul cel mai mare
va fi deplasat pe pozitia n 1 i, iar parcurgerea
urmatoare, i + 1 se va face pana la pozitia n 1 i,
exclusiv. Partea necorespunzatoare a algoritmului
consta in faptul ca vectorul va fi, in final, reparcurs, desi
acesta este deja sortat,
Pentru inlaturarea acestui neajuns, se va
proceda la urmatorea implementare pentru algoritmul
de sortare cu bule :
Descrierea in pseudocod a algoritmului functiei de
sortare cu bule cu un numar optim de treceri
functie sortare_optima2_bule_ (v, n)
inceput
j = 0 ; // contor trecere
repeta
inceput
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
sortat = adevarat
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
pentru i=0 la n-2 j executa
inceput
// daca cel putin doua elemente vecine nu sunt
in ordinea ceruta se interschimba elementele
// si variabila fanion sortat primeste valoarea
fals
daca v[i]>v[i+1] atunci
inceput
aux = v[i]
v[i] = v[i+1]
v[i+1] = v[i]
sortat = fals
sfarsit
sfarsit
j =j+1 // incrementare contor trecere
cat timp (sortat = fals)
sfarsit.
Descrierea algoritmului sortarii cu bule in C, cu un
numar optim de treceri :
void sortare_bule (float v[], int n)
{
bool sortat ; int i; float aux ;
int j = 0; // contor treceri
// bucla de testare cu interschimbare
do
{
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
bool sortat = true
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=0 ; i<n -1 j ; i++)
// daca cel putin doua elemente vecine nu sunt
in ordinea ceruta
// se interschimba elementele intre ele si
variabila fanion sortat primeste valoarea fals
if (v[i]>v[i+1])
{
aux = v[i] ; v[i] = v[i+1] ; v[i+1] = aux ;
sortat = false ;
}
j++ ; // incrementare contor treceri
}
while ( !sortat) ;
}
Un program complet se poate scrie, utilizand
aceasta implementare, asemanator cu exemplele
anterioare.
Algoritmul este eficient in cazul in care avem un sir deja
sortat in care au fost inserate cateva elemente la stanga
elementelor din vectorul initial sortat
In cazul sirului v = (1, 9, 6, 2, 3, 4, 5, 7) se evidentiaza
urmatoarele treceri:
Prima trecere : v = (1, 6, 2, 3, 4, 5, 7, 9)
A doua trecere: v = (1, 2, 3, 4, 5, 6, 7, 9)
A treia trecere detecteaza ca sirul este sortat
do
{
// parcurgerea sirului de la stanga la dreapta
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
bool sortat = true
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
for (i=primul ; i<ultimul; i++)
// daca cel putin doua elemente vecine nu sunt
in ordinea ceruta
// se interschimba elementele intre ele si
variabila fanion sortat primeste valoarea fals
if (v[i]>v[i+1])
{
aux = v[i] ; v[i] = v[i+1] ; v[i+1] = aux ;
sortat = false ;
}
ultimul -- ; // decrementare contor ultimei pozitii
// parcurgerea sirului de la dreapta la stanga
if ( !sortat)
{
sortat = true;
for (i=ultimul ; i>primul; i--)
// daca cel putin doua elemente vecine nu
sunt in ordinea ceruta
// se interschimba elementele intre ele si
variabila fanion sortat primeste valoarea fals
if (v[i]<v[i-1])
{
aux = v[i] ; v[i] = v[i-1] ; v[i-1] = aux ;
sortat = false ;
}
primul
++; //
incrementare
contorul
primei pozitii
}
}
while ( !sortat) ;
}
Un program complet este descries mai jos,
utilizand
aceasta
implementare,
asemanator
cu
exemplele anterioare.
}
ultimul-- ; // decrementare contor ultimei pozitii
nr_treceri++;
cout<<"\n vectorul dupa trecerea "<<nr_treceri<<':';
for (k = 0; k <= n - 1; k++) cout<<v[k]<<'
';
// parcurgerea sirului de la dreapta la stanga
if (!sortat)
{
sortat = true;
for (i=ultimul ; i>primul; i--)
{
// daca cel putin doua elemente vecine nu sunt in
ordinea ceruta
// se interschimba elementele intre ele si variabila
fanion sortat primeste valoarea fals
if (v[i]<v[i-1])
{
nr_comparari++;
aux = v[i] ; v[i] = v[i-1] ; v[i-1] = aux ;
sortat = false ;
}
}
primul++; // incrementare contorul primei pozitii
nr_treceri++;
cout<<"\n
vectorul
dupa
trecerea
"<<nr_treceri<<':';
for (k = 0; k <= n - 1; k++) cout<<v[k]<<'
';
}
}
while (!sortat);
}
void main ()
{
float v[100] ; int n,i ;char r='d' ;
// redirectarea iesirii stdout intr-un fisier text, fisout
// in scopul listarii ulterioare a rezultatelor
// if((freopen("fisout","wt",stdout))==NULL)
// {
// printf("\n eroare de deschidere");
//
exit(1);
// }
while (r=='d')
{
// citirea dimensiunii vectorului
do
{
cout<<"\n dati numarul de componente ale
vectorului [0,"<<nmax<<"]=" ;
cin>>n;
}
while ((n<0) || (n>nmax)) ;
// citirea componenteler vectorului
for (i=0 ; i<n ; i++)
{
cout<<"v["<<i<<"]=";
cin>>v[i];
}
// afisarea rezultatului testarii
cout<<"\n vectorul dat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// apelarea functiei de sortare cu bule
sortare_alternanta_bule (v, n);
cout<<"\n vectorul ordonat:" ;
for (i=0 ; i<n ; i++) cout<<v[i]<<' ' ;
// afisarea numarului de comparari si treceri prin
vector
cout<<"\n
sortarea
s-a
facut
cu
"<<nr_comparari<<" comparari"
<<" si in "<<nr_treceri<<" treceri";
// initializarea numarului de comparari si
treceri
nr_comparari = 0;
nr_treceri = 0;
cout<<"\n continuati ?(d/n):";
cin>>r;
}
}
Mai jos, sunt date cateva rezultate obtinute in urma
executarii programului de mai sus :
Sortarea unui vector anterior sortat in care s-au inserat
la stanga componentele 8 si 6
v[2]=3
v[3]=9
v[4]=7
v[5]=4
v[6]=5
v[7]=6
vectorul dat:1 2 3 9 7 4 5 6
vectorul dupa trecerea 1:1 2 3 7 4 5 6 9
vectorul dupa trecerea 2:1 2 3 4 7 5 6 9
vectorul dupa trecerea 3:1 2 3 4 5 6 7 9
vectorul dupa trecerea 4:1 2 3 4 5 6 7 9
vectorul ordonat:1 2 3 4 5 6 7 9
sortarea s-a facut cu 7 comparari si in 4 treceri
continuati ?(d/n):n
Algoritmul de sortare cu bule a unui sir cu parcurgerea
alternata a sirului pana la ultima interschimbare
Algoritmului anterior i se poate aduce o ultima
imbunatatire, daca se tine cont de pozitia (indexul) la
care are loc ultima interschimbare. Daca la o parcurgere,
de la stanga la dreapta sau de la dreapta la stanga,
ultima interschimbare are loc de pe pozitiile p si p+1,
respectiv p si p-1, atunci se poate spune ca elementele
cu indexul mai mare ca p, respectiv mai mic ca p, sunt
deja ordonate.
Descrierea in pseudocod a algoritmului :
functie sortare_alternanta_bule_ (v, n)
inceput
primul = 0 ; // precizarea primului element
ultimul = n - 1 ; // precizarea ultimului element
repeta
inceput
// parcurgerea sirului de la stanga la dreapta
// se initializeaza variabila sortat cu adevarat,
considerandu-se apriorii vectorul sortat
sortat = adevarat
// se compara doua cate doua elementele vecine
pentru orice i din intervalul [0, n-1]
pentru i=0 la n-2 j executa
inceput
// daca cel putin doua elemente vecine nu sunt
in ordinea ceruta se interschimba elementele
i++;
}
while (x < v[j])
{
j--;
}
if('<j)
{
interschimba(v[i], vfj]);
i++;
j--;
}
else
{
return j;
}
} while (true);
}
Dac v[i] este mai mic dect elementul de referin
atunci cu certitudine el face parte din primul subvector,
altfel, este un element care poate fi plasat n cel de-al
doilea subvector. Primul ciclu caut elemente mai mari
sau egale cu elementul de referin.
Dac v[j] este mai mare dect elementul de referin
atunci cu certitudine el face parte din cel de-al doilea
subvector, altfel, este un element care poate fi plasat n
primul subvector. Cel de-al doilea ciclu caut elemente
mai mici sau egale cu elementul de referin.
Primul lucru pe care trebuie s-1 demonstrm este c
cele dou cicluri se termin. Prima valoare a lui i pentru
care se termin primul ciclu este chiar st deoarece
acesta este elementul de referin. Avem mai multe
cazuri:
Cazul 1: x este cel mai mic element din vector, ne mai
existnd nici un element egal cu el.
In cazul n care x este cel mai mic element din vector,
neexistnd nici un element egal cu.el, cel de-al doilea
ciclu se termin tot cu j egal cu st. In acest caz
partiionarea se ncheie, cel mai din dreapta element al
subvectorului stng fiind cel de pe poziia j.
Cazul II: Exist cel puin un element mai mic sau egal cu
x.
{
if v[j] < v[indexMinim])
{
indexMinim = j; )
}
}
if (indexMinim != i)
{
interschimba(v[i], vfindexMinim]);
}
}
}
Uneori este preferat urmtoarea variant:
void sortareSelectie2(int v[], int n)
{
int i; int j;
for (i = 0; i < n - 1; i++)
{
for(j = i + l;j<n;j++)
{
if(v[j]<v[i])
{
interschimba(v[i], v[j]);
}
}
}
}
Aceast variant este mai compact, dar mult mai
lent, nlocuinclu-se o singur instruciune de atribuire
cu trei instruciuni similare.
Algoritmul, n prima sa variant, efectueaz pentru
fiecare i, n-i-1 comparaii i cel mult o interschimbare.
Deci avem (n - 1) + (n - 2) + ... + 2 + 1 = n * ( n - 1) / 2
comparaii
i maxim n- 1 interschimbri. Avantajul acestui algoritm,
comparativ cu sortarea cu bule, const n faptul c
efectueaz puine deplasri de date.
2.2.4.
Sortarea Heap
Aceast sortare este o mbuntire a seleciei directe.
Este evident c un algoritm de selectare a maximului
dintr-un vector de n elemente trebuie s efectueze cel
temp = v[i];
for (k = i-pas; k >= j; k =k-pas)
{
v[k + pas] = v[k];
}
v[j] = temp;
}
}
}
void sortareShell(int v[], int n, int pas[], int nrPasi)
{
int i;
int j;
for (i=nrPasi-1; i >= 0; i--)
{
for(j = 0;j<pas[i];j++)
{
sorteazaSubsir(v, j, pas[i], n);
}
}
}
Pentru exemplificare ne propunem s sortm irul:
16 15 14 13 12 II 10 9 8 7 6 5 4 3 2 1 cu
paii: 1, 3, 5, 7
Dup sortarea cu pasul 7 s-au efectuat 11 deplasri:
2 1 7 6 5 4 3 9 8 14 13 12 11 10 16 15
Dup sortarea cu pasul 5 s-au efectuat 11 deplasri:
2 17 6 5 4 3 9 8 14 13 12 11 10 16 15
Dup sortarea cu pasul 3 s-au efectuat 15 deplasri:
2 1 4 3 5 7 6 9 8 11 10 12 14 13 16 1
5
Dup sortarea cu pasul 1 s-au efectuat 22 deplasri:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Se observ c sortarea cu. pasul 5 nu are nici un efect.
Timpul de rulare depinde att de numrul de pai ct i
de valorile pe care le au aceti pai. A. A. Papernov i G.
V. Stasevici au demonstrat urmtoarea teorem:
Timpul de rulare al sortrii Shell este 0(n 3/2) atunci
cnd paii sunt generai cu urmtoarea formula: h s =
2x+1 + 1, cu 0 < s < t= log2n.
2.2.6.
Sortarea prin Fuziune.
Sortarea prin fuziune (MergeSort), ca i sortarea rapid,
folosete divide el impera:
1. Divide: vectorul V, format din elementele cu indecii
ntre s i d, notat n continuare prin V[s..d], este mprit
pe jumtate n doi subvectori V[s..m] i V[m+I..d],
2. Impera: cele dou pri sunt sortate apelnd recursiv
pn cnd se ajunge la poriuni formate dintr-un singur
element.
3. Combin: se combin cele dou jumti innd cont
de faptul c fiecare snbvector este sortat. Spre
deosebire de sortarea rapid, unde greul cade pe etapa
de mprire, aici partea mai complicat este fcut n
etapa de combinare a rezultatelor.Implementarea
algoritmului este prezentat n continuare:
void sortareFuziuneOnt v[], ini s, int d)
{
int m = (s + d) / 2;
if (s < m)
{
sortareFuziune(v, s, m);
}
if(m+l<d)
{
sortareFuziune(v, m + 1, d);
}
coinbina(v, s, m, d);
}
Descrierea algoritmului :
Urmtorul algoritm primete vectorul v[st..dr] cu
elementele din subvectorii v[st...m], v[m+l..dr] ordonate
cresctor i are ca efect combinarea celor doi subvectori
astfel nct ntreg vectorul, de la st la dr, s fie ordonat
cresctor, folosind un vector temporar.
temp[k] = v[i];
}
else
{
temp[k] = v[j];
j++;
}
k++;
}
for ( ; i <= m; i++)
{
temp[k] = v[i]; k++;
}
for ( ; j <= d; j++)
{
temp[k] = v[j];
k++;
}
memcpy(&v[s], temp, (d - s + 1) * sizeof(int));
delete temp;
}
Algoritmul este simplu: se ncepe cu capetele din
stnga ale subvectorilor, i i j fiind indicatori ctre ceie
mai mici elemente din subvectori care nu au fost nc
mutate n vectorul temporar. Ct timp ambii subvectori
mai au elemente care nu au fost nc mutate se va muta
n vectorul temporar cel mai mic dintre v[i] i v[j], dup
care se incrementeaz indexul corespunztor. Indexul k
arat prima poziie liber din vectorul temporar i firete
c va fi incrementat dup fiecare mutare. Dup ce se
termin unul din subvectori se vor muta toate
elementele rmase din cellalt subvector.
Adncimea arborelui'de partiionare este de log 2 n,
indiferent de dispunerea elementelor vectorului. La
fiecare nivel avem n atribuiri i n cel mai defavorabil caz
3n comparaii, iar n cel mai favorabil caz 2n comparaii.
Numrul mare de comparaii se datoreaz faptului c
pentru a selecta elementul care trebuie mutat n
vectorul temporar, atunci cnd ambii vectori mai au
elemente care nu au fost mutate, se fac trei comparaii:
dou comparaii pentru a se verifica dac ambii
{
int i = s;
int j = m + 1;
int *temp = new int[d - s + 1];
int k = 0;
while (v[i] >= vj]])
{
temp[k] = v[j];
j++;
k++;
if (j > d)
{
goto out;
}
}
while (true)
{
do
{
temp[k] = v[i];
i++;
k++;
if (i > m)
{
goto out;
}
}
while (v[i] < v[j]);
do
{
temp[k] = v[j];
j++;
k++;
if(j>d)
{
goto out;
}
}
while(v[i] >= v[j]);
}
out:
for ( ; i <= in; i++)
{
temp[k] = v[i];
k++;
}
for( ;j <=d;j++)
{
temp[k]
v[j];
=
'
k++;
}
memcpy(&v[s], temp, (d - s + 1) * sizeof(int));
delete temp;
}
In cazul cel mai favorabil, de exemplu cnd
elementele din ce! de-al doilea subvector sunt mai mici
toate dect oricare element din primul subvector, avem
2 comparaii pentru a muta fiecare element din cel de-al
doilea subvector. Dup care avem cte o comparaie
pentru a muta un element din primul subvector. innd
cont c fiecare subvector are n/2 elemente ajungem la
3/2n comparaii. Dac elementele sunt distribuite astfel
nct trebuie s lum cte un element din fiecare
subvector avem 2 comparaii pentru fiecare element,
deci un total de aproximativ 2n comparaii. Dar aceast
implementare este puin cam lung i nestructurat,
folosind instruciunea goto. Dac revenim la prima
variant vedem c defectul acesteia const n aceea c
dup ce se incrementeaz i sau j se verific dac nu s-au
terminat ambii subvectori n loc s se verifice numai
subvectorul al crui cap a fost "ars".
Descrierea algoritmului cu revenire la prima varianta
Urmtorul algoritm primete vectorul v[st..dr] cu
elementele din subvectorii v[st..m], v[m+l..dr] ordonate
cresctor i are ca efect combinarea celor subvectori
astfei nct ntreg vectorul, de la st la dr, s fie ordonate
cresctor, folosind un vector temporar.
P1. Se efectueaz urmtoarele iniializri: i = st, j = m +
1 i k = 0.
P2. Dac v[i] >= v[j] se trece la pasul P7, altfel se trece
la. pasul P3.
P3. Se efectueaz atribuirea temp[k] = v[i].
P4. Se incrementeaz k.
P5. Se incrementeaz i.
P6. Dac k > m se continu cu pasul P1l, altfel
continu cu pasul P2.
P7. Se efectueaz atribuirea temp[k] = v[j].
P8. Se incrementeaz k.
P9. Se incrementeaz j.
P10. Dac j > d se continu cu pasul P11, altfel
continu cu pasul P2.
P1l. Dac i > m se continu cu pasul P16, altfel
continu cu pasul P12.
P12. Se efectueaz atribuirea temp[k] = v[i].
P13. Se incrementeaz i.
P14. Se incrementeaz k.
P15. Se continu cu Pil.
P16. Dac j > dr se continu cu pasul P21, altfel
continu cu pasul P17.
P17. Se efectueaz atribuirea temp[k] = v[j].
P18. Se incrementeaz j.
P19. Se incrementeaz k.
P20. Se continu cu P16.
P21. Se copiaz coninutul vectorului temp n
ncepnd cu adresa lui v[st] i algoritmul se ncheie.
se
se
se
se
v,
}
else
{
temp[k] = v[j];
k++;
if(j>d)
{
break;
}
}
}
for ( ; i <= m; i++)
{
temp[k] = v[i];
k++;
}
for (; j <= d; j++)
{
ternpjk] = v[j];
k++;
}
memcpy(&v[s], temp, (d - s + 1) * sizeof(int));
delete temp;
}
Este evident c pentru fiecare element mutat,
atunci cnd ambii subvectori mai au elemente, se
efectueaz 2 comparaii, dup care fiecare element, se
efectueaz o singur comparaie. Se mut n primul ciclu
ce! puin n/2 elemente i cel mult n - 1 de unde putem
obine numrul de comparaii pentru cazul cel mai
favorabil i cel mai defavorabil. Atunci cnd nu ne
permitem alocarea unui vector temporar, putem
interclasa
cei
doi
subvectori
ntr-o
manier
asemntoare cu inseria direct:
Descrierea algoritmului cu interclasarea vectorilor
asemanator insertiei directe
.
Urmtorul algoritm primete vectorul v[st..dr]
cu elementele din subvectorii v[st..m], v[m+i..dr]
ordonate cresctor i are ca efect combinarea celor
subvectori astfel nct ntreg vectorul, de la st la dr, s
fie ordonate cresctor, folosind un vector temporar.
P1. Se iniializeaz i cu st i j cu m + 1.
P2. Dac j > dr algoritmul se ncheie, altfel se continu
cu pasul P3.
P3. Dac v[i] >= v[j] se continu cu pasul P6, altfel se
continu cu pasul P4.
P4. Se incrementeaz i.
P5. Se continu cu pasul P3.
P6. Dac i este egal cu j algoritmul se ncheie, altfel se
continu cu pasul P7.
P7. Se atribuie lui temp valoarea v[j] i lui k valoarea j 1.
P8. Dac avem k < i se sare la pasul P12, altfel se
continu cu pasul P9.
P9. Se efectueaz atribuirea v[k + 1] = v[k].
P10. Se decrementeaz k.
P1l. Se continu cu pasul P8.
P12. Se efectueaz atribuirea v[i] = temp.
P13. Se incrementeaz i.
P14. Se incrementeaz j.
P15. Se continu cu pasul P2.
Se iau pe rnd elementele celui de-ai doilea
subvector, ncepnd cu cel mai mic element, i se caut
poziia n care trebuie inserate n partea stng, innd
cont de faptul c toate elementele clin partea stng
sunt ordonate cresctor. In momentul n care se gsete
un element din subvectorul drept care se afl deja pe
poziia corect algoritmul se ncheie, Dac elementul v[j]
trebuie inserat pe o poziie i oarecare, mai nti i se face
loc, deplasndu-se elementele din intervalul [i, j - 1] cu o
poziie spre dreapta, dup care are loc inserarea. Se
continu cu urmtorul element din subvectorul drept,
poziia acestuia cutndu-se ncepnd cu poziia clin
dreapta celei pe care a avut oc ultima inserare.
Descrierea algoritmului cu interclasarea
asemanator insertiei directein C
void combina5(int v[], int s, int m, int d)
{
int i = s;
int j ;
int k;
vectorilor
int temp;
for (j = m + 1; j <= d; j++)
{
while (v[i] < v[j])
{
i++;
}
if(i==j)
{
break;
}
temp = v[j];
for(k = j- l;k>=i;k--)
{
v[k+l] = v[k];
}
v[i] = temp;
i++;
}
}
In cazul cel mai favorabil, cnd elementele din
stnga sunt mai mici dect cel mai mic element din
dreapta avem n/2 + 2 comparaii i nici o deplasare. Dar
dac este valabil condiia inversa avem n/2(n/2 + 2)
comparatii si n/2(n/2 + 1) deplasari.Timpul mediu este
de ordinul O(n2).
In final, trebuie s se in cont de faptul c
adncimea arborelui de partiionare este log2n i c
pentru fiecare nivel trebuie s adugm costul operaiei
de combinare. Deci, atunci cnd putem folosi vectori
temporari algoritmul are ordinul 0(nlog 2n) i 0(n2log2n)
n cazul folosirii ultimei variante a algoritmului de
interclasare.
2.2.7. Sortarea prin Numrare.
Algoritmul este foarte simplu: mai nti se
determin poziia fiecrui element al vectorului ntr-un
vector sortat dup care se plaseaz elementele pe
poziiile calculate ntr-un vector temporar. Dun care se
copiaz elementele din vectorul temporar la loc n
vectorul iniia!.
Descrierea algoritmului :
Dndu-se vectorul v cu n elemente, urmtorul
algoritm rearanjeaz elementele vectorului astfel nct
ia final acestea s fie n ordine, v[0] v[l] ... v[n - 2]
v[n 1].
P1. Se iniializeaz i cu 0.
P2. Dac i este egal cu n se continu cu P6, altfel se
continu cu pasul P3.
P3. Se iniializeaz poziie[i] cu 0.
P4. Se incrementeaz i.
P5. Se continu cu pasul P2.
P6. Dac i este egal cu n - 1 se continu cu P14, altfel se
continu cu pasul P7.
P7. Se iniializeaz j cu i + 1.
P8. Dac j este egal cu n se continu cu P12, altfel se
continu cu pasul P9.
P9. Dac v[i] > v[j] se incrementeaz poziie[i], altfel se
incrementeaz poziie[j],
P10. Se incrementeaz j.
P11. Se continu cu pasul P8.
P12. Se incrementeaz i.
P13. Se continu cu pasul P6.
P14. Se iniializeaz i cu 0.
P15. Dac i este egal cu n se continu cu P19, altfel se
continu cu pasul P16.
P16. Se iniializeaz temp[poziie[i]] cu v[i].
P17. Se incrementeaz i.
P18. Se continu cu pasul P15.
P19. Se copiaz temp n v i algoritmul se ncheie.
Descrierea algoritmului in C :
void sortareNumarare(int v[], int n)
{
int i;
int j;
int *pozitie = new int[n];
int *temp = new int[n];
for (i = 0; i < n; i++)
{
Pozitie[i] = 0;
}
for (i = 0; i < n - 1; i++)
{
for (j = i + l;j <n;j++)
{
if(v[i]>v[j])
{
pozitie[i]++;
}
else
{
pozitie[j]++;
for (i = 0; i < n; i++)
{
temp[pozitie[i]] = v[i];
}
}
}
memcpy(v, temp, n * sizeof[int));
delete poziie;
delete temp;
}
Se compar fiecare dou elemente, prin urmare
primul element se compar cu n - 1 elemente, al doilea
element cu toate cele n - 2 elemente aflate n dreapta lui
i
aa
mai
departe,
n
total
avem
n(n
acestea s fie n ordine, v[0] v[l] ... v[n -2] <v[n ]].
P1. Se iniializeaz i cu 0.
P2. Dac i este mai mare dect k se trece la pasul P6,
altfel se trece la pasul P3.
P3. Se iniializeaz apariii[i] cu 0.
P4. Se incrementeaz i.
P5. Se continu cu pasul P2.
P6. Se iniializeaz i cu 0.
P7. Dac i este egal cu n se trece la pasul P1l, altfel se
trece la pasul P8.
P8. Se incrementeaz apariii[v[i]].
P9. Se incrementeaz i.
P10. Se continu cu pasul P7.
P11. Se decrementeaz apariii[0].
P12. Se iniializeaz i cu 1.
P13. Dac i este mai mare dect k se trece la pasul P17,
altfel se trece la pasul P14.
P14, Se efectueaz atribuirea apariii[i] = apariii[i] +
apariii[i - 1].
P15, Se incrementeaz i.
P16. Se continu cu pasul P13.
P!7. Se iniializeaz i cu 0.
P18. Dac i este egal cu n se trece la pasul P23, altfel se
trece la pasul P19.
P19, Se efectueaz atribuirea temp[apariii[v[i]]] = v[i].
P20. Se decrementeaz apariii[v[i]].
P21. Se incrementeaz i.
P22. Se continu cu pasul P18.
P23. Se copiaz coninutul vectorului temp n vectorul v
i algoritmul se ncheie.
.
Mai nti se iniializeaz elementele vectorului
apariii cu 0. Apoi pentru fiecare element al vectorului v
se incrementeaz elementul corespunztor din vectorul
apariii. In urmtoarea etap, apariii[v[i]] va conine
indexul pe care l va avea n vectorul sortat ultimul
element cu valoarea v[i]. De aceea, dup ce se
efectueaz atribuirea temp[apariii[v[i]]] = v[i] se
decrementeaz
apariii[v[i]],
deoarece
urmtorul
element cu aceeai valoare va fi amplasat n stnga.
Descrierea algoritmului in C :
void sortareNumai'areDistributii(int v[], ini n, int k)
{
int i;
int *apariii = new int[k + 1];
int *ternp = new int[n];
for (i = 0; i <= k; i++)
{
aparitii [i] = 0;
}
for(i =0; i < n; i++)
{
aparitii[v[i]]++;
}
aparitii[0]--;
for (i = 1; i <= k; i++)
{
aparitii[i] = aparitii[i] + aparitii[i - 1];
}
for(i = 0; i < n; i++)
{
temp[aparitii[v[i]]] = v[i];
aparitii[v[i]--;
}
memcpy(v, temp, n * sizeof(int));
delete temp;
delete apariii;
}
Algoritmul are un timp de execuie ele ordinul O(max(n,
k + 1)).