1. Následující dvojici řazení je možno implementovat tak, aby byla

Transkript

1. Následující dvojici řazení je možno implementovat tak, aby byla
1.
Následující dvojici azení je možno implementovat tak, aby byla stabilní
a)
b)
c)
d)
e)
Heap sort a Insert sort
Selection sort a Quick sort
Selection sort a Merge sort
Heap sort a Merge sort
Radix sort a Quick sort
ešení
K nestabilním azením náleží z vyjmenovaných jen Quick a Heap Sort. Ty se objevují ve
všech variantách krom c).
2.
Poskytneme-li Quick Sort-u a Merge sort-u stejná data k se azení, platí
a)
b)
c)
d)
e)
Quick sort bude vždy asymptoticky rychlejší než Merge Sort
Merge sort bude vždy asymptoticky rychlejší než Quick Sort
n kdy m že být Quick sort asymptoticky rychlejší než Merge Sort
n kdy m že být Merge sort asymptoticky rychlejší než Quick Sort
oba algoritmy budou vždy asymptoticky stejn rychlé
ešení
Asymptotická složitost Quick sortu je O(n2) a ono „n2“ n kdy m že nastat. U Merge sortu
je to Θ(n·log2(n)), takže první a poslední možnost odpadá. Quick Sort pracuje v tšinou
v ase úm rném n·log2(n), tedy podobn rychle jako Merge sort, takže druhá možnost
odpadá. Také je ale je asymptotická rychlost Quick sortu rovna Ω(n·log2(n)), tj Quick Sort
nebude nikdy asymptoticky rychlejší než „n·log2(n)“, tedy nebude asymptoticky rychlejší
než Merge sort, sbohem t etí možnosti.
V p ípad , že Quick Sort opravdu selže a pob ží v ase úm rném n2, Merge sort
neselže a bude tedy asymptoticky rychlejší.
Poznámka:
N kte í z dálka si myslí, že Θ(n·log2(n)) popisuje n co jako pr m rnou složitost, tj. takovou, se kterou se
v tšinou v praxi setkáme. Není to tak, Θ(n·log2(n)) indikuje víc, indikuje, že pokaždé bude b h algoritmu
úm rný n·log2(n), i kdyby traka e padaly.
Kdosi p i zkoušce vážn tvrdil, že dokáže vhodnou metodou výb ru pivota stla it složitost Quick sortu na
Θ(n). No nazdar!
3.
P edložíme-li Heap sort-u vzestupn se azenou posloupnost délky n, bude složitost
celého azení
a)
b)
c)
d)
e)
Θ(n), protože Heap sort vytvo í haldu v ase Θ(n)
Θ(n2), protože vytvo ení haldy bude mít práv tuto složitost
Θ(n·log2(n)), protože vytvo ení haldy bude mít práv tuto složitost
Θ(n·log2(n)), protože zpracování haldy bude mít práv tuto složitost
Θ(n), protože vytvo ení i zpracování haldy bude mít práv tuto složitost
ešení
Tvorba haldy v Heap sortu odpovídá zhruba n/2 násobnému volání funkce (procedury,
algoritmu...), která opraví „haldu“, jejímž jediným nedostatkem je velikost prvku v ko eni
(= jediný nemá vlastnost haldy). Máme-li ovšem dánu vzestupn se azenou
posloupnost, není co kam za azovat, neb celá posloupnost i každá její souvislá ást
p edstavuje haldu, takže volání doty né funkce nezabere více než konstantní dobu. Na
„vytvo ení“ haldy v tomto p ípad padne as úm rný konst·n/2. Celková doba doty ného
azení je ale Θ(n·log2(n)), což je (asymptoticky!) více než Θ(n) strávených v první fázi,
takže delší as úm rný n·log2(n), musí být stráven ve fázi zpracování haldy. Platí
varianta d).
4.
Heap sort
a)
b)
c)
d)
e)
není stabilní, protože halda (=heap) není stabilní datová struktura
není stabilní, protože žádný rychlý algoritmus ( Θ(n*log(n))) nem že být stabilní
je stabilní, protože halda (=heap) je vyvážený strom
je stabilní, protože to zaru uje pravidlo haldy
neplatí o n m ani jedno p edchozí tvrzení
ešení
Heap sort není stabilní, c) i d) odpadají. b) není pravda, nebo Merge sort má doty nou
rychlost a stabilní být m že (v tšinou je). D lení na stabilní a nestabilní a u datových
struktur neexistuje (co by to v bec bylo?), takže ani a) neplatí a zbývá jen e).
Poznámka: Zde bychom ocenili zp tnou vazbu, pro se množství respondent (35 !!) zdála
„nestabilní datová struktura“ tak p ítažlivá.
5.
Merge sort
a)
b)
c)
d)
e)
lze napsat tak, aby nebyl stabilní
je stabilní, protože jeho složitost je Θ (n*log(n))
je nestabilní, protože jeho složitost je Θ(n*log(n))
je rychlý ( Θ(n*log(n))) práv proto, že je stabilní
neplatí o n m ani jedno p edchozí tvrzení
ešení
Rychlost a stabilita azení nejsou v žádné p í inné souvislosti, odpov di b), c), d) jsou
pouhá „mlha“. P i slévání (vemte si k ruce kód!) m žeme jednoduchou zám nou ostré a
neostré nerovnosti v porovnávání prvk zp sobit, že p i stejných porovnávaných prvcích
dostane p ednost bu prvek z levé nebo z pravé ásti azeného úseku. Když dostanou
p ednost prvky z pravé ásti, octnou se nakonec v se azeném poli p ed t mi prvky, které
byly v levé ásti a m ly stejnou hodnotu. To je ovšem projev nestability. Platí možnost
a).
6.
Následující posloupnost p edstavuje haldu o ty ech prvcích uloženou v poli.
a)
b)
c)
d)
1
1
1
2
3
4
2
3
4
2
4
4
2
3
3
1
ešení
Prvek na 1. pozici musí být menší než prvek na 2. a 3. pozici, možnost d) odpadá. Prvek
na 2. pozici musí být menší než prvek na 4. pozici, možnosti a) a b) odpadají. Více
podmínek haldy pro 4 prvky nelze formulovat, varianta c) je halda.
7.
Následující posloupnost p edstavuje haldu o ty ech prvcích uloženou v poli
a)
b)
c)
d)
1
1
1
2
3
3
4
3
4
2
2
1
2
4
3
4
ešení
Prvek na 1. pozici musí být menší než prvek na 2. a 3. pozici, možnost d) odpadá. Prvek
na 2. pozici musí být menší než prvek na 4. pozici, možnosti a) a c) odpadají. Více
podmínek haldy pro 4 prvky nelze formulovat, varianta b) je halda.
8.
Následující posloupnost ísel p edstavuje haldu uloženou v poli. Prove te první krok
fáze azení v Heap Sortu (t íd ní haldou), tj. a) za a te nejmenší prvek haldy na jeho
definitivní místo v se azené posloupnosti a b) opravte zbytek tak, aby op t tvo il haldu.
1 5 2 17 13 24 9 19 23 22
ešení
a) prohodíme první prvek s posledním:
22 5 2 17 13 24 9 19 23 1
b) Prvek 22 necháme „propadnout“ („probublat“) haldou na odpovídající mu místo:
2 5 9 17 13 24 22 19 23 1
9.
Následující posloupnost ísel p edstavuje haldu uloženou v poli. Prove te první krok
fáze azení v Heap Sortu (t íd ní haldou), tj. a) za a te nejmenší prvek haldy na jeho
definitivní místo v se azené posloupnosti a b) opravte zbytek tak, aby op t tvo il haldu.
4 8 11 12 9 18 13 17 21 25
ešení
a) prohodíme první prvek s posledním:
25 8 11 12 9 18 13 17 21 4
b) Prvek 25 necháme „propadnout“ („probublat“) haldou na odpovídající mu místo:
8 9 11 12 25 18 13 17 21 4
10.
V ur itém problému je velikost zpracovávaného pole s daty rovna rovna 2n−2, kde n
charakterizuje velikost problému. Pole se adí pomocí Merge Sort-u ( azení sléváním).
S použitím Θ/Ο/Ω symboliky vyjád ete asymptotickou složitost tohoto algoritmu nad
uvedeným polem v závislosti na hodnot n. Výsledný vztah p edložte v co nejjednodušší
podob .
ešení
Pole velikosti m adí Merge Sort pokaždé v ase Θ(m·log(m)).V našem p ípad je
m = 2n−2. I kdyby pole m lo velikost práv 2n (tedy ješt v tší), byla by asymptotická
složitost rovna
Θ(2n·log(2n)) = Θ(n·log(2n)) = Θ(n· (log(2) + log(n))) = Θ(n· log(2) + n · log(n)).
Protože výraz n· log(2) roste asymptoticky pomaleji než n · log(n) m žeme dále psát:
Θ(n· log(2) + n · log(n)) = Θ(n · log(n)).
Záv r tedy je: Protože jak pole velikosti n, tak i pole velikosti 2n se adí Merge Sort v
asymptoticky stejném ase Θ(n · log(n)), se adí i pole velikosti 2n−2 v témž ase
Θ(n · log(n)).
11.
Následující posloupnost et zc je nutno se adit pomocí Radix Sortu (p ihrádkového
azení). Prove te první pr chod (z celkových ty pr chod ) algoritmu danými daty a
napište, jak budou po tomto prvním pr chodu se azena.
IIYI PIYY YIII YPPP YYYI PYPP PIPI PPYI
ešení
K dispozici budou t i „p ihrádky“ ozna ené symboly I, P a Y. První pr chod pracuje
s posledním symbolem v každém et zci, takže obsah „p ihrádek“ bude:
I : IIYI YIII YYYI PIPI PPYI
P: YPPP PYPP
Y: PIYY
12.
Quick sort adí následující ty prvkové pole ísel.
2
4
1
3
Jako pivotní hodnotu volí první v po adí tj. nejlev jší. Jak bude pole rozd leno na „malé“
a „velké“ hodnoty po jednom pr chodu polem? (Svislá ára nazna uje d lení.)
a)
b)
c)
d)
1 | 4
2
1
2 | 3
1
2 | 4
2
1 | 3
3
4
3
4
ešení
V jednom okn editoru si otev te kód algoritmu uvedený níže (i na p ednášce!) a ve
druhém sledujte tento text (máte-li toho zapot ebí :-)).
Nejprve se ur í pivot (=2) a nastaví se indexy L = 1, P = 4.
L
P
2
4
1
3
I. Dokud L vidí hodnotu ost e menší než pivot, pohybuje se doprava, takže v tomto
p íipad jenom z stane stát ( ádek 6 v kódu).
II. Dokud P vidí hodnotu ost e v tší než pivot, pohybuje se doleva, takže 3 nechá být a
zastaví se u hodnoty 1 ( ádek 7 v kódu).
L
P
2
4
1
3
III. Pokud je L < P (a to te je), prohodí se prvky pod L a P
L
P
1
4
2
3
a vzap tíi se povinn L posune o krok doprava a R o krok doleva ( ádky 8-10 v kódu).
LP
1
4
2
3
IV. Te dochází algoritmus k podmínce vn jšího cyklu while(iL <= iR) (na ádku 15) což
znamená, že vn jší cyklus ješt nekon í a bude se opakovat po ínaje ádkem 6:
V. Dokud L vidí hodnotu ost e menší než pivot, pohybuje se doprava, takže v tomto
p íipad jenom z stane stát ( ádek 6 v kódu).
VI. . Dokud P vidí hodnotu ost e v tší než pivot, pohybuje se doleva, takže 4 nechá být a
zastaví se u hodnoty 2 ( ádek 7 v kódu).
P
L
1
4
2
3
VII. Nyní neplatí podmínka na ádku 8 (iL < iR) a neplatí ani podmínka v ásti else na
ádku 12 (iL == iR), takže se neprovede v bec nic.
VIII. Podminka opakovani vnejsiho cyklu na radku 15 (iL <= iR) již neplatí, cyklus kon í
a p echazí se k rekurzivnímu voláni. "Malé" prvky se nalezají na za átku pole až do
indexu P v etn , takže obsahují hodnotu: 1. "Velké“ prvky se nalezají na konci pole
po ínaje indexem L, takže obsahují hodnoty 4, 2, 3 (v tomto po adí).
Pole je tedy rozd leno mezi prvním a druhým prvkem stejn jako ve variant a).
Poznámka.
N kte í respondenti byli p esv d eni o nejednozna nosti nabízených variant odpov dí. Prosíme
proto každého, kdo postupoval podle odlišné varianty Quick sortu, než která byla uvedena
v p ednášce, a vyšla mu jiná varianta, aby se p ihlásil, rádi mu jeho dosud nep íznivou klasifikaci
zlepšíme.
Kód QuickSortu
1 void qSort(Item a[], int low, int high) {
2
int iL = low, iR = high;
3
Item pivot = a[low];
4
5
do {
6
while (a[iL] < pivot) iL++;
7
while (a[iR] > pivot) iR--;
8
if (iL < iR) {
9
swap(a,iL, iR);
10
iL++; iR--;
11
}
12
else {
13
if (iL == iR) { iL++; iR--;}
14
}
15
while( iL <= iR);
16
17
if (low < iR) qSort(a, low, iR);
18
if (iL < high) qSort(a, iL, high);
19 }
13.
Quick sort adí následující ty prvkové pole ísel.
3
2
1
4
Jako pivotní hodnotu volí první v po adí tj. nejlev jší. Jak bude pole rozd leno na „malé“
a „velké“ hodnoty po jednom pr chodu polem? (Svislá ára nazna uje d lení.)
a)
b)
c)
d)
e)
1 | 2
4
1
2 | 3
1
2 | 4
2
3 | 1
1
2
3 |
3
4
3
4
4
ešení
Nejprve se ur í pivot (=3) a nastaví se indexy L = 1, P = 4.
L
P
3
2
1
4
I. Dokud L vidí hodnotu ost e menší než pivot, pohybuje se doprava, takže v tomto
p íipad jenom z stane stát ( ádek 6 v kódu).
II. Dokud P vidí hodnotu ost e v tší než pivot, pohybuje se doleva, takže 3 nechá být a
zastaví se u hodnoty 1 ( ádek 7 v kódu).
L
P
3
2
1
4
III. Pokud je L < P (a to te je), prohodí se prvky pod L a P
L
P
1
2
3
4
a vzap tíi se povinn L posune o krok doprava a R o krok doleva ( ádky 8-10 v kódu).
LP
1
2
3
4
IV. Te dochází algoritmus k podmínce vn jšího cyklu while(iL <= iR) (na ádku 15) což
znamená, že vn jší cyklus ješt nekon í a bude se opakovat po ínaje ádkem 6:
V. Dokud L vidí hodnotu ost e menší než pivot, pohybuje se doprava, takže v tomto
p ípad se posune o krok dále nad hodnotu 3 (rovnou pivotu) ( ádek 6 v kódu).
P
L
1
2
3
4
VI. . Dokud P vidí hodnotu ost e v tší než pivot, pohybuje se doleva, takže v tomto
p ípad neud lá nic, protože vidí hodnotu (2) menší než pivot (3) ( ádek 7 v kódu).
P
L
1
2
3
4
VII. Nyní neplatí podmínka na ádku 8 (iL < iR) a neplatí ani podmínka v ásti else na
ádku 12 (iL == iR), takže se neprovede v bec nic.
VIII. Podmínka opakováaní vn jšího cyklu na ádku 15 (iL <= iR) již neplatí, cyklus kon í
a p echazí se k rekurzivnímu voláni. "Malé" prvky se nalezají na za átku pole až do
indexu P v etn , takže obsahují j hodnoty: 1 a 2. "Velké“ prvky se nalezají na konci pole
po ínaje indexem L, takže obsahují hodnoty 3, 4 (v tomto po adí).
Pole je tedy rozd leno mezi druhým a t etím prvkem stejn jako ve variant b).
14.
Napište nerekurzivní verzi algoritmu Quick-sort. P i návrhu algoritmu postupujte
metodou shora dol .
ešení
Myšlenka:¨D lení na „malé“ a „velké“ hodnoty v aktuáln azeném úseku bude probíhat
stejn jako v rekurzivní verzi. Jedin se zm ní postup, jakým budeme jednotlivé úseky
registrovat. (Mimochodem, po adí zpracování úsek se také nezm ní.)
Použijeme vlastní zásobník, do n hož budeme ukládat meze (horní a dolní  jako
dvojici celých ísel) úsek , které mají být zpracovány.
(Objevilo se i korektní ešení, kde autor ukládal na zásobník i meze práv zpracovaného
úseku, ale musel si pamatovat více informací a algoritmus byl složit jší)
P edpokládejme, že p vodní pole má meze 1..n. Celý algoritmus pak vypadá takto:
Vytvo prázdný zásobník
Ulož dvojici (1,n) na zásobník
While (zásobník je neprázdný) {
vyjmi z vrcholu zásobníku (pop) meze úseku (Low, High), který budeš te zpracovávat
rozd l aktuální úsek (od Low do High) stejn jako v rekurzivní verzi na dva úseky
s mezemi
(Low, iR) a (iL, High)
if (Low < iR) vlož (push) dvojici (Low, iR) na zásobník
if (iL < High) vlož (push) dvojici (iL, High) na zásobník
}
Pro úpný algoritmus ješt popište, jak se d lí úsek na „malé“ a „velké“.
15.
Implementujte nerekurzivní variantu Merge sortu s využitím vlastního zásobníku. Vaše
implementace nemusí usilovat o maximální rychlost, sta í, když dodrží asymptotickou
složitost Merge sortu. Jednotlivé operace zásobníku neimplementujte, p edpokládejte,
že jsou hotovy, a pouze je vhodn volejte.
ešení
Merge sort eší svou úlohu pomocí procházení stromu rekurze v po adí postorder. Je to
z ejmé, protože k tomu, aby mohl být zpracován (=se azen) n který úsek pole, musí být
jïž se azeny ob jeho poloviny, tj azení muselo prob hnout v potomcích uzlu, který
reprezentuje aktuální úsek.
Musíme tedy um t zpracovat uzly stromu v po adí postorder bez rekurze.
Na zásobník si budeme ukládat jednotlivé uzly spolu s informací koikrát byly již
navštíveny. Do zpracování uzlu se dáme až tehdy, když ze zásobníku vyzvedneme uzel
s informací že do n ho již vstupujeme pot etí (tj p icházíme z jeho již zpracovaného
pravého podstromu).
Pr chod stromem m že proto vypadat nap . takto:
if (ourTree.root == null) return;
stack.init();
stack.push(ourTree.root, 1)
while (stack.empty() == false) {
(nodeX, time) = stack.pop();
if (isLeaf(nodeX) == true) continue; // Listy ve stromu merge sortu
// budou úseky o délce jedna,
// ty nijak zpracovávat (= adit) nebudeme
if( time == 1) {
stack.push(nodeX, time+1);
// po et návšt v akt. uzlu vzrostl
stack.push(nodeX.left, 1)
// a doleva jdeme poprvé
}
if( time == 2) {
stack.push(nodeX, time+1);
stack.push(nodeX.right, 1)
}
if( time == 3) {
zpracuj(nodeX);
}
} // end of while
// po et návšt v akt. uzlu vzrostl
// a doprava jdeme poprvé
// zpracování = slou ení úseku pole
//odpovídajícího uzlu nodeX.
Uvedenou kostru algoritmu je jen nutno doplnit:
-- uzel stromu odpovídá azenému úseku pole, nodeX tedy bude charakterizován a
nahrazen dvojicí (horní mez, dolní mez) ur ující úsek pole.
-- test isLeaf(nodeX) nahradíme testem, zda horní a dolní mez jsou si rovny
-- vÿpo teme st ed azeného úseku. Levý následník pak bude charakterizován dvojicí
ísel (dolní mez, st ed), pravý následník dvojicí ísel (st ed+1, horní mez)
-- zpracuj (nodeX) nebude nic jiného, než oby ejné slévání obou polovi ních úsek
(dolní mez, st ed) a (st ed+1, horní mez) do úseku (dolní mez, horní mez) stejn jako je
tomu v rekurzivním Merge sort-u.
Budeme jen pot ebovat pomocné pole pro slévání (to v rekurzivní variant také
pot ebujeme). Slévání v nejjednodušší variant sleje oba úseky do pomocného pole a
odtud je zkopíruje zp t do azeného pole.
16.
Merge Sort lze naprogramovat bez rekurze („zdola nahoru“) také tak, že nejprve
slou íme jednotlivé nejkratší možné úseky, pak slou íme delší úseky atd. Prove te to.
ešení
Kdyby pole m lo velikost rovnou mocnin dvou, sta ily by nám jen dva jednoduché
vno ené cykly, jak to ukazuje následující kód, ve kterém funkce
merge(levyKraj, stred, pravyKraj) slévá standardním zp sobem levou a pravou polovinu
azeného úseku [levyKraj, stred] a [stred+1, pravyKraj]:
delkaUseku = 2;
while (delkaUseku <= array.length) {
levyKraj = 0;
while(levyKraj < array.length-1) {
pravyKraj = levyKraj+delkaUseku-1;
stred = PravyKraj-delkaUseku/2;
merge(levyKraj, stred, pravyKraj);
}
levyKraj += delkauseku;
}
delkaUseku *= 2;
V obecném p ípad ovšem délka pole mocninou nebude a proto poslední aktuáln
slévaný úsek v poli m že mít kratší délku než všechny ostatní, jak to ukazují následující
obrázky.
V p ípad , že je zbývající úsek (v šedé barv ) delší než polovina délky aktuáln
slévaných úsek (levý obrázek), se adíme jej pomocí jednoho slévání.
V p ípad , že je zbývající úsek kratší nebo stejn dlouhý jako polovina délky aktuáln
slévaných úsek (pravý obrázek), není co slévat, protože úsek již musí být se azený 
po n kterém z p edchozích pr chod s kratší délkou azených úsek .
Oba uvedené p ípady jsou zachyceny v následující modifikaci (pseudo)kódu:
delkaUseku = 2;
while (delkaUseku <= array.length) {
levyKraj = 0;
while(levyKraj < array.length-1) {
pravyKraj = levyKraj+delkaUseku-1;
stred = PravyKraj-delkaUseku/2;
if (PravyKraj <= array.length-1)
merge(levyKraj, stred, pravyKraj);
else
if (stred < array.length-1) {
pravyKraj = array.length-1;
merge(levyKraj, stred, pravyKraj);
}
else { } // nedelej nic
}
levyKraj += delkauseku;
}
delkaUseku *= 2;