Roztřídění poznámek z PPA2 podle témat

This commit is contained in:
Filip Znachor 2023-06-04 13:09:10 +02:00
parent 247b469959
commit b8eb3580e7
19 changed files with 343 additions and 349 deletions

View file

@ -9,7 +9,7 @@
## Rozhraní (interface)
- popisuje, co nějaká třída umí: hlavičky metod
- popisuje, co nějaká třída umí: **hlavičky metod**
- neříká nic o tom, jak jsou metody implementovány
- různé třídy mohou implementovat stejné rozhraní
- jedna třída může implementovat více rozhraní
@ -41,37 +41,12 @@
- dědit lze vždy jen od **jednoho** předka
- referenci na potomka je možné kdykoli přetypovat na referenci na předka
- přetypovává se **reference**, nikoli instance samotná
- obráceně ne (runtime error) => předek nemusí implementovat vše co potomek
- každou referenci je možné přetypovat na referenci na `Object`
- obráceně ne (runtime error), předek nemusí implementovat vše, co potomek
- každou referenci je možné přetypovat na `Object`
## Polymorfismus
- instanci potomka lze použít všude kde se očekává předek
- parametr metody
- pole předků
- ...
# Rekurzivní programy
- programy, které volají sami sebe
- rekurze, musí někdy skončit!
## Přímá rekurze
- metoda volá přímo sebe sama
- je vidět na první pohled
## Nepřímá rekurze
- `a` volá `b`, `b` volá `a`
## Problémy rekurze
- problémy s hloubkou zásobníku
- spíše programátorská chyba - **přetečení zásobníku**
- pro mnoho praktických problémů je rekurze dostatečná
- Java umožňuje nastavit velikost zásobníku
- často lze zapsat do nerekurzní formy
- nerekurzivní zápis je **často rychlejší**
- zrychlení se projeví, pokud je samotný vykonávaný kód triviální
- někdy je přepsání poměrně složité
- ...

View file

@ -0,0 +1,24 @@
# Rekurzivní programy
- programy, které volají sami sebe
- rekurze, musí někdy skončit!
## Přímá rekurze
- metoda volá přímo sebe sama
- je vidět na první pohled
## Nepřímá rekurze
- `a` volá `b`, `b` volá `a`
## Problémy rekurze
- problémy s hloubkou zásobníku
- spíše programátorská chyba - **přetečení zásobníku**
- pro mnoho praktických problémů je rekurze dostatečná
- Java umožňuje nastavit velikost zásobníku
- často lze přepsat do nerekurzivní formy
- nerekurzivní zápis je **často rychlejší**
- zrychlení se projeví, pokud je samotný vykonávaný kód triviální
- někdy je přepsání poměrně složité

View file

@ -1,16 +1,16 @@
# Výpočetní složitost
- doba výpočtu T(n) v závislosti na vstupu n
- doba výpočtu $T(n)$ v závislosti na vstupu $n$
- je důležité určit, co je důležité
- zajímá nás, co se děje, když n roste
- zajímá nás, co se děje, když $n$ roste
- různé funkce rostou různě rychle
- druhy algoritmů
- rychle rostoucí funkce = neefektivní algoritmus
- pomalu rostoucí funkce = efektivní algoritmus
- záleží na tom, jestli se funkce vejde pod jinou
- například lineární pod kubickou
- ne hned, ale od nějakého $n_0$ ano
- nezáleží na násobku, jen to $n_{0}$ bude dál
- nemusí se vejít hned, ale třeba od nějakého $n_0$ ano
- nezáleží na násobku $n$, jen to $n_{0}$ bude dál
## O-notace
@ -76,7 +76,7 @@ Význam funkcí může být:
- čas výpočtu pro nejhorší možný vstup velikosti n
- průměrný čas výpočtu pro vstup velikosti n
- počet instrukcí pro ...
- množství paměti nutné pro zpracování ...
- množství paměti nutné pro zpr# 1020acování ...
- ...
Je ale potřeba nesrovnávat hrušky s pomeranči.
@ -85,5 +85,5 @@ Je ale potřeba nesrovnávat hrušky s pomeranči.
## Určení výpočetní složitosti programu
- je potřeba určit funkci $g(n)$
- při určování můžeme zanedbat časy jednotlivých instrukcí, **pokud jsou opravdu konstatní** (nezávislá na parametru n)
- při určování můžeme zanedbat časy jednotlivých instrukcí, **pokud jsou opravdu konstantní** (nezávislé na parametru $n$)
- výpočetní složitost neřeší rozdílné výkony počítačů

View file

@ -95,7 +95,7 @@ Nalezení pořadí (indexů) pro množinu prvků podle nějakého uspořádání
- procházíme obě posloupnosti současně (držíme aktuální index pro obě)
- do výsledné posloupnosti zapíšeme menší ze dvou aktuálních prvků
- v příslušné posloupnosti se posuneme na další prvek
- efentivní implementace:
- efektivní implementace:
- pracuje s úseky původního pole a eliminuje testování konce polí A a B
- alokuje se dočasné pole
- v poli se vytvoří tzv. bitonická posloupnost

View file

@ -0,0 +1,25 @@
# Abstraktní datové typy
- abstraktní - nezabývá se rozdíly, ale tím, co je **společné**
- definují možné operace s daty
- nedefinují způsob uložení dat ani provedení operací (implementaci)
**ADT vs rozhraní**
- ADT můžou být implementovány různě v různých jazycích
- rozhraní jen způsob implementace ADT v Javě
**Kolekce**
- datové struktury, které uchovávají sadu prvků
- umožňují operace s daty
- přidat prvek (na začátek, na konec, za/před prvek, s nějakým klíčem, ...)
- vybrat prvek (na začátku, na konci, na indexu, s extrémním klíčem, ...)
- odebrat prvek (na začátku, na konci, na indexu, s extrémním klíčem, ...)
**Implementace ADT**
- určuje složitost operací
- obvykle reprezentována třídou
**Úkol programátora**
- vybrat vhodnou ADT
- vybrat vhodnou implementaci ADT
- vědět, co ADT dělá (jaká je složitost operací)

View file

@ -1,29 +1,3 @@
# Abstraktní datové typy
- abstraktní - nezabývá se rozdíly, ale tím, co je **společné**
- definují možné operace s daty
- nedefinují způsob uložení dat ani provedení operací (implementaci)
**ADT vs rozhraní**
- ADT můžou být implementovány různě v různých jazycích
- rozhraní jen způsob implementace ADT v Javě
**Kolekce**
- datové struktury, které uchovávání sadu prvků
- umožňují operace s daty
- přidat prvek (na začátek, na konec, za/před prvek, s nějakým klíčem, ...)
- vybrat prvek (na začátku, na konci, na indexu, s extrémním klíčem, ...)
- odebrat prvek (na začátku, na konci, na indexu, s extrémním klíčem, ...)
**Implementace ADT**
- určuje složitost operací
- obvykle reprezentována třídou
**Úkol programátora**
- vybrat vhodnou ADT
- vybrat vhodnou implementaci ADT
- vědět, co ADT dělá (jaká je složitost operací)
# Zásobník
- abstraktní datová struktura
@ -69,7 +43,7 @@
- $n = 2^k + 1, o = 2^{k+1} - 1$
- $o = 2(2k) - 1 = 2(2k + 1) - 3 = 2n - 3 < 2n$
- **závěr**:
- složitost oprace přidání je $\mathcal{O}(n)$
- složitost operace přidání je $\mathcal{O}(n)$
- průměrná složitost je $\Theta(1)$
- při zvětšování **o konstantu** to neplatí
- přidání n prvků $\Omega(n^2)$
@ -104,7 +78,7 @@ Tyto aspekty není potřeba se učit, vyplývají ze způsobu implementace.
#### Použití z klientské třídy
- použití obou implementací je úplně stejné
- použití obou implementací je úplně stejné
- uživatel vůbec nemusí vědět, co se děje uvnitř
- vnitřní data jsou udržována v konzistentním stavu

View file

@ -0,0 +1,45 @@
# Odstranění rekurze
**Proč?**
- problémy s hloubkou zanoření (velikost zásobníku je malá)
- neefektivní, protože se při volání metod kopírují hodnoty skutečných parametrů do zásobníku
### Koncová rekurze
- kód končí jediným rekurzivním voláním
- nejjednodušší druh rekurze, je snadné přepsat na cyklus
### Eliminace vlastním zásobníkem
- použití zásobníku jako seznamu úkolů (například třída `Task`)
- položky v zásobníku budou obsahovat:
- co se má udělat (parametry)
- v jaké části úkolu jsme (segment)
- data nutná pro pokračování výpočtu (mezivýsledky)
- pokud je potřeba pro dokončení úkolu rekurze, přidá se do zásobníku nový úkol
- až se dojde k triviálnímu úkolu, data se předají dalšímu úkolu
**Jak rekurzi odstranit?**
- rozdělíme kód na segmenty mezi rekurzivními voláními
- na začátku do zásobníku vložíme pouze hlavní úkol
- zpracováváme položky zásobníku, dokud není prázdný
- místo rekurzivního volání:
- uložíme stav výpočtu položky na vrcholu zásobníku (stavové proměnné)
- zvýšíme segment na vrcholu zásobníku (jsme o jednu část úkolu dál)
- do zásobníku přidáme nový úkol reprezentující rekurzi
- ukončíme zpracování aktuálního úkolu (smyčka se k němu vrátí později)
- místo vrácení hodnoty:
- uložíme výsledek do proměnné `result`
- odebereme úkol z vrcholu zásobníku
- při pokračování dalším segmentem:
- obnovíme stavové proměnné z položky na vrcholu zásobníku
- výsledek posledního rekurzivního volání se nachází v proměnné `result`
- na konci vrátíme proměnnou `result`
**Závěr**
- každou rekurzivní funkci je možné přepsat bez použití rekurze
- pracný mechanický postup
- horší přehlednost kódu
- potřeba implementace zásobníku
- přepsání bez rekurze zachovává třídu složitosti (nezajišťuje efektivitu)
- pro nalezení efektivnější implementace mechanický postup neexistuje

View file

@ -0,0 +1,42 @@
# Fronta
- abstraktní datová struktura
- podobá se zásobníku
- **FIFO** - first in, first out
**Operace**
- přidání prvku na konec
- vybrání prvku na začátku
- odebrání prvku na začátku
### Implementace spojovým prvkem
- velmi podobná zásobníku
- kromě odkazu na první prvek uchovává i **odkaz na poslední prvek**
**Složitost operací**
- všechny $\Theta(1)$
- vyšší náročnost na paměť
### Implementace polem
- velmi podobná zásobníku
- po odebrání vznikají prázdná místa v poli
- je možné je využít pro přidávání
- struktura obsahuje:
- index prvního prvku
- počet obsazených indexů
- při přidávání prvku:
- kontrolujeme, jestli se prvek do pole vejde
- pokud je **místo na začátku**, vložíme prvek na začátek pole
- pokud je **pole plné** zvětšíme pole a zkopírujeme od indexu prvního prvku až do délky pole
- využijeme modula - pomocí něj index "přeteče" na začátek pole
- máme pole o 5 prvcích
- vkládáme na index 5, nicméně 5 % 5 je 0 (začátek pole)
**Složitost operací**
- vybrání/odebrání prvního prvku $\Theta(1)$
- přidání prvku na konec:
- většinou $\Theta(1)$
- při zvětšení pole $\Theta(n)$
- v průměru $\Theta(1)$

View file

@ -0,0 +1,34 @@
# Třídy s typovým parametrem
- často potřeba kolekce pro různé datové typy
- není vhodné vytvářet samostatné kolekce (třídy) pro různé datové typy
### Řešení pomocí Object
- všechny třídy dědí z Object
- vkládání není problém
- **problém**
- při vybrání prvku je potřeba **jej přetypovat**
- případná chyba se objeví až za běhu programu
**Obalovací třída**
- anglicky wrapper
- poskytuje metody, které provádějí **přetypování na správnou třídu**
- skrývá vnitřní implementaci s `Object`
- **problém**
- je potřeba ji vytvořit pro každý datový typ
**Generická třída**
- **"správné" řešení**
- třída s typovým parametrem `class MyStack<T>`
- datový typ `Object` poté nahradíme typem `T`
- bohužel nefunguje všude
- typ `T` pouze přebíráme a vracíme
- vnitřně používáme `Object`, který poté přetypujeme
- typovým parametrem nemůže být primitivní datový typ
- využijeme obalovací třídu (`Integer`, `Double`, ...)
- přetypování (boxing a unboxing) probíhá automaticky, je ale dobré o něm vědět
- někdy je potřeba, aby typový parametr něco uměl
- je možné použít rozhraní
- `class MyStack<T extends IProcessable>`
- pokud potřebujeme použít nějakou metodu tohoto rozhraní ve třídě, můžeme `Object` přetypovat na `T`

View file

@ -16,7 +16,7 @@ Jak specifikovat pozici?
- ukazuje na prvek
- některé operace problematické (např. vložení - před nebo za prvek?)
- ukazuje mezi prvky
- jednožnačné vkládání: sem
- jednoznačné vkládání: sem
- vybírání/odebírání dohodou - třeba prvek napravo
### Implementace spojovou strukturou
@ -43,7 +43,7 @@ Jak specifikovat pozici?
- seznam neumožňuje prácí **na více místech**
- vznik odděleného ukazovátka od dat - **iterátoru** (návrhový vzor)
- třída, která má aktuální index `current` a referenci `list
- třída, která má aktuální index `current` a referenci `list`
- stejná implementace jako u seznamu (místo `first` se použije `list.first`)`
**Operace**
@ -75,84 +75,4 @@ Jak specifikovat pozici?
- pro index následovníka (`next`) - prázdného či obsazeného prvku
- index začátku `first`
- index volného pole `empty`
- index aktuálního prvku `current`
# Kolekce v Javě
Java poskytuje knihovní třídy pro základní implementaci kolekcí
### Třída `LinkedList<T>`
- spojová datová struktura
**Operace**
- operace v $\mathcal O(1)$
- `void addFirst(T)`, `void addLast(T)`
- `T getFirst()`, `T getLast()`
- `void removeFirst()`, `void removeLast()`
- operace v $\Omega(n)$
- `void add(int, T)` - vložení na daný index
- `T get(int)` - získání z indexu
- `void remove(int)` - smazání z indexu
- `Iterator<T> iterator()`
- `ListIterator<T> listIterator()` - lepší iterátor
### Třída `ArrayList<T>`
- dynamické pole
**Operace**
- operace v $\mathcal O(1)$
- `void add(T)`
- `T get(int)`
- operace v $\Omega(n)$
- `void remove(int)
- operace pro získání iterátorů - ale s **vyšší složitostí**!
### Třída `Iterator<T>`
**Operace**
- `T next()` - vrátí přeskočený prvek a posune se dopředu
- na konci vyhodí výjimku
- `boolean hasNext()` - existence dalšího prvku
- `void remove()` - odstraní poslední prvek vrácený metodou `next()`
- před dalším odstraněním potřeba zavolat `next()`
- chybí:
- návrat na začátek (stačí si vyžádat nový iterátor)
- metoda pro vložení prvku
**Vylepšený `ListIterator<T>`**
- má navíc:
- `T previous()` - vrátí hodnotu předchozího prvku a posune iterátor zpět
- na začátku vyhodí výjimku
- `void add(T)` - vloží prvek na pozici iterátoru
### Rozhraní `Queue<T>`
- rozhraní fronty
- `LinkedList<T>` toto rozhraní implementuje
**Operace**
- `void add()`
- `T element()`
- `T remove()`
### Třída `Stack<T>`
implementace zásobníku dynamickým polem - `ArrayList`
- stejné metody poskytuje i `LinkedList`
**Operace**
- `void push(T)`
- `T pop()`
- `T peek()`
### Obecná zásada
Kolekce v čase $\mathcal O(1)$ umí buď
1) vybrat prvek na specifickém indexu, anebo
2) vyjmout/vložit prvek na pozici iterátoru,
ale nikdy ne obojí současně!
Najít i-tý prvek ve spojovém seznamu zabere vždy $\Omega(n)$.
- index aktuálního prvku `current`

View file

@ -0,0 +1,79 @@
# Kolekce v Javě
Java poskytuje knihovní třídy pro základní implementaci kolekcí
### Třída `LinkedList<T>`
- spojová datová struktura
**Operace**
- operace v $\mathcal O(1)$
- `void addFirst(T)`, `void addLast(T)`
- `T getFirst()`, `T getLast()`
- `void removeFirst()`, `void removeLast()`
- operace v $\Omega(n)$
- `void add(int, T)` - vložení na daný index
- `T get(int)` - získání z indexu
- `void remove(int)` - smazání z indexu
- `Iterator<T> iterator()`
- `ListIterator<T> listIterator()` - lepší iterátor
### Třída `ArrayList<T>`
- dynamické pole
**Operace**
- operace v $\mathcal O(1)$
- `void add(T)`
- `T get(int)`
- operace v $\Omega(n)$
- `void remove(int)`
- operace pro získání iterátorů - ale s **vyšší složitostí**!
### Třída `Iterator<T>`
**Operace**
- `T next()` - vrátí přeskočený prvek a posune se dopředu
- na konci vyhodí výjimku
- `boolean hasNext()` - existence dalšího prvku
- `void remove()` - odstraní poslední prvek vrácený metodou `next()`
- před dalším odstraněním potřeba zavolat `next()`
- chybí:
- návrat na začátek (stačí si vyžádat nový iterátor)
- metoda pro vložení prvku
**Vylepšený `ListIterator<T>`**
- má navíc:
- `T previous()` - vrátí hodnotu předchozího prvku a posune iterátor zpět
- na začátku vyhodí výjimku
- `void add(T)` - vloží prvek na pozici iterátoru
### Rozhraní `Queue<T>`
- rozhraní fronty
- `LinkedList<T>` toto rozhraní implementuje
**Operace**
- `void add()`
- `T element()`
- `T remove()`
### Třída `Stack<T>`
implementace zásobníku dynamickým polem - `ArrayList`
- stejné metody poskytuje i `LinkedList`
**Operace**
- `void push(T)`
- `T pop()`
- `T peek()`
### Obecná zásada
Kolekce v čase $\mathcal O(1)$ umí buď
1) vybrat prvek na specifickém indexu, anebo
2) vyjmout/vložit prvek na pozici iterátoru,
ale nikdy ne obojí současně!
Najít i-tý prvek ve spojovém seznamu zabere vždy $\Omega(n)$.

View file

@ -36,7 +36,7 @@ Ideálně by měly mít všechny (kromě všech klíčů) složitost $\Theta(1)$
**Tabulka s přímým adresováním**
- pole, kde index je klíč
- problém:
- často neznáme rozsah možnách klíčů
- často neznáme rozsah možných klíčů
- počet přípustných klíčů může být velmi velký nebo nekonečný
- neřeší složitější klíče
- výhoda: splňuje $\Theta(1)$

View file

@ -74,7 +74,7 @@ Operace se můžou lišit podle druhu stromu (binární, uspořádaný, ...).
- předek vrcholu s indexem i leží na indexu i/2 (celočíselně)
- strom o hloubce h má vrcholy s maximálním indexem $2^{h+1} - 1$
- **reprezentace vrcholu**:
- reprezenntován svým indexem
- reprezentován svým indexem
- musí mu být přiřazena data, která jsou uložena v poli
- **data přiřazená vrcholu**:
- uložená do pole na index vrcholu
@ -88,7 +88,7 @@ Operace se můžou lišit podle druhu stromu (binární, uspořádaný, ...).
- **nevýhody**:
- pouze pro binární stromy
- musíme předem znát počet prvků
- alokuje se paměť i přo nepřítomné prvky (nebo referenci)
- alokuje se paměť i pro nepřítomné prvky (nebo referenci)
Pozn.: **Úplný binární strom**
- pro určitý index k platí:
@ -119,13 +119,14 @@ Pozn.: **Úplný binární strom**
- zahájení průchodu zavoláním metody `preorder/inorder/postorder` nad kořenem
### Binární vyhledávací strom (BST)
- reprezentuje uspořádanou množinu prvků
- prvky seřazené pomocí klíče - nejjednodušeji `int`
- strom je pouze implementací, ADT hierarchický není
**Operace**
- vložení prvku s klíčem
- oderání prvku s klíčem
- odebrání prvku s klíčem
- zjištění přítomnosti prvku s klíčem
- nalezení největšího a nejmenšího klíče
- vybrání všech prvků v pořadí klíčů

View file

@ -6,7 +6,7 @@
### Druhy grafů
- **Orientovaný graf**
- podchybuje nesymetrické vztahy
- podchycuje nesymetrické vztahy
### Formální definice
@ -24,7 +24,7 @@
- $\vert V\vert$ - počet vrcholů grafu
- $\vert E\vert$ - počet hran grafu
+ $V(G)$ - množina vrcholů grafu $G$
+ $V(E)$ - množina hran grafu $G$
+ $E(G)$ - množina hran grafu $G$
- $y \in V$ je sousedem $x \in V$ právě když
- existuje orientovaná hrana $E = (x, y)$
- existuje neorientovaná hrana $E, x \in E, y \in E$
@ -200,4 +200,57 @@
+ celkem
+ úplný, popř. hustý graf nebo implementace sousednosti maticí: $\Omega(\vert V\vert^2)$
+ implementace sousednosti seznamem: $\mathcal{O}(\vert V\vert + \vert E\vert)$
+ graf může mít počet hran až $\vert V\vert^2$
+ graf může mít počet hran až $\vert V\vert^2$
**Strom dosažitelnosti**
- tvoří se z nějakého určeného vrcholu (kořen)
- ukazuje, jaká je nejkratší cesta do ostatních vrcholů
- reprezentován polem, kde na indexu vrcholu je uložen předek
- nemusí být jednoznačný (může existovat více nejkratších cest)
#### Prohledávánı́ do hloubky (DFS)
- Depth-First Search
- algoritmus postupuje do většı́ vzdálenosti od počátečnı́ho vrcholu, pokud může
+ předpokládáme, že označenı́ (mark) je před volánı́m DFS inicializováno na 0 pro všechny vrcholy
+ DFS je potřeba doplnit o nějaký užitečný kód
+ záleží to na řešeném problému
**Značenı́ vrcholů**
- nezpracovaný (”bı́lá”), kód 0
- rozpracovaný (”šedá”), kód 1
- dokončený (”černá”), kód 2
**Složitost**
- rekurzivní metoda se pro každý vrchol volá pouze jednou - $\Omega(\vert V\vert)$
- pro každý vrchol se prochází seznam hran:
- reprezentace maticí - $\Omega(\vert V\vert^2)$
- reprezentace seznamem - $\mathcal{O}(\vert E\vert)$
- celkem: $\mathcal{O}(\vert V\vert + \vert E\vert)$ při reprezentaci seznamem
- může být i $\Omega(\vert V\vert^2)$, pokud $\vert E\vert = k\vert V\vert^2$
**Použití DFS**
- Zjištění dosažitelnosti vrcholu
- pokud předpokládáme, že bude vrchol daleko, je DFS vhodnější než BFS
+ Zjištění cyklu v grafu
+ vrchol označíme jedničkou a poté ho znovu hledáme
+ Topologické řazení
+ prvně je potřeba ověřit, že graf nemá cykly
+ vrcholy jsou činnosti, hrany jsou závislosti
+ hrana $A \to B$ značí, že se prvně musí vykonat A a potom až B
+ pomocí DFS můžeme snadno určit pořadí činností (pomocí otočeného grafu)
**DFS bez rekurze**
- pravděpodobně nastanou problémy s hloubkou zásobníku
+ vystačíme si se zásobníkem celých čísel (vrcholů)
+ `segment` (jaký je stav vrcholu) je v označení vrcholu (`mark`)
**Nejkratší cesta v ohodnoceném grafu**
- velmi častý problém
- ohodnocení: čas, vzdálenost, ...
- úkol: nalézt nejkratší vzdálenost ke všem vrcholům
- **Dijkstrův algoritmus**
- je potřeba prioritní fronta
- přidání dvojice vrchol + ohodnocení
- vybrání/odebrání vrcholu s nejmenším ohodnocením
- změna ohodnocení vrcholu

View file

@ -75,21 +75,4 @@
+ budeme je vytvářet od konce stromu (od listů)
- složitost
- nejhorší a očekávaná: $\Theta(n \log(n))$
- paměťová: $\Theta(1)$
# Rekapitulace ADT
**Přístup k datům pomocí indexů**
| | vybrání | odebrání | přidání |
| ----------------- | ----------- | ---------------------------- | ---------------------------- |
| spojová struktura | $\Theta(n)$ | $\Theta(1)$ pokud byl vybrán | $\Theta(1)$ pokud byl vybrán |
| dynamické pole | $\Theta(1)$ | $\Theta(n)$ | $\Theta(n)$ |
**Přístup k datům pomocí klíče**
| | přidání | odebrání | vybrání | vybrání maxima | odebrání maxima |
| ------- | ----------------- | ----------------- | ----------------- | ----------------- | ----------------- |
| Tabulka | $\Theta(1)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(n)$ | $\Theta(n)$ |
| BST | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ |
| Halda | $\Theta(\log(n))$ | N/A | N/A | $\Theta(1)$ | $\Theta(\log(n))$ |
- paměťová: $\Theta(1)$

View file

@ -0,0 +1,16 @@
# Rekapitulace ADT
**Přístup k datům pomocí indexů**
| | vybrání | odebrání | přidání |
| ----------------- | ----------- | ---------------------------- | ---------------------------- |
| spojová struktura | $\Theta(n)$ | $\Theta(1)$ pokud byl vybrán | $\Theta(1)$ pokud byl vybrán |
| dynamické pole | $\Theta(1)$ | $\Theta(n)$ | $\Theta(n)$ |
**Přístup k datům pomocí klíče**
| | přidání | odebrání | vybrání | vybrání maxima | odebrání maxima |
| ------- | ----------------- | ----------------- | ----------------- | ----------------- | ----------------- |
| Tabulka | $\Theta(1)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(n)$ | $\Theta(n)$ |
| BST | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ | $\Theta(\log(n))$ |
| Halda | $\Theta(\log(n))$ | N/A | N/A | $\Theta(1)$ | $\Theta(\log(n))$ |

View file

@ -1,120 +0,0 @@
# Odstranění rekurze
**Proč?**
- problémy s hloubkou zanoření (velikost zásobníku je malá)
- neefektivní , protože se při volání metod kopírují hodnoty skutečných parametrů do zásobníku
### Koncová rekurze
- kód končí jediným rekurzivním voláním
- nejjednodušší druh rekurze, je snadné přepsat na cyklus
### Eliminace vlastním zásobníkem
- použití zásobníku jako seznamu úkolů (například třída `Task`)
- položky v zásobníku budou obsahovat:
- co se má udělat (parametry)
- v jaké čísti úkolu jsme (segment)
- data nutná pro pokračování výpočtu (mezivýsledky)
- pokud je potřeba pro dokončení úkolu rekurze, přidá se do zásobníku nový úkol
- až se dojde k triviálnímu úkolu, data se předají dalšímu úkolu
**Jak rekurzi odstranit?**
- rozdělíme kód na segmenty mezi rekurzivními voláními
- na začátku do zásobníku vložíme pouze hlavní úkol
- zpracováváme položky zásobníku, dokud není prázdný
- místo rekurzivního volání:
- uložíme stav výpočtu položky na vrcholu zásobníku (stavové proměnné)
- zvýšíme segment na vrcholu zásobníku (jsme o jednu část úkolu dál)
- do zásobníku přidáme nový úkol reprezentující rekurzi
- ukončíme zpracování aktuálního úkolu (smyčka se k němu vrátí později)
- místo vrácení hodnoty:
- uložíme výsledek do proměnné `result`
- odebereme úkol z vrcholu zásobníku
- při pokračování dalším segmentem:
- obnovíme stavové proměnné z položky na vrcholu zásobníku
- výsledek posledního rekurzivního volání se nachází v proměnné `result`
- na konci vrátímě proměnnou `result`
**Závěr**
- každou rekurzivní funkci je možné přepsat bez použití rekurze
- pracný mechanický postup
- horší přehlednost kódu
- potřeba implementace zásobníku
- přepsání bez rekurze zachovává třídu složitosti (nezajišťuje efektivitu)
- pro nalezení efektivnější implementace mechanický postup neexistuje
# Fronta
- abstraktní datová struktura
- podobá se zásobníku
- FIFO - first in, first out
**Operace**
- přidání prvku na konec
- vybrání prvku na začátku začátku
- odebrání prvku na začátku začátku
### Implementace spojovým prvkem
- velmi podobná zásobníku
- kromě odkazu na první prvek uchovává i **odkaz na poslední prvek**
**Složitost operací**
- všechny $\Theta(1)$
- vyšší náročnost na paměť
### Implementace polem
- velmi podobná zásobníku
- po odebrání vznikají prázdná místa v poli
- je možné je využít pro přidávání
- struktura obsahuje:
- index prvního prvku
- počet obsazených indexů
- při přidávání prvku:
- kontrolujeme, jestli se prvek do pole vejde
- pokud je **místo na začátku**, vložíme prvek na začátek pole
- pokud je **pole plné** zvětšíme pole a zkopírujeme od indexu prvního prvku až do délky pole
- využijeme modula - pomocí něj index "přeteče" na začátek pole
- máme pole o 5 prvcích
- vkládáme na index 5, nicméně 5 % 5 je 0 (začátek pole)
**Složitost operací**
- vybrání/odebrání prvního prvku $\Theta(1)$
- přidání prvku na konec:
- většinou $\Theta(1)$
- při zvětšení pole $\Theta(n)$
- v průměru $\Theta(1)$
# Třídy s typovým parametrem
- často potřeba kolekce pro různé datové typy
- není vhodné vytvářet samostatné kolekce (třídy) pro různé datové typy
### Řešení pomocí Object
- všechny třídy dědí z Object
- vkládání není problém
- při vybrání prvku je potřeba **jej přetypovat**
- případná chyba se objeví až za běhu programu
**Obalovací třída**
- anglicky wrapper
- poskytuje metody, které provádějí **přetypování na správnou třídu**
- skrývá vnitřní implementaci s `Object`
- je potřeba ji vytvořit pro každý datový typ
**Generická třída**
- třída s typovým parametrem `class MyStack<T>`
- datový typ `Object` poté nahradíme typem `T`
- bohužel nefunguje všude
- typ `T` pouze přebíráme a vracíme
- vnitřně používáme `Object`, který poté přetypujeme
- typovým parametrem nemůže být primitivní datový typ
- využijeme obalovací třídu (`Integer`, `Double`, ...)
- přetypování (boxing a unboxing) probíhá automaticky, je ale dobré o něm vědět
- někdy je potřeba, aby typový parametr něco uměl
- je možné použít rozhraní
- `class MyStack<T extends IProcessable>`
- pokud potřebujeme použít nějakou metodu tohoto rozhraní ve třídě, můžeme `Object` přetypovat na `T`

View file

@ -1,57 +0,0 @@
# Grafy 2
#### Prohledávání do šířky (BFS)
**Strom dosažitelnosti**
- tvoří se z nějakého určeného vrcholu (kořen)
- ukazuje, jaká je nejkratší cesta do ostatních vrcholů
- reprezentován polem, kde na indexu vrcholu je uložen předek
- nemusí být jednoznačný (může existovat více nejkratších cest)
#### Prohledávánı́ do hloubky (DFS)
- Depth-First Search
- algoritmus postupuje do většı́ vzdálenosti od počátečnı́ho vrcholu, pokud může
+ předpokládáme, že označenı́ (mark) je před volánı́m DFS inicializováno na 0 pro všechny vrcholy
+ DFS je potřeba doplnit o nějaký užitečný kód
+ záleží to na řešeném problému
**Značenı́ vrcholů**
- nezpracovaný (”bı́lá”), kód 0
- rozpracovaný (”šedá”), kód 1
- dokončený (”černá”), kód 2
**Složitost**
- rekurzivní metoda se pro každý vrchol volá pouze jednou - $\Omega(\vert V\vert)$
- pro každý vrchol se prochází seznam hran:
- reprezentace maticí - $\Omega(\vert V\vert^2)$
- reprezentace seznamem - $\mathcal{O}(\vert E\vert)$
- celkem: $\mathcal{O}(\vert V\vert + \vert E\vert)$ při reprezentaci seznamem
- může být i $\Omega(\vert V\vert^2)$, pokud $\vert E\vert = k\vert V\vert^2$
**Použití DFS**
- Zjištění dosažitelnosti vrcholu
- pokud předpokládáme, že bude vrchol daleko, je DFS vhodnější než BFS
+ Zjištění cyklu v grafu
+ vrchol označíme jedničkou a poté ho znovu hledáme
+ Topologické řazení
+ prvně je potřeba ověřit, že graf nemá cykly
+ vrcholy jsou činnosti, hrany jsou závislosti
+ hrana $A \to B$ značí, že se prvně musí vykonat A a potom až B
+ pomocí DFS můžeme snadno určit pořadí činností (pomocí otočeného grafu)
**DFS bez rekurze**
- pravděpodobně nastanou problémy s hloubkou zásobníku
+ vystačíme si se zásobníkem celých čísel (vrcholů)
+ `segment` (jaký je stav vrcholu) je v označení vrcholu (`mark`)
**Nejkratší cesta v ohodnoceném grafu**
- velmi častý problém
- ohodnocení: čas, vzdálenost, ...
- úkol: nalézt nejkratší vzdálenost ke všem vrcholům
- **Dijkstrův algoritmus**
- je potřeba prioritní fronta
- přidání dvojice vrchol + ohodnocení
- vybrání/odebrání vrcholu s nejmenším ohodnocením
- změna ohodnocení vrcholu