Jméno:... St. Sk.:. Cvičící:.. Bodů ze cv.: (Tučná čísla indikují počet neuspěvších (z 50) v jednotlivých otázkách) 1. 36 Heap sort a) není stabilní, protože halda (=heap) není stabilní datová struktura b) není stabilní, protože žádný rychlý algoritmus ( Θ(n*log(n))) nemůže být stabilní c) je stabilní, protože halda (=heap) je vyvážený strom d) je stabilní, protože to zaručuje pravidlo haldy e) neplatí o něm ani jedno předchozí tvrzení 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á. 2. 9 Funkce int ff(int x, int y) { if (x < y) return ff(x+1,y); return x; a) buď hned vrátí první parametr nebo jen do nekonečna volá sama sebe b) vrátí maximální hodnotu z obou parametrů c) vrátí součet svých parametrů d) vrátí x+1 e) neprovede ani jednu z předchozích možností Když je x větším (nebo stejným) z obou parametrů, jeho hodnota je vrácena ihned. V opačném případě se v rekurzivním volání jeho hodnota zvětšuje tak dlouho, dokud nedosáhne hodnoty y tedy původně většího z obou parametrů. Při návratu z rekurze již k žádným změnám nedochází, opět je tedy vrácena hodnota většího z obou parametrů. Platí možnost b). 3. 0 Obsah uzlů daného stromu vypíšeme v pořadí postorder. Vznikne posloupnost a) G F E D C B A b) F C G D B E A c) G C F A E B D d) D E B F G C A Přímo z definice pořadí postorder plyne, že je nutno volit odpověď d). 4. 9 Metoda hashování s vnějším zřetězením a) nemá problém s kolizemi, protože při ní nevznikají
b) dokáže uložit pouze předem známý počet klíčů c) ukládá synonyma do samostatných seznamů v dynamické paměti d) ukládá synonyma spolu s ostatními klíči v poli Každý alespoň elementární popis zřetězeného rozptylování vede na odpověď b). Kolize vznikají vždy, pole se tu nepoužívá a počet klíčů není teoreticky omezen. 5. 20 V určitém problému je velikost zpracovávaného pole s daty rovna rovna 3n 2 log(n 2 ) kde n charakterizuje velikost problému. Pole se řadí pomocí Insert sort-u. Asymptotická složitost tohoto algoritmu nad uvedeným polem je tedy a) Ο(n 2 log(n)) b) Ο(n 2 log(n 2 )) c) Ο(n 4 log 2 (n)) d) Ο(n 2 log(n) + n 2 ) e) Ο(n 2 ) Máme-li velikost pole rovnu k, Insert sort jej zpracuje v čase Ο(k 2 ). Velikost našeho pole je k = 3n 2 log(n 2 ), takže bude zpracováno v čase Ο(k 2 ) = Ο((3n 2 log(n 2 )) 2 ) = = O(9n 4 log 2 (n 2 )) = O(9n 4 4 log 2 (n)) = O(n 4 log 2 (n)). Platí tedy možnost c). 6. 4 Uvedený konečný automat se startovním stavem S přijme slovo a) aaaa b) ba c) bab d) bbb e) žádné z předchozích Slovo bab je to pravé. Posloupnost přechodů je tato: (S), b --> (B), b --> (A), b --> (B). (B) je koncový stav, takže slovo bab je přijato. Žádné jiné slovo z nabízených přijato nebude, takže platí možnost c). Poznámka: Redakčním nedopatřením vznikly dva přechody ze stavu C označené písmenem b. Přechod ze stavu C po přečtení symbolu b je tedy nejednoznačný, naštěstí to však nemá na řešení celé otázky 6 žádný vliv. Objekt na obrázku kromě toho zůstává i nadále řádným konečným automatem, jen se stal nedeterministickým, ale o tom až v dalších semestrech... 7. 10 Datový typ a) je plně popsán soustavou axiomů b) je určen druhy dat a typickými operacemi c) je určen typickými operacemi d) je určen druhy dat
To je otázka pamatování si definice. Pro datový typ musí být známo, s jakými vůbec hodnotami může pracovat (= druhy dat) a také musí být známo, jaké operace nad těmito hodnotami lze v rámci datového typu provádět (= typické operace). Platí b). 8. 7 Levá rotace v a) v podstromu s kořenem u přemístí pravého syna u p do kořene. Přitom se uzel u stane levým synem p a levý podstrom p se stane pravým podstromem b) v podstromu s kořenem u přemístí levého syna u l do kořene. Přitom se uzel u stane pravým synem l a levý podstrom l se stane pravým podstromem c) v podstromu s kořenem u přemístí pravého syna u p do kořene. Přitom se uzel u stane levým synem p a pravý podstrom p se stane levým podstromem d) v podstromu s kořenem u přemístí levého syna u l do kořene. Přitom se uzel u stane pravým synem l a pravý podstrom l se stane levým podstromem Zde můžeme jen doporučit nakreslení obrázku, případně nahlédnutí do vhodné publikace, možnost a) se ukáže být jedinou správnou. 9. 33 Vyvážení BVS některou z operací rotace a) je nutno provést po každé operaci Insert b) je nutno provést po každé operaci Delete c) je nutno provést po každé operaci Insert i Delete d) snižuje hloubku stromu e) není pro udržování BVS nezbytné Binární vyhledávací stromy se vyvažovat mohou a také nemusí. Možnosti a) b) c) tedy opadají a zřejmě platí možnost e). Možnost d) neplatí, protože rotace nemusí snížit hloubku stromu, je-li provedena dostatečně vysoko ve stromu v některé z kratších větví (jednoduché cvičení najděte na to příklad, budete potřebovat pět pater stromu). 10. 20 V následující datové struktuře lze maximálně třemi operacemi přečíst poslední prvek a) fronta b) zásobník c) tabulka d) strom e) žádná z uvedených V tabulce ani ve stromu žádný poslední prvek definován není, možnosti c) a d) odpadají. Poslední prvek ve frontě obsahující alespoň pět prvky se na začátek dostane po nejméně čtyřech operacích Delete a žádný jiný přístup k němu není, takže možnost a) odpadá také. V zásobníku je naopak poslední prvek na vrcholu, takže je pomocí operací Pop či Top snadno přístupný i ke čtení. Platí možnost b)
Jméno:... St. Sk.:. Cvičící:.. Bodů ze cv.: (Tučná čísla indikují počet neuspěvších (z 45) v jednotlivých otázkách) 1. 13 Merge sort a) lze napsat tak, aby nebyl stabilní b) je stabilní, protože jeho složitost je Θ (n*log(n)) c) je nestabilní, protože jeho složitost je Θ(n*log(n)) d) je rychlý ( Θ(n*log(n))) právě proto, že je stabilní e) neplatí o něm ani jedno předchozí tvrzení 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). 2. 5 Funkce int ff(int x, int y) { if (y>0) return ff(x, y-1)+1; return x; a) sečte x a y, je-li y nezáporné b) pro kladná y vrátí y, jinak vrátí x c) spočte rozdíl x y, je-li y nezáporné d) spočte rozdíl y x, je-li y nezáporné e) vrátí hodnotu svého většího parametru Pro y záporné nebo nulové vrátí fukce hodnotu x, Pro kladné y volá sama sebe. Počet volání je roven hodnotě y (neboť tento parametr se při každém volání o jedničku zmenší) a při návratu z rekurze se návratová hodnota z většuje pkaždé o 1. Počet volání je y, nejvnitřnější volání vrátí hodnotu x, takže návrat z rekurze přičte k x ještě hodnotu y. Správná odpověď je a). 3. 2 Obsah uzlů daného stromu vypíšeme v pořadí preorder. Vznikne posloupnost a) A B C D E F G H b) D B E A F C G c) A B D E C F G d) D E F G B C A Přímo z definice pořadí postorder plyne, že je nutno volit odpověď c). 4. 17 Metoda otevřeného rozptylování
a) generuje vzájemně disjunktní řetězce synonym b) dokáže uložit pouze předem známý počet klíčů c) zamezuje vytváření dlouhých clusterů ukládáním synonym do samostatných seznamů v dynamické paměti d) dokáže uložit libovolný předem neznámý počet klíčů Varianty a), c), d) platí zřejmě pro zřetězené rozptylování, což vyplývá bezprostředně již z jakéhokoli jednoduchého popisu zřetězeného rozptylování. Zbývá jen správná možnost b). 5. 16 V určitém problému je velikost zpracovávaného pole s daty rovna rovna 2n 3 log(n) kde n charakterizuje velikost problému. Pole se řadí pomocí Select sort-u. Asymptotická složitost tohoto algoritmu nad uvedeným polem je tedy a) Θ(n 2 ) b) Θ(n 6 log 2 (n)) c) Θ(n 3 log(n)) d) Θ(n 3 log(n) + n 2 ) e) Θ(n 5 log(n)) Máme-li velikost pole rovnu k, Select sort jej zpracuje v čase Θ(k 2 ). Velikost našeho pole je k = 2n 3 log(n), takže bude zpracováno v čase Θ (k 2 ) = Θ((2n 3 log(n)) 2 ) = = Θ(4n 6 log 2 (n)) = Θ(n 6 log 2 (n)). Platí tedy možnost b). 6. 9 Uvedený konečný automat se startovním stavem S přijme slovo a) babb b) ba c) aaa d) aba e) žádné z předchozích Slovo aba je to pravé. Posloupnost přechodů je tato: (S), a --> (A), b --> (S), b --> (A). (A) je koncový stav, takže slovo aba je přijato. Žádné jiné slovo z nabízených přijato nebude, takže platí možnost d). 7. 6 Druh dat a) popisuje syntaxi datového typu b) popisuje sémantiku datového typu c) je povolená množina hodnot d) lze popsat graficky pomocí oválů, malých kroužků a spojovacích čar a písmen Toto je otázka směrem do definic druh dat je to, co se zmiňuje ve variantě c).
8. 13 Pravá rotace v a) v podstromu s kořenem u přemístí pravého syna u p do kořene. Přitom se uzel u stane levým synem p a levý podstrom p se stane pravým podstromem b) v podstromu s kořenem u přemístí levého syna u l do kořene. Přitom se uzel u stane pravým synem l a levý podstrom l se stane pravým podstromem c) v podstromu s kořenem u přemístí pravého syna u p do kořene. Přitom se uzel u stane levým synem p a pravý podstrom p se stane levým podstromem d) v podstromu s kořenem u přemístí levého syna u l do kořene. Přitom se uzel u stane pravým synem l a pravý podstrom l se stane levým podstromem Zde můžeme stejně jako ve vedlejším oddělení jen doporučit nakreslení obrázku, případně nahlédnutí do vhodné publikace, možnost d) se ukáže být jedinou správnou. 9. 17 Binární vyhledávací strom: a) musí splňovat podmínku haldy b) udržuje v každém uzlu referenci na uzel s nejbližším větším klíčem c) udržuje v každém uzlu referenci na uzel s nejbližším větším i s nejbližším menším klíčem d) po každém vložení prvku do BVS musí proběhnout vyvážení stromu e) po průchodu v pořadí inorder vydá seřazenou posloupnost klíčů Podmínku haldy musí splňovat pouze halda, která ani není BVS. Reference na jiné uzly než jen bezprostřední potomky nejsou v BVS povinností a vyvažování BVS také není povinné. Zbývá tak jen možnost e), o níž se hovoří i při výkladu pořadí inorder jako takového. 10. 27 Následující datová struktura má definován index a) fronta b) tabulka c) strom d) množina e) žádná z uvedených Ze základních ADT má index definováno jen pole. To se v nabízeném seznamu nevyskytuje, takže platí možnost e) žádná z uvedených.
Jméno:... St. Sk.:. Cvičící:.. Bodů ze cv.: 1. (5 b.) Deklarujte uzel binárního stromu, který bude obsahovat celočíselné složky výška a hloubka. Ve složce hloubka bude uložena hloubka daného uzlu ve stromu, ve složce výška jeho výška. Výška uzlu X je definována jako vzdálenost od jeho nejvzdálenějšího potomka (= počet hran mezi uzlem X a jeho nejvzdálenějším potomkem). Napište funkci, která každému uzlu ve stromu přiřadí korektně hodnotu jeho hloubky a výšky. -------------------- Při počítaní hloubky stačí každému potomku uzlu X přiřadit o 1 větší hloubku než má uzel X. Sama rekurzivní procedura mluví za dlouhé výklady: void sethloubka(node x, int depth) { if (x == null) return; x.hloubka = depth; sethloubka(x.left, depth+1); sethloubka(x.right,depth+1); Přiřazení hloubky každému uzlu ve stromu pak provedeme příkazem sethloubka(ourtree.root, 0); Všimněte si, že hloubky se uzlům přiřazují celkem logicky v pořadí preorder. Při přiřazování výšky uzlu X musíme naopak znát výšku jeho potomků, takže se nabízí zpracování v pořadí postorder: void setvyska(node x) { int vyskal = -1; // nejprve predpokladejme, ze uzel nema potomky. int vyskar = -1; // dtto if (x.left!= null) { setvyska(x.left); vyskal = x.left.vyska; if (x.right!= null) { setvyska(x.right); vyskar = x.right.vyska; x.vyska = max(vyskal, vyskar) + 1; Přiřazení výšky každému uzlu ve stromu pak provedeme příkazem if (ourtree.root!= null) setvyska(ourtree.root); Celý proces s výškou lze zachytit ještě úsporněji: int setvyska(node x) { if (x == null) return -1; x.vyska = 1+ max(setvyska(x.left), setvyska(x.right)); Přiřazení výšky každému uzlu ve stromu pak provedeme např. příkazem zbytecnaprom = setvyska(ourtee.root);
2. (5 b.) 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. 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) // počet návštěv akt. uzlu vzrostl // a doprava jdeme poprvé if( time == 3) { zpracuj(nodex); // end of while // 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.
3. (5 b.) Implementujte operace Init, Search, Insert pro rozptylovací tabulku s otevřeným rozptylováním, do níž se ukládají celočíselné klíče. Předpokládejte, že rozptylovací funkce je již implementována a Vám stačí ji jen volat. Použijte strategii Linear probing. Zde pokud se nevyskytne přímá žádost řešení prozatím neuvádím, jedná se jen o přímou implementaci standardní situace popsané v přednášce i literatuře, nic se tu nemusí vymýšlet, předpokládáme tedy, že si zájemci mohou (příp. s knihou či obrazovkou) tamtéž uvedené kódy projít.
Jméno:... St. Sk.:. Cvičící:.. Bodů ze cv.: 1. (5 b.) Na vstupu je neseřazené pole prvků pole[počet]. Bez toho, abyste toto pole seřadili, napište funkci, která v něm nalezne nalezne k-tý nejmenší prvek (= k-tý v seřazeném poli). Použijte metodu rozděl a panuj jako při řazení Quick-sortem, ale celé pole neřaďte. Hodnota k bude vstupním parametrem Vašeho algoritmu. Postup, který ve Quick sort-u dělí úsek pole na malé a velké hodnoty můžeme beze zbytku využít i zde. Dejme tomu, že máme pole prvků [1..n]. Nechť po rozdělení na malé a velké má poslední prvek v oddílu malých index m. Nyní můžeme usuzovat takto: Pokud je k <= m, znamená to, že hledaný k-tý nejmenší prvek se nalézá mezi prvními (nejmenšími!!) m prvky v poli, čili jej budeme dále hledat pouze v úseku [1..m] a úsek [m+1..n] můžeme zcela zanedbat. Pokud naopak k > m, znamená to, že úsek [1..m] je zcela nezajímavý a hledat dále musíme již jen v úseku [m+1..n]. Ve vybraném úseku pak budeme dále tuto myšlenku aplikovat zcela identicky pomocí rekurze na čím dál menší úseky, tak dlouho, dokud nebude aktuální úsek obsahovat jen jeden prvek. To bude právě k-tý nejmenší prvek v celém poli. Postup dělení na malé a velké okopírujeme z Quick sort-u a umístíme pro přehlednost do jedné funkce, která vrátí hodnotu m, tj index posledního prvku v úseku malých hodnot. int maxindexsmall(int [ ] array, int Lo, int Hi); S využitím této funkce můžeme pak celý postup zapsat takto: int k_min (int [ ] array, int Lo, int Hi, int k) { if (Lo == Hi) return array[lo]; int m = maxindexsmall(array, Lo, Hi); if (k <= m) return k_min(array, Lo, m, k); if (k > m) return k_min(array, m+1, Hi, k); Hledanou k-tou nejmenší hodnotu celého pole pak získáme příkazem k_nejm_hodnota = k_min(array, 1, n, k); Poznámka: Jo, jo, rekurze... 2. (5 b.) Implementujte cyklickou frontu v poli pole[délka]. Dokud není pole zcela naplněno, musí být fronta stále funkční, tzn. musí být možné do ní stále přidávat i z ní ubírat. Narazí-li konec nebo začátek fronty na konec nebo začátek pole, neposunujte všechny prvky v poli, zvolte výhodnější cyklickou strategii, která zachová konstantní složitost operace Vlož a Vyjmi. Myšlenka cyklické fronty je velmi jednoduchá. Připojíme první pozici pole ihned za poslední pozici a poslední pozici ihned před první. Tak se nám celé pole zacyklí neboť při postupu stále doprava dojdeme nakonec na začátek pole. Nejsnáze se to implementuje pomocí indexů pole. Dejme tomu, že máme pole array[n]. int nextindex(int i) { if (i < n-1) return (i+1); else return 0;
int previndex(int i) { if (i > 0) return (i-1); else return (n-1); Budeme-li pro pohyb v poli užívat pouze uvedené funkce, nemůžeme se dostat mimo meze pole. Protože se snadno stane, že po určité době života fronty se i při neprázdné frontě octne konec fronty před jejím začátkem v poli (namaluje si příslušný obrázek!), bude vhodné pro kontrolu prázdnosti či přeplnění fronty registrovat zvlášť počet prvků ve frontě Vložení prvku do fronty pak proběhne například takto: boolean insert(int key) { if (fronta.pocetprvku == n) return false; fronta.konec = nextindex(fronta.konec); array[fronta.konec] = key; fronta.pocetprvku++; return true; Obdobně bude implementována operace Delete, operace Init a Empty jsou již snad příliš jednoduché na to, aby tu musely být uvedeny. 3. (5 b.) Implementujte operace Init, Search, Insert a Delete pro rozptylovací tabulku se zřetězeným rozptylováním, do níž se ukládají celočíselné klíče. Předpokládejte, že rozptylovací funkce je již implementována a Vám stačí ji jen volat. Zde pokud se nevyskytne přímá žádost řešení prozatím neuvádím, jedná se jen o přímou implementaci standardní situace popsané v přednášce i literatuře, nic se tu nemusí vymýšlet, předpokládáme tedy, že si zájemci mohou (příp. s knihou či obrazovkou) tamtéž uvedené kódy projít.
Statistika Test A, první v tomto souboru, Typická náročná otázka: 1.... Halda není stabilní datová struktura.... Respondentů: 50 Otázka 1 9 5 10 7 2 4 8 6 3 Počet chybných odpovědí 36 33 20 20 10 9 9 7 4 0 lebková X X X X Test B, druhý v tomto souboru, Typická náročná otázka: 10....Žádná z uvedených datových struktur nemá definován index Respondentů: 45 Otázka 10 4 9 5 1 8 6 7 2 3 Počet chybných odpovědí 27 17 17 16 13 13 9 6 5 2 lebková X X X X