algoritmi sortiranja u c++

30
Skripta Algoritmi I structure podataka u C++ “Algoritmi sortiranja” Autor: Adnan Goretić

Upload: adnan-goretic

Post on 24-May-2015

1.586 views

Category:

Software


7 download

DESCRIPTION

Algoritmi i strukture podataka u C++ Tema: Algoritmi sortiranja: Selection Sort, Bubble Sort, Insertion Sort, Shell Sort, Merge Sort, Quick Sort, Tree sort, Heap sort , Counting sort, Radix sort

TRANSCRIPT

Page 1: Algoritmi sortiranja u C++

SkriptaAlgoritmi I structure podataka u C++

“Algoritmi sortiranja”

Autor: Adnan Goretić

Mail: [email protected]

Facebook: https://www.facebook.com/ado.svdc

Page 2: Algoritmi sortiranja u C++

SADRŽAJ

1. ALGORITMI SORTIRANJA (UVOD) 3

2.SORTIRANJE ZAMJENOM ELEMENATA 4

2.1 Sortiranje izborom najmanjeg elementa (Selection Sort) 3

2.2 Sortiranje zamjenom susjedih elemenata (Bubble Sort) 5

3. SORTIRANJE UMETANJEM 6

3.1 Jednostavno sortiranje umetanjem (Insertion Sort) 6

3.2 Višsestruko sortiranje umetanjem (Shell Sort) 7

4. REKURZIVNI ALGORITMI SORTIRANJA 9

4.1 Pomoćna procedura spajanja (Merge) 9

4.2 Sortiranje spajanjem (Merge Sort) 9

4.3 Brzo sortiranje (Quick Sort) 11

5. SORTIRANJE POMOĆU BINARNOG STABLA 13

5.1 Sortiranje obilaskom binarnog stabla traženja (Tree sort) 13

5.2 Sortiranje pomocu hrpe (Heap sort ) 14

6.SORTIRANJE U LINEARNOM VREMENU 16

6.1 Sortiranje brojanjem (Counting sort) 16

6.2 Radix sort 18

7. PITANJA I ZADACI 20

8. ZAKLJUČAK 20

9. LITERATURA 20

Autor: Adnan Goretić ([email protected])

Page 3: Algoritmi sortiranja u C++

1. ALGORITMI SORTIRANJA

Uvod:

Prema definiciji algoritama sortiranja, oni predstavljaju svaki algoritam koji riješava problem sortiranja. U ovom radu ćemo se baviti algoritmima koji svoj rad temelje na:

1) Sortiranje zamjenom elementa

2) Sortiranje umetanjem

3) Sortiranje rekurzivnim pozivom

4) Sortiranja pomoću binarnih stabala

5) Sortiranje u linearnom vremenu

2. SORTIRANJE ZAMJENOM ELEMENATA

Ovi algoritmi predstavljaju najjednostavnije algoritme za sortiranje. U ovu skupinu algoritama izmeđuostalog spadaju: Selectrion sort (sortiranje izborom najmanjeg elementa) teBubble sort (sortiranje zamjenom susjednih elemenata)

2.1 Selection Sort

Način rada:Algoritam svoj rad započinje tako sto se prvo pronadje najmanji element u nizu a onda se pronađeni element zamjeni za prvim. Ovaj se postupak ponavlja toliko puta kolika je dužina niza, bez obzira da li je niz već sortiran ili ne. Ovaj postupak se može vidjeti na slijedećoj slici (slika br.1).

Autor: Adnan Goretić ([email protected])

Page 4: Algoritmi sortiranja u C++

Slika br.1 (prikaz rada selection sort-a) [1]

Algoritam se može implementirati na slijedeći način:

void selectionSort (int *a, int n) {int i, j, min;for (i = 0; i < n; i++) {min = i;

for (j = i+1; j < n; j++)if (a[ j ] < a[min]) min = j;zamjena(&a[i], &a[min]);}}

Algoritam koristi I pomoćnu funkciju zamjena čiji je kod slijedeći:

void zamjena (int *x, int *y){int aux;aux = *x;*x = *y;*y = aux;}

Što se tiče vremenske složenosti algoritma, možemo reći da je algoritam uglavnom spor, I da vrši poređenje bez obzira na početno stanje niza tj. bez obriza da li je niz već sortiran ili ne. Ukupan broj operacija se može zapisati I matematičkom forulom:

n(n — 1)/2 + 3(n — 1) = O(n2).

Autor: Adnan Goretić ([email protected])

Page 5: Algoritmi sortiranja u C++

2.2 Bubble Sort

Algoritam radi na slijedeći način:Prolazi nizom od početka prema kraju I uspređuje susjedne elemente. Ako je neki element veći od slijedećeg izvrši se zamjena. Kada dođe do kraja niza najveća vrijednost će biti na zadnjem mjestu. Jedina prednost u pogledu na selection sort, ovaj algoritam se može zaustaviti čim se ustanovi da nema više elemenata za zamjenu. Ova činjenica neće promjeniti ocjenu vremenske složenosti, ali će u velikoj mjeri doprinjeti brzem radu algoritma.

Način rada algoritma možemo primjetiti na slijedećoj slici (slika br.2).

Slika br.2 ( prikaz rada bubble sort algoritma) [1]

Autor: Adnan Goretić ([email protected])

Page 6: Algoritmi sortiranja u C++

Algoritam se može implementirati slijedećim kodom:

void bubbleSort (int *a, int n){int i, j;for (i = 0; i < n-1; i++){for (j = 0; j < n-1-i; j++){if (a[j+1] < a[j])zamjena(&a[j], &a[j+1]);}}};Ova verzija algoritma se može poboljšati, na taj način što će se sortiranje završiti čim se ustanovi da nema više elemenata za zamjenu. Kod poboljšane verzije izgleda ovako:

void bubbleSort (int *a, int n){int i, j, chg;for (i = 0, chg = 1; chg; i++) {chg = 0;for (j = 0; j < n-1-i; j++)if (a[j+1] < a[j]) {zamjena(&a[j], &a[j+1]);chg = 1;}}};Analiza vremenske složenosti ovog algoritma bi se mogla prikazati matematičkom formulom:

4(n − 1) + 4(n − 2) + · · ·+ 4·1 = 4n(n − 1)/2 = 2n(n − 1).

U svakom slučaju ocjena je ista kao i kod selection sort-a, značiO(n2).

3. SORTIRANJE UMATANJEM

Rad ovih algoritama se svodi na umetanje novog elementa u već sortirani niz. U ovoj skupini algoritama imamo:Insertion sort (jednostavno sortiranje umetanjem) i Shell sort (višestruko sortiranje umetanjem)

3.1 Insertion Sort

Rad ovog algoritma se može objasiniti na slijedeći način: algoritam uzima prvi element iz ne sortiranog dijela te ga umetne u sortirani dio na svoje odgovarajuće mjesto. Sa ovom operacijom se dužina sortiranog dijela niza sa svakom interacijom povećava za 1 dok se dužina ne sortiranog dijela smanjuje za 1. Princip rada se može vidjeti na slijedećoj slici.

Autor: Adnan Goretić ([email protected])

Page 7: Algoritmi sortiranja u C++

Slika br.3 (prikaz rada Insertion sort-a) [1]

Insertion sort se može implementirati slijedećim kodom:

void insertionSort (int *a, int n){int i, j;int aux;for (i = 1; i < n; i++) {aux = a[i];for (j = i; j >= 1 && a[j-1] > aux; j--)a[j] = a[j-1];a[j] = aux;}};

Što se vremenske složenosti tiče, ovaj algoritam ima istu ocjenu kao selection I bubble sort tj. O(n2) ali se u praksi ipak pokazao mnogo bržim nego što je to prikazanom ocjenom brzine.

3.2 Shell Sort

Shell sort se bazira na Inserion sort-u. To se najbolje vidi iz koda Shell sort-a gdje je dodana samo jedna dodatna for petlja. Ovaj algoritam radi tako što početni niz razdvaja na grupe (tzv.subsort-ove) koji su međusobno udaljeni razmakom kojeg ćemo označiti sa k. Sada se na svakoj od ovih grupa primjenjuje Insertion sort. Ovim radnjama se smanjuje broj grupa ali se povćava broj elemenata u grupi. Svaka slijedeća grupa ima veći broj elemenata sa većim stepenom uređenosti. Rad algoritma se vidi na slijedećoj slici.

Autor: Adnan Goretić ([email protected])

Page 8: Algoritmi sortiranja u C++

Slika br.4 (prikaz rada Shell sort-a) [1]

Algoritam se može implementirati slijedećim kodom:

void ShellSort (int *a, int n){int i, j, step;int aux;for (step = n/2; step > 0; step /= 2) {for (i = step; i < n; i++) {aux = a[i];for (j = i; j >= step && a[j-step] > aux; j -= step)a[j] = a[j-step];a[j] = aux;}}};

Kao što možemo primjetiti, nije bilo velikih izmjena u odnosu na Insertion sort osim jedne dodatne for petlje. Što se tiče složenosti algoritma, on predstavlja veliku nepoznanicu za istraživače.Za ovaj algoritam još uvijek ne postoje jasne ocjene složenosti. Iz razpoloživih ocjena može se zaključiti da je složenost u najgorem slučaju O(n3/2) što je bolje nego kod dosada opisanih algoritama. Eksperimentalna mjerila pokazuju da je algoritam izuzetno brz u svakom slučaju brži nego što to pokazuju raspoložive cojene.

Autor: Adnan Goretić ([email protected])

Page 9: Algoritmi sortiranja u C++

4. REKURZIVNI ALGORITMI SORTIRANJA

Rekurzivni algoritmi sortiranja su algoritmi koji u svrhu sortiranja pozivaju sami sebe. U ovu grupu spadaju:Merge sort (sortiranje spajanjem) i Quick sort (brzo sortiranje)

4.1 Pomoćna procedura spajanja (Merge)

Ovaj postupak se odnosi na spajanje dva manja niza u jedan veći niz. Ovaj postupak predstavlja pomoćnu proceduru u radu Merge sort-a. Može se implementirati sljedećim kodom:

void merge (int *c, int *aux, int l, int k, int r) {int i, k1, count, aux;k1 = k-1; k2 = l; count = r-l+1; while (l <= k1 && k <= r) { if (c[l] <= c[k]){aux[k2++] = c[l++];}else{aux[k2++] = c[k++];}}while (l <= k1) aux[k2++] = c[l++];while (k <= r) aux[k2++] = c[k++];for (i = 0; i < count; i++, r--),c[r] = aux[r];}

4.2 Sortiranje spajanjem (Merge Sort)

Pored toga što algoritam radi pomoću rekurzije, njegov rad se isto bazira na dijeljenju niza na dva manja niza podjednakih dužina, nad kojima se vrši rekurzivni poziv Merge sort-a. Nakon toga se ta dva manja niza spajaju u jedan. Princip rada algoritma se može najbolje uočiti na slijedećoj slici.

Autor: Adnan Goretić ([email protected])

Page 10: Algoritmi sortiranja u C++

Slika br.5 (prikaz rada Merge sort algoritma) [1]

Sam kod algoritma igleda ovako:

void mergeSort (int *a, int *aux, int left, int right) {int mid;if (left < right){mid = (left + right) / 2;mergeSort (a, aux, left, mid);mergeSort (a, aux, mid+1, right);merge (a, aux, left, mid+1, right);}}

Vremenska analiza složenosti Merge sort-a se može matematički prikazati formulom:

(log2 n + 1) · O(n) = O(n log n).

Zahvaljujući ovakvoj ocjeni radi se o jednom od najbrzih poznatih algoritama za sortiranje. Prednost ovog algoritma prema drugim je ta što se može sortirati velika količina podataka koji su pohranjeni na nekoj vanjskoj memoriji računara. Dok manu ovog algoritma čini veliki utrošak memorije zbog kreiranja dodatnog niza spajanja potrebnog u procesu sortiranja.

Autor: Adnan Goretić ([email protected])

Page 11: Algoritmi sortiranja u C++

4.3 Quick Sort

Quick sort radi na slijedećem principu: izabere se jedan element u nizu, tzv.pivot. Svi ostali elementi se razvrstavaju prema njemu zavisno o tome da li su veći ili manji od pivota.

Slika br.6 (podijela niza preko pivota) [1]

Da bi se niz moga do kraja sortirati, mora se izvrsiti rekurzivni poziv Quick sor algoritma na ljevi niz odnosno manje od pivota, te na desni niz tj.veći od pivota.

Slika br.7 (zamjena elemenata preko pivota) [1]

Autor: Adnan Goretić ([email protected])

Page 12: Algoritmi sortiranja u C++

Ovaj postupak se može ilustrovati na slijedeći način.

Slika br.8 (sortiranje niza pomoću Quick sort-a) [1]

Quick sort se može implementirati slijedećim kodom:

void quickSort(int *a, int l, int r) {int i,j;if (l>=r) return;i = l+1; j = r;while ((i <= j) && (i<=r) && (j>l)) {while ((a[i] < a[l]) && (i<=r)) i++;while ((a[j] > a[l]) && (j>l)) j--;if (i<j)zamjena(&a[i], &a[j]);}if (i>r) { zamjena(&a[r], &a[l]);quickSort(a, l, r-1);}else if (j<=l) quickSort(a, l+1, r);}else { zamjena(&a[j], &a[l]);quickSort(a, l, j-1);quickSort(a, j+1, r);}

Autor: Adnan Goretić ([email protected])

Page 13: Algoritmi sortiranja u C++

Vremenska analiza složenosti pokazuje da je prosječno vrijeme izvšavanja Quick sort algoritma O(n logn). Ova ocjena zavisi uglavnom od izabira pivota. U najčešćem slucaju pivot se bira na mjestu prvog, zadnjeg ili nekog od elementa u sredini niza.

5. SORTIRANJE POMOĆU BINARNOG STABLA

U ovu grupu spadaju Tree sort I Heap sort. Oba ova algoritma svoj rad zasnivaju naubacivanju elemenata u binarno stablo. Nakon tog postupka, se iz binarnog stable čitaju elementi u sortiranom redosljedu. Razlika između ovih algoritama je u vrsti binarnog stable kojeg koriste.Tree sort koristi binarno stablo traženja a Heap sort koristi hrpu kao binarno stablo.

5.1 Sortiranje obilaskom binarnog stabla traženja (Tree sort)

Tree sort algoritam se koristi pomoćnom funkcijom binarnog stabla inorder, koja posjećuje sve cvorove stabla u zavisnosti da li su veći ili manji od korijena. Prvo one što su manji od korijena a onda one što su veće od korijena.Upravo ova svojstva koristi Tree sort.Način rada tree sort-a je sljedeći.Algoritam prvo kreira binarno stablo pretrage.Podaci iz niza se ubacuju u binarno stablo pomocu inorder funkcije.Zatim se čitaju podaci iz binarnog stabla također funkcijom inorder.Algoritam se može implementirati koristeci još dodatne pomoćne funkcije insert koja dodaje novi cvor u binarno stablo. Algoritam Tree sort-a moze se prikazati kodom:

struct cvorliste{int x;cvorliste *korijen;cvorliste *ljevo;cvorliste *destno;cvorliste(){korijen=NULL;}};

void treeSort (int *a, int n) {int i, next;cvorliste *tree;tree = NULL;for (i=0; i<n; i++)tree = insert(a[i], tree);next=0;Inorder(tree, a, &next);}

cvorliste *insert (int x, cvorliste *korijen) {if (korijen == NULL) {node->element = x;korijen -> ljevo = korijen -> destno = NULL;}else if (x < korijen ->element)korijen -> ljevo = insert(x, korijen -> ljevo);elsekorijen -> destno = insert(x, korijen -> destno);return korijen;}

Autor: Adnan Goretić ([email protected])

Page 14: Algoritmi sortiranja u C++

void Inorder (cvorliste * korijen, int *a, int *np) {if (korijen!= NULL) {Inorder(korijen -> ljevo, a, np);a[*np] = korijen ->element;*np += 1;Inorder(korijen -> destno, a, np);}}

Analiza vremenske složenosti ovog algoritma pokazuje O(n log n) ocjenu. Međutim ova ocjena vrijedi samo u prosječnom slučaju. U najgorem slučaju ocjena vremenske složenosti će biti O(n2), tj. ako je početni niz podataka već sortiran.

5.2 Sortiranje pomocu hrpe (Heap sort)

Definicija hrpe: Hrpa predstavlja specijalnu formu binarnog stable koja mora zadovoljiti svojstvo hrpe. Svojstvo hrpe predstavlja uvijet: ako je B dijete od A, onda je A[indeks] < B[indeks]. Ovo svojstvo garantuje da je vrijednost korijena uvijek najmanja.

Algoritam radi na slijedeći način. Podaci iz niza kojeg zelimo sortirati prvo se redom ubacuju u hrpu. Onda se podaci skidaju sa hrpe I vracaju nazad u niz. Upravo zbog svojstva hrpe, podaci izlaze iz hrpe u sortiranom redosljedu.

Za ilustraciju Heap sort-a imamo niz brojeva 17, 31, 3, 43, 11, 24, 8, koji su prikazani na slijedećoj slici.

Autor: Adnan Goretić ([email protected])

Page 15: Algoritmi sortiranja u C++

Slika br.9 (prikaz rada Heap Sort algoritma) [1]

Autor: Adnan Goretić ([email protected])

Page 16: Algoritmi sortiranja u C++

Implementacija se vrši slijedećim kodom:

void heapSort (int *a, int n) {int i;buildHeap (a, n);for (i = n; i >= 2; i--) {swap(&a[0], &a[i-1]);adjust(a, 0, i-1);}}void buildHeap (int *a, int n) {int i;for (i = n/2-1; i >= 0; i--)adjust(a, i, n);}void adjust(int *a, int i, int n) {int j;int x;j = 2*i+1;x = a[i];while (j <= n-1 ) {if ((j < n-1) && (a[j] > a[j+1])) j++;if (x <= a[j]) break;a[(j-1)/2] = a[j];j = 2*j+1;}a[(j-1)/2] = x;}

Vremenska složenost Heap sort algoritma iznosi kao I kod Tree sort algoritma O (n log n). Eksperimenti pokazuju da Heap sort spade među najbrze poznate algoritme za sortiranje I da uspješno soritra velike količine podataka.

6.SORITANJE U LINEARNOM VREMENU

Prethodno su opisani algoritmi koji imaju vremensku složenost O(n2) i O(n log n). Ono što je zajedničko za te algoritme je da se sortirani poredak ulaznih elemenata temelji na poređenju. Postoje algoritmi sortiranja koji osim poređenja koriste i neke druge operacije. Ti algoritmi pod određenim uvjetima mogu postići linearnu vremensku složenost. U ovu grupu alogitama spadaju Counting sort i Radix sort.

6.1 Sortiranje brojanjem (Counting sort)

Counting sort (Algoritam sortiranja brojanjem) koristi pretpostavku da su elementi iz ulaznog niza cjelobrojne vrijednosti iz raspona 0 do k, 0 <= a[i] <= k.

K-a prestavlja parameter koji se može izraziti formulom k=max-min+1, gdje max i min predstavljaju maximalne i minimalne vrijednosti nekog niza.

Osnovni mehanizam rada algoritma se sastoji u tome da se za svaki ulazni element x odredi broj elemenata iz niza koji su manji od x. To se koristi da se element x direktno pozicionira na svoje odgovarajuće mjesto u sortiranom nizu.

Primjer Counting sort-a može se vidjeti na slijedećoj slici.

Autor: Adnan Goretić ([email protected])

Page 17: Algoritmi sortiranja u C++

Slika br.10( Rad counting sort algoritma) [2]

Kod Caunting sort algoritma se može zapisati:

void CountingSort(int *a, int *b, int k, int duzina){int *c=new int[k];for(int i=0; i<k; i++){

c[i]=0;}for(int j=0; j<duzina; j++){

c[a[j]]=c[a[j]]+1;}for(int i=1; i<k; i++){

c[i]=c[i]+c[i-1];}for(int j=duzina-1; j>=0; j--){

c[a[j]]=c[a[j]]-1;b[c[a[j]]]=a[j];}}

Autor: Adnan Goretić ([email protected])

Page 18: Algoritmi sortiranja u C++

Vremenska analiza složenosti se može predstaviti sa formulom O(k+n). Ako se u obzir ume da se counting sort koristi najčešće kada je k=O(n), onda je ukuona složenost O(n).

6.2 Radix sort

Ovaj algoritam svoj rad bazira na odvojenim analizama ulaznih elemenata I njihovih znakova na odgovarajućim pozicijama. Ako predpostavimo da su ključevi prikazani ako decimalni brojevi, gdje se za svaki prikaza koristi k decimala.Tako imamo dk-1, dk-2….d0.Algoritam rad započinje tako da ključeve ne sortiranog niza prvo sortira po cifri d0. Ovo se vrsi sve dok algoritam ne dodoje do vrijednosti dk-1. Sortiranje se obavlja tako da se uzimaju elementi iz niza I svrstavaju se u 10 nizova: Q[0], Q[1], … Q[9]. U niz Q[0] se stavljaju elementi cija je zadnja cifra 0, u Q[1] se stavljaju elementi čija je zadnja cifra 1 itd. Nakon ovakvog razmjestraja svi mali nizovi se spajaju u jedan niz i to uzlazno Q[0], Q[1], … Q[9].

Algoritam se može implementirati slijedećim kodom:

void RadixSort(int *a, int n){int *b=new int[n];int m=0,eks=1;for(int i=0; i<n; i++){if(a[i]>m){m=a[i];}while(m/eks>0){int korpa[10]={0};for(int i=0; i<n; i++){korpa[a[i]/eks%10]++;}for(int i=0; i<10; i++){korpa[i]+=korpa[i-1];}for(int i=n-1; i>=0; i--){b[korpa[a[i]/eks%10]--]=a[i];}for(int i=0; i<n; i++){a[i]=b[i];eks*=10;}}}}

Vremenska složenost ovog algoritma je O(nk), I to samo ako je cifra k kostantna. Ako k nije konstantan onda se vremenska složenost povećava u skladu sa k.

Autor: Adnan Goretić ([email protected])

Page 19: Algoritmi sortiranja u C++

Slika br.11( Proces rada Counting sort-a) [3]

Autor: Adnan Goretić ([email protected])

Page 20: Algoritmi sortiranja u C++

7. PITANJA I ZADACI

1. Usporedi način rada Selection sort-a i Bubble sort-a!

2. Šta predstavlja postupak Merge?

3. Objasni rad algoritama sa vremenskom složenosti O(n logn) ?

4. Koja je razlika između Insertion sort-a i Shell sort-a?

5. Koja to svojstva čine hrpu?

6. Objasni razliku izmedju Tree sort-a i Heap sort-a?

7. Na koji način radi Counting sort?

8. Na koji način radi Radix sort?

9. Ilustrovati rad algoritama sa O(n2)vremenskom složenosti!

10. Šta utječe na vremensku složenost Quick sort algoritma?

8. ZAKLJUČAK

Algoritmi za sortiranje prema definiciji predstavljaju svaki algoritam koji izvršava sortiranje elemenata nekog niza, liste ili drugog tipa podatka. Algoritme za sortiranje možemo razlikovati na osnovu svoje složenosti. Algoritmi su, u ovom radu, hronološki poredani prema svojoj složenosti. Ta hronologija mogla bi se predstaviti ujedno kao i historijat razvoja algoritama za sortiranje. Svaki od algoritama ima sličnosti sa drugim ali ipak i velikih razlika, koje se uglavnom projiciraju na vremensku zahtjevnost algoritma.

9. LITERATURA

[1] “Algoritmi i strukture podataka“, Robert Manger, Miljenko Marušić, Zagreb, 2007

[2] “Algorithms in C++”, Robert Sedgewick, Addison-Wesley, 2002

[3] “Algorithms, data structures, and problem solving with C++ “, Mark Allen Weiss, Addison-

Wesley Pub. Co., 1996

Autor: Adnan Goretić ([email protected])

Page 21: Algoritmi sortiranja u C++

POPIS SLIKA

1 Prikaz rada selection sort-a 4

2 Prikaz rada bubble sort algoritma 5

3 Prikaz rada Insertion sort-a 7

4 Prikaz rada Shell sort-a 8

5 Prikaz rada Merge sort algoritma 10

6 Podijela niza preko pivota 11

7 Zamjena elemenata preko pivota 11

8 Sortiranje niza pomoću Quick sort-a 12

9 Prikaz rada Heap Sort algoritma 15

10 Rad counting sort algoritma) 17

11 Proces rada Counting sort 19

Autor: Adnan Goretić ([email protected])