Cvičenie 5 – Triedenie
Ciele cvičení:
- rozumieť princípom algoritmov QuickSort, MergeSort a HeapSort a vedieť analyzovať ich časovú zložitosť
- spoznať údajovú štruktúru halda
QuickSort
Uvažujme triedu TriediaciAlgoritmus
(nižšie). Rozšírením tejto triedy vytvorte triedu QuickSort
, ktorá bude implementovať tento algoritmus prekrytím metódy utried
.
Ďalšie úlohy:
- S využitím knižnice CallTree preskúmajte štruktúru rekurzívnych volaní.
- Ako vyzerá strom volaní?
- Aký je obsah poľa pri jednotlivých volaniach?
- Koľko porovnaní sa vykoná?
- Nájdite taký vstup pre vašu implementáciu algoritmu
QuickSort
, aby časová zložitosťQuickSort
-u na tomto vstupe bola minimálna (O(n.log n)
– zdôvodnite). Viete podať algoritmus, ako takýto vstup vytvoriť pre zadanú množinu hodnôt? - Nájdite taký vstup pre vašu implementáciu algoritmu
QuickSort
, aby časová zložitosťQuickSort
-u na tomto vstupe bola maximálna (Ɵ(n2) – zdôvodnite). - Čo viete povedať o modifikovanom algoritme, v ktorom sa pivot nevolí ako prvý (či prostredný) prvok podpoľa, ale ako náhodný prvok v podpoli (implementujte). Tento algoritmus nazývame pravdepodobnostný (randomizovaný) QuickSort.
Vyzeráš ako strom v poli
- Nakreslite takmer úplný binárny strom pre 10 náhodne zvolených čísel (od 1 po 30).
- Zakreslite tento strom do poľa s 10 políčkami po úrovniach tak, ako to bolo ukázané na prednáške.
- Strom v poli dajte kolegovi a požiadajte ho, aby našiel hodnotu v strome, ktorá je pravým synom ľavého syna koreňa (cesta: LR). Ďalej ho požiadajte, aby našiel hodnotu na ceste od koreňa RL (najprv pravý syn, potom ľavý syn). Popíšte aj navigačnú cestu k nejakému ďalšiemu uzlu v strome a požiadajte ho, aby hodnotu v tomto uzle našiel.
- Predpokladajme, že pole
p
uchováva hodnoty takmer úplného binárneho stromu (spôsobom, ako bolo ukázané na prednáške). Reťazeccesta
nech obsahuje navigačnú cestu so začiatkom v koreni stromu, ktorá je tvorená písmenamiL
(presuň sa do ľavého syna) aR
(presuň sa do pravého syna). Implementujte metódu, ktorá vráti hodnotu v uzle stromu určenom zadanou navigačnou cestou.
1
- Je nakreslený strom haldou?
- Vytvorte metódu, ktorá overí, či takmer úplný binárny strom uložený v poli je haldou.
1 public static boolean jeHaldou(int[] pole)
Zlučovanie usporiadaných postupností a MergeSort
- Vytvorte 2 usporiadané (napr. 5-prvkové) postupnosti čísel. Aplikovaním algoritmu z prednášky zlúčte tieto 2 postupnosti do 10-prvkového usporiadaného poľa.
- Naprogramujte metódu, ktorá zrealizuje spojenie dvoch utriedených polí
p1
ap2
do utriedeného výsledného poľa. Aká je jej časová zložitosť?
1 public static int[] zluc(int[] p1, int[] p2);
- Vyskúšajte odsimulovať algoritmus MergeSort z prednášky na utriedenie poľa 10 hodnôt. Rozdiskutujte aj časovú zložitosť algoritmu.
HeapSort
Poznámka: pri jednotlivých operáciach si všímajte ako sa mení obsah poľa uchovávajúceho takmer úplný binárny strom v závislosti od zmeny obsahu v strome.
- Nakreslite takmer úplný binárny strom pre 10 náhodne vybraných čísel (od 1 po 30).
- Je zakreslený strom haldou?
- Nakreslite hodnoty do stromu tak, aby bola splnená vlastnosť haldy.
- Zmeňte hodnotu v koreni na náhodné menšie číslo. Aplikujte algoritmus z prednášky, aby sa strom opäť stal haldou. Aká je časová zložitosť tohto algoritmu?
- Vyskúšajte si odsimulovať algoritmus na vybudovanie haldy z prednášky aplikovaním na 10 náhodných hodnôt. Zdôvodnite celkovú časovú zložitosť vybudovania haldy.
- Po vybudovaní haldy aplikujte algoritmus z prednášky na utriedenie poľa. Zdôvodnite celkovú časovú zložitosť utriedenia hodnôt pomocou haldy.
- Pre pokročilých: Dokážte, že časová zložitosť vytvorenia
n
-prvkovej haldy jeO(n)
Prioritný rad
- Implementujte prioritný rad čísel (priorita čísla je jeho hodnota) s obmedzenou maximálnou kapacitou:
1
2
3
4
5
6 public class PrioritnyRad {
public PrioritnyRad(int kapacita);
public boolean offer(int cislo);
public int poll();
public boolean isEmpty();
}
Prioritný rad je rad, v ktorom metóda poll
vráti najväčšie číslo v rade. Ak je takých čísel viac, vráti ľubovoľné z nich. Triedu implementujte tak, aby žiadna z operácií nemala trvanie dlhšie ako O(log(n))
, kde n
je počet hodnôt uložených v rade. Obmedzenie na kapacitu určuje, že v rade nemôže čakať viac hodnôt, než je zadaná kapacita radu.
Pozri, čo som našiel na webe
Pozrite si niektorú z týchto implementácií QuickSortu:
- http://www.vogella.de/articles/JavaAlgorithmsQuicksort/article.html
- http://algs4.cs.princeton.edu/23quicksort/Quick.java.html
V čom je táto implementácia iná? V čom sa líši operácia rozdeľovania podľa pivota? Aká je časová zložitosť rozdeľovania podľa pivota v týchto implementáciách?
Dolné ohraničenie pre triedenie (pre fajnšmekrov)
- Označme si
M(h)
maximálny počet listov v binárnom strome s výškouh
. Určte hodnotuM(h)
s využitím vzťahuM(h) = 2*M(h-1)
, ktorý vyplýva z toho, že strom s maximálnym počtom listov a výškouh
vieme vybudovať z 2 stromov s maximálnym počtom listov a výškouh-1
. Presnú hodnotuM(h)
dokážte matematickou indukciou vzhľadom nah
. - Analyzujte rozhodovacie stromy pre triediaci algoritmus založený na porovnávaní, ktorý usporadúva postupnosti dĺžky
n
. Aký je minimálny počet listov v tomto strome? - S využitím Stirlingovej formuly ukážte, že výška v rozhodovacom strome (a tým aj minimálny počet porovnaní) musí byť aspoň
Omega(n.log(n))
.
Medián v čase O(n)
(pre fajnšmekrov)
Rozdiskutujte algoritmus na nájdenie mediána postupnosti v lineárnom čase.
Trieda TriediaciAlgoritmus
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 import java.util.Arrays;
public abstract class TriediaciAlgoritmus {
/**
* Aktualne usporiadavane pole
*/
private int[] p;
/**
* Porovna prvky v poli na zadanych indexoch
*
* @return vrati zaporne cislo, ak p[idx1] < p[idx2], * 0, ak p[idx1]==p[idx2], a * kladne cislo, ak p[idx1] > p[idx2]
*/
protected int porovnaj(int idx1, int idx2) {
return Integer.compare(p[idx1], p[idx2]);
}
/**
* Vymeni prvky v usporiadavanom poli na zadanych indexoch
*/
protected void vymen(int idx1, int idx2) {
int pom = p[idx1];
p[idx1] = p[idx2];
p[idx2] = pom;
}
/**
* Vypise pole
*/
protected void vypisPole() {
System.out.println(Arrays.toString(p));
}
/**
* Usporiada prvky v poli od najmensieho po najvacsie
*
* @param p
* pole, ktoreho prvky maju byt usporiadane
*/
public void utried(int[] p) {
this.p = p;
utried(p.length);
this.p = null;
}
/**
* Metoda, ktora implementuje triedenie podla urciteho algoritmu
* @param dlzkaPola dlzka (pocet prvkov) usporiadavaneho pola
*/
protected abstract void utried(int dlzkaPola);
}