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;