Texty k Programování na VŠFS Petr Kučera 29. května 2006 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 1 / 117
Obsah Základní informace k předmětu Dynamicky alokovaná paměť Jednoduché dynamicky alokované datové struktury Stromy Grafy Vstup a výstup do souboru Rekurze Třídící algoritmy Petr Kučera () Texty k Programování na VŠFS 29. května 2006 2 / 117
Základní informace k předmětu e-mail: www: Zápočet: Zkouška: Literatura: kucerap@kti.ms.mff.cuni.cz http://kti.ms.mff.cuni.cz/ kucerap/vsfs/programovani Dostatečná účast (chybět nejvýš čtyři vyučovací hodiny), nebo zápočtový program (postup jako minulý semestr.) Praktická, naprogramování zadané úlohy nebo úloh. Töpferová Dana, Töpfer Pavel Sbírka úloh z programování, Grada 1992 Töpfer Pavel Algoritmy a programovací techniky, Prometheus 1995 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 3 / 117
Část I Dynamicky alokovaná paměť Petr Kučera () Texty k Programování na VŠFS 29. května 2006 4 / 117
Typ ukazatel Proměnná typu ukazatel na typ obsahuje adresu dat daného typu. Deklarace: var p:^typ; type typ ukazatele = ^typ; Například: var p: ^string[32]; type pstr32 = ^string[32]; type ppstr32 = ^pstr32; Porovnání ukazatelů: =, <> Hodnota nil, má-li ukazatel hodnotu nil, znamená to, že nikam neodkazuje. Vždy testovat. Dereferenční operátor ^ (ukazatel^), použití například: writeln (pˆ); Petr Kučera () Texty k Programování na VŠFS 29. května 2006 5 / 117
Dynamická alokace paměti new Někdy je užitečné říct si o paměť až za běhu programu, například, když na začátku nevíme, kolik jí budeme potřebovat, nebo chceme prostě šetřit pamětí. Alokace, procedura new (ukazatel). Například: new (p); Velikost paměti, která se alokuje, je daná typem ukazatele. Pokud se nepodaří paměť alokovat, program skončí s chybou. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 6 / 117
Uvolnění paměti dispose Dealokace, uvolnění paměti, procedura dispose (ukazatel). Například: dispose (p); Po dispose má ukazatel nedefinovanou hodnotu. Okamžitě přiřadit nil. Pokud p a q jsou ukazatelé ukazující na stejný objekt a zavoláme dispose (p);, hodnota q se nemění. Pokud je p=nil, dojde po dispose (p); k chybě a ukončení programu. Testovat! Pokud paměť neuvolníte a ztratíte na ni ukazatel, zůstane zabraná až do konce programu. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 7 / 117
Poznámky k paměti V pascalu je paměť běžícího programu organizována ve čtyřech segmentech: Code segment, v němž je uložen kód programu. Data segment, v němž jsou uložena staticky alokovaná data (tj. na začátku programu). Stack segment, v němž jsou uloženy aktivační záznamy při volání procedur a funkcí (tj. kam se vrátit po jejich ukončení, kam vrátit hodnotu apod.) a hlavně lokální proměnné v procedurách a funkcích. Heap segment, čili halda, zde se přiděluje dynamicky alokovaná paměť. Velikosti jednotlivých segmentů mohou být omezeny. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 8 / 117
Příklad program ukazatel (input, output); var p: ^string[32]; type pstring32 = ^string[32]; begin new (p); p^ := Ahoj světe! ; writeln (p^); dispose (p); p := nil; end. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 9 / 117
Lineární spojový seznam Lineární spojový seznam je posloupnost záznamů stejného typu seřazených za sebe a spojených pomocí ukazatelů. Každý uzel seznamu obsahuje nějaká data a odkaz na následníka. Data Data Data Data NIL Petr Kučera () Texty k Programování na VŠFS 29. května 2006 10 / 117
Lineární seznam v Pascalu Typ ukazatel na záznam R může být deklarován před deklarací typu R, ale v rámci jednoho bloku type. (Jinak by nešlo realizovat.) Deklarace typu spojového seznamu (data jsou například jedno číslo): type PLSeznam = ^TLSeznam; TLSeznam = record data : integer; dalsi : PLSeznam; end; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 11 / 117
Manipulace s lineárním spojovým seznamem (cvičení) I 1 function vytvor(n: integer): PLSeznam; Funkce vytvoří seznam s N prvky a vrátí odkaz na první prvek z tohoto seznamu (nil, pokud N <= 0). V položkách data budou uložena čísla 1... N vzestupně od začátku do konce. 2 function najdi(s: PLSeznam; d: integer): PLSeznam; Funkce najde prvek v seznamu, který má položku data shodnou s d a vrátí odkaz na něj, nebo nil, pokud prvek v seznamu není. 3 function najdipredchudce(s, p: PLSeznam): PLSeznam; Funkce najde předchůdce prvku p v seznamu s a vrátí na něj odkaz. Pokud se p v seznamu nevyskytuje, vrátí nil, je-li p první, vrátí p. 4 function konec(s: PLSeznam): PLSeznam; Vrátí odkaz na poslední prvek seznamu. 5 procedure vypis(s: PLSeznam); Vypíše na obrazovku čísla v pořadí, v jakém jsou v seznamu. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 12 / 117
Manipulace s lineárním spojovým seznamem (cvičení) II 1 procedure vlozzaprvek(p, q: PLSeznam); Vloží prvek q za prvek p. 2 procedure pridejnazacatek(var s: PLSeznam; p: PLSeznam); Vloží prvek p na začátek seznamu s. 3 procedure pridejnakonec(var s: PLSeznam; p: PLSeznam); Vloží prvek p na konec seznamu s. 4 function smaz(var s: PLSeznam; p: PLSeznam): PLSeznam; Smaže prvek p ze seznamu s. Buď si nejprve vymění data s následníkem, nebo nejprve najde předchůdce. Vrací odkaz na smazaný prvek, v položce dalsi bude mít nil. Pokud jej nebudete už používat, nezapomeňte po smaz na dispose. 5 procedure zrus(var s: PLSeznam); Projde seznam a pomocí dispose zruší jeho prvky, s bude po ukončení obsahovat nil. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 13 / 117
Část II Jednoduché dynamicky alokované datové struktury Petr Kučera () Texty k Programování na VŠFS 29. května 2006 14 / 117
Jednoduché datové struktury Lineární spojový seznam. Obousměrný spojový seznam Seznam s hlavou Petr Kučera () Texty k Programování na VŠFS 29. května 2006 15 / 117
Obousměrný spojový seznam Obousměrný spojový seznam je lineární seznam, v němž má každý prvek odkaz nejen na následníka, ale i na předchůdce. Data Data Data Data NIL NIL Petr Kučera () Texty k Programování na VŠFS 29. května 2006 16 / 117
Obousměrný seznam v Pascalu type POSeznam = ^TOSeznam; TOSeznam = record data : integer; dalsi : POSeznam; pred : POSeznam; end; Funkce a procedury přistupující k obousměrnému seznamu jsou implementované podobně jako pro lineární seznam. Něco je jednodušší, například vkládání, mazání, protože máme odkaz na předchůdce. Ale musíme hlídat i odkazy na předchůdce, proto je implementace poněkud pracnější. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 17 / 117
Seznam s hlavou Seznam libovolného typu si reprezentujeme hlavou, což je struktura, která teprve odkazuje na skutečný seznam. Hlava může obsahovat i jiné položky, například odkaz na konec, počet prvků a podobně. Příklad čtyřprvkového lineárního seznamu s hlavou: 4 Hlava Data Data Data Data NIL Petr Kučera () Texty k Programování na VŠFS 29. května 2006 18 / 117
Seznam s hlavou v Pascalu Příklad pro lineární seznam s hlavou s odkazy na začátek a počtem prvků: type THLSeznam = record pocet : integer; zac : PLSeznam; kon : PLSeznam; end; Manipulace je shodná s manipulací s lineárním seznamem, ale musíme upravovat počet a oba odkazy v hlavě. Hlava může být alokovaná staticky. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 19 / 117
Zásobník Datová struktura řídící se pravidlem: Poslední dovnitř, první ven. (Anglicky Last In First Out, zkráceně LIFO.) Dno Vrchol Přistupujeme k němu obvykle pomocí těchto procedur a funkcí: top Vrací prvek na vrcholu zásobníku. push Uloží prvek na vrchol zásobníku. pop Smaže prvek z vrcholu zásobníku a vrátí jej. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 20 / 117
Zásobník pomocí pole Například zásobník celých čísel: const MAX_ZAS_VEL = 100; type TZasobnikA = record zasobnik : array [1..MAX_ZAS_VEL] of integer; vrchol : integer; end; procedure ZAInit (var z : TZasobnikA); {Inicializace} function ZATop (var z : TZasobnikA) : integer; procedure ZAPush (var z : TZasobnikA; cis : integer); function ZAPop (var z : TZasobnikA) : integer; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 21 / 117
Zásobník pomocí pole (poznámky) Položka vrchol vlastně vždy obsahuje počet prvků v zásobníku. Přístupové procedury a funkce pouze aktualizují hodnotu položky vrchol a přistupují jen k zasobnik [vrchol]. Pozor na přetečení nebo podtečení (nutno kontrolovat). ZATop zásobník nemění, předání odkazem použito, aby se nekopírovalo celé pole. Výhodou implementace pomocí pole je rychlost. Nevýhodou je, že musíme dopředu dobře odhadnout maximální velikost zásobníku a to, že zásobník zabírá stále stejně paměti, i když obsahuje jen jeden či dva prvky. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 22 / 117
Zásobník pomocí seznamu s hlavou Přidáváme i odebíráme ze začátku seznamu. Výhody a nevýhody opačně než u implementace pomocí pole. Opět zásobník celých čísel: type TZasobnikLS = record zac : PLSeznam; end; procedure ZLSInit (var z : TZasobnikLS); {Inicializace} function ZLSTop (var z : TZasobnikLS) : integer; procedure ZLSPush (var z : TZasobnikLS; cis : integer); function ZLSPop (var z : TZasobnikLS) : integer; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 23 / 117
Fronta Datová struktura řídící se pravidlem: První dovnitř, první ven. (Anglicky First In First Out, zkráceně FIFO.) Vstup Výstup Přistupujeme k němu obvykle pomocí těchto procedur a funkcí: first Vrací první prvek fronty. put Uloží prvek na konec fronty. get Smaže prvek ze začátku fronty a vrátí jej. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 24 / 117
Fronta pomocí pole Například fronta celých čísel: const MAX_FR_VEL = 100; type TFrontaA = record fronta : array [1..MAX_FR_VEL] of integer; zac, kon : integer; pocet : integer; end; procedure FAInit (var z : TFrontaA); {Inicializace} function FAFirst (var z : TFrontaA) : integer; procedure FAPut (var z : TFrontaA; cis : integer); function FAGet (var z : TFrontaA) : integer; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 25 / 117
Fronta pomocí pole (poznámky) Při posunu indexů zac a kon počítáme modulo MAX VELIKOST FRONTY, jako by bylo pole do kolečka. Vyhneme se tím přesunu prvků, kdybychom došli na konec pole. Abychom poznali přetečení a podtečení, musíme si pamatovat počet prvků (a kontrolovat jej). Výhody a nevýhody jako u zásobníku pomocí pole. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 26 / 117
Fronta pomocí seznamu s hlavou Přidáváme na konec seznamu, odebíráme ze začátku. Výhody a nevýhody jako u zásobníku pomocí seznamu. Fronta celých čísel: type TFrontaLS = record zac, kon : PLSeznam; end; procedure FLSInit (var z : TFrontaLS); {Inicializace} function FLSTop (var z : TFrontaLS) : integer; procedure FLSPush (var z : TFrontaLS; cis : integer); function FLSPop (var z : TFrontaLS) : integer; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 27 / 117
Varianty fronty Oboustraná fronta Přidávání i odebírání z obou konců, implementace nejsnáze pomocí obousměrně vázaného seznamu. Prioritní fronta Prvky jsou odebírány podle priorit (pokud mají stejnou prioritu, bere se jako normální fronta). Pokud je různých priorit málo, třeba d, můžeme implementovat pomocí d různých seznamů nebo polí. Pokud je priorit hodně, bývá implementace pomocí pole nebo seznamu pomalá (kvůli třídění), lepší je halda (o té možná jindy). Petr Kučera () Texty k Programování na VŠFS 29. května 2006 28 / 117
Cvičení 1 Implementujte zásobník celých čísel pomocí pole i dynamicky pomocí seznamu. 2 Implementujte frontu celých čísel pomocí pole i dynamicky pomocí seznamu. 3 Implementujte prioritní frontu celých čísel, která upřednostňuje sudá čísla před lichými. 4 Implementujte oboustranou frontu. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 29 / 117
Část III Stromy Petr Kučera () Texty k Programování na VŠFS 29. května 2006 30 / 117
Stromy 6 Co je to strom 7 Reprezentace stromu 8 Binární vyhledávací stromy 9 Cvičení Petr Kučera () Texty k Programování na VŠFS 29. května 2006 31 / 117
Co je to strom... Formálně: Acyklický graf. Neformálně: Struktura skládající se z vrcholů (uzlů). Jeden je význačný, nazývá se kořen, ten reprezentuje přístup ke stromu. Kořen má několik následníků neboli synů. Každý z nich je kořenem menšího stromu, tj. má další syny atd. V binárním stromě má každý vrchol nejvýš dva syny (levého a pravého). Strom Binární strom Petr Kučera () Texty k Programování na VŠFS 29. května 2006 32 / 117
Reprezentace binárního stromu S vrcholem mohou být asociována data (v příkladu celé číslo). Ve vrcholu se občas hodí mít kromě odkazu na syny i odkaz na otce (v příkladu chybí). type PBinVrchol = ^TBinVrchol; TBinVrchol = record levy, pravy : PBinVrchol; data : integer; end; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 33 / 117
Reprezentace obecného stromu I Je-li maximální počet synů omezený a ne příliš velký, můžeme odkazy na ně uchovávat v poli. const MAX_POCET_SYNU = 20; type PVrchol = ^TVrchol; TVrchol = record syn : array [1..MAX_POCET_SYNU] of PVrchol; data : integer; end; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 34 / 117
Reprezentace obecného stromu II Jinou možností je uchovávat odkazy na syny ve spojovém seznamu. type PSeznamSynu = ^TSeznamSynu; PVrchol = ^TVrchol; TSeznamSynu = record syn : PVrchol; dalsi : PSeznamSynu; end; TVrchol = record syn : PSeznamSynu; data : integer; end; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 35 / 117
Reprezentace obecného stromu III Dalším způsobem je pamatovat si jen odkaz na prvního syna a svého dalšího bratra. Chceme-li se dozvědět všechny syny, přejdeme do prvního a projdeme postupně všechny jeho bratry. Obecný strom jsme vlastně nahradili binárním. type PVrchol = ^TVrchol; TVrchol = record syn, bratr : PVrchol; data : integer; end; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 36 / 117
Binární vyhledávací strom (BVS) je binární strom, v němž navíc asociujeme s každým vrcholem tzv. klíč (například číslo). Navíc pro každý vrchol v platí, že všechny prvky v podstromu levého syna (není-li nil) mají klíč menší než klíč v a všechny prvky v podstromu pravého syna (není-li nil) mají klíč větší než klíč v. 50 23 51 6 43 67 1 11 27 62 89 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 37 / 117
Reprezentace BVS Binární strom, liší se jen položkou klíč. Položka data pro jednoduchost vynechána. type PBVSVrchol = ^TBVSVrchol; TBVSVrchol = record levy, pravy : PBVSVrchol; klic : integer; end; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 38 / 117
Vyhledávání v BVS function najdi (PBVSVrchol : koren; x : integer):pbvsvrchol Hledáme prvek s klíčem x. Začínáme v kořeni Porovnáme hodnotu x s klíčem aktuálního vrcholu, je-li x stejný jako klíč, končíme, je-li x větší, změníme aktuální klíč na pravého syna, je-li x menší, změníme aktuální klíč na levého syna. x=43 50 23 51 6 43 67 1 11 27 62 89 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 39 / 117
Vkládání do BVS procedure vloz (var PBVSVrchol : koren; x : integer) Nejprve postupujeme stejně jako ve funkci najdi, dokud nenarazíme na nil. (Pokud prvek najdeme, tak ohlásíme chybu.) Tím jsme našli místo, kde by vrchol byl, kdyby byl ve stromě. Vytvoříme nový vrchol se zadaným klíčem a přidáme jej na nalezené místo. x=46 50 23 51 6 43 67 1 11 46 27 62 89 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 40 / 117
Mazání z BVS procedure smaz (var PBVSVrchol : koren; x : integer) Nejprve stejně jako ve funkci najdi nenajdeme prvek v, při hledání v najdeme rovnou i jeho otce o. Nemá-li vrchol v syna, prostě v, s využitím odkazu na o, odstraníme. Má-li vrchol v jediného syna s, smažeme v a s připojíme jako syna o (toho, kterým byl v). Pokud má vrchol v dva syny,... viz následující slide. x=46 50 x=43 50 23 51 23 o 51 6 43 o 67 6 v 43 67 1 11 46 27 v 62 89 1 11 s 27 62 89 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 41 / 117
Mazání z BVS (dokončení) Má-li v dva syny l a r, najdeme nejbližší menší prvek w, což je největší prvek z levého podstromu. Ten najdeme tak, že jdeme z l stále doprava, všimněte si, že w nikdy nemá pravého syna. Vyměníme datové a klíčové položky w a v. Poté w smažeme tak, jak bylo popsáno. Analogicky lze použít nejbližší větší prvek. x=23 v 23 6 l r 43 o 50 x=23 50 o 51 67 11 v 6 l r 43 51 67 1 46 11 27 62 89 w 1 23 w 46 27 62 89 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 42 / 117
Cvičení Implementujte funkce a procedury pro vyhledávání, vkládání a mazání v binárním vyhledávacím stromě. Implementujte funkce, které najdou největší a nejmenší prvek z binárního vyhledávacího stromu. Implementujte funkci, která vypíše prvky uložené v binárním vyhledávacím stromě od nejmenšího do největšího, nepoužívejte přitom rekurzi, o které si řekneme později. Jako nápověda může sloužit, že se zde využije zásobník. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 43 / 117
Část IV Grafy Petr Kučera () Texty k Programování na VŠFS 29. května 2006 44 / 117
Grafy 10 Co je to graf 11 Reprezentace grafu 12 Průchod stromu a grafu 13 Průchod stromu do hloubky 14 Průchod stromu do šířky 15 Průchod grafu do hloubky 16 Průchod grafu do šířky 17 Cvičení Petr Kučera () Texty k Programování na VŠFS 29. května 2006 45 / 117
Co je to graf... Graf je struktura skládající se z vrcholů a hran mezi těmito vrcholy. (Například silniční síť.) Orientovaný graf hrany jsou orientované, tj. u každé je dáno, že vede z jednoho vrcholu do jiného a ne naopak. Neorientovaný graf hrany nejsou orientované, tj. každá vede jakoby oběma směry. Sousedi či následníci vrcholy jsou ty vrcholy, do nichž z něj vede hrana. Cesta je posloupnost vrcholů v 1,..., v k, že mezi dvěma následujícími vždy vede hrana a žádný vrchol se neopakuje. Cyklus je cesta, která začíná i končí ve stejném vrcholu. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 46 / 117
Co je to graf... Neorientovaný graf Orientovaný graf b f d h b f d h c c a g e a g e Cesta a, b, c, d, e Petr Kučera () Texty k Programování na VŠFS 29. května 2006 47 / 117
Matice sousednosti Matice sousednosti grafu s n vrcholy je matice A velikosti n n booleovských hodnot. b Deklarace: var A: array [1..N, 1..N] of boolean; A[i, j] = true, právě když je v grafu hrana z vrcholu i do vrcholu j. a f c g d e h a b c d e f g h a 0 1 0 0 0 0 1 0 b 1 0 1 0 0 1 0 0 c 0 0 0 1 0 0 1 0 d 0 0 0 0 1 0 0 0 e 0 0 0 0 0 0 0 0 f 0 0 0 0 0 1 0 1 g 0 0 0 0 0 0 0 1 h 0 0 0 0 0 0 0 0 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 48 / 117
Matice incidence b Matice incidence grafu s n vrcholy a m hranami je matice I velikosti m n booleovských hodnot. a Deklarace: var I: array [1..M, 1..N] of boolean; I [i, j] = true, právě když vrchol j je prvkem hrany i. f c g d e h 1 2 3 4 5 6 7 8 9 10 a 1 1 0 0 0 0 0 0 0 0 b 1 0 1 1 0 0 0 0 0 0 c 0 0 1 0 1 1 1 0 0 0 d 0 0 0 0 1 0 0 1 0 0 e 0 0 0 0 0 0 0 1 0 0 f 0 0 0 1 0 1 0 0 1 0 g 0 1 0 0 0 0 1 0 0 1 h 0 0 0 0 0 0 0 0 1 1 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 49 / 117
Seznam sousedů Seznam sousedů grafu s n vrcholy a m hranami se skládá z pole V délky n, v němž si ke každému vrcholu i pamatujeme odkaz na seznam vrcholů, do nichž z i vede hrana. Seznamy buď dynamicky pomocí lineárního spojového seznamu. Hodí se pokud se často mění struktura grafu. Nebo pomocí druhého pole E délky m. V [i] pak obsahuje index v poli E, na němž začíná seznam sousedů vrcholu i, seznam končí na V [i + 1] 1. Pro jednoduchost přidáme vrchol N + 1, abychom mohli k vrcholům přistupovat jednotným způsobem. Z téhož důvodu i M + 1 v jako rozsah v deklaraci V. Deklarace: var V: array [1..N+1] of 1..M+1; E: array [1..M] of 1..N; Petr Kučera () Texty k Programování na VŠFS 29. května 2006 50 / 117
Seznam sousedů příklad b f d h c a g e a b c d e f g h? V 1 3 6 8 9 9 11 12 12 1 2 3 4 5 6 7 8 9 10 11 E b g a c f d g e c h h Petr Kučera () Texty k Programování na VŠFS 29. května 2006 51 / 117
Průchod stromu nebo grafu Chceme navštívit všechny vrcholy a něco s nimi udělat. Navíc máme požadavky na pořadí, v jakém je navštívíme. Například chceme vypsat uzly binárního vyhledávacího stromu uspořádané podle klíčů. Nebo chceme v grafu pro daný počáteční vrchol spočítat délku nejkratší cesty (co do počtu hran) do ostatních vrcholů. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 52 / 117
Průchod stromu do hloubky Konkrétní úloha: Vypsat uzly binárního vyhledávacího stromu uspořádané podle klíčů. Postupujeme tak, že v daném vrcholu v nejprve vypíšeme všechny uzly v podstromu levého syna, potom vrchol v, potom všechny uzly v podstromu pravého syna. Uzly v obou podstromech zpracováváme analogicky. Realizace buď pomocí rekurze (později), nebo pomocí zásobníku. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 53 / 117
Průchod stromu do hloubky algoritmus 1 Z prázdný zásobník 2 v kořen stromu 3 zezasobniku false 4 Dokud není v nil, opakuj následující kroky: 1 Má-li v levého syna l a zezasobniku = false 1 push(z, v) 2 v l 3 zezasobniku false 4 pokračuj další smyčkou cyklu 2 vypiš v 3 Má-li v pravého syna r 1 v r 2 zezasobniku false 3 pokračuj další smyčkou cyklu 4 v pop(z) 5 zezasobniku true Petr Kučera () Texty k Programování na VŠFS 29. května 2006 54 / 117
Průchod stromu do hloubky varianty Podle pořadí zpracovávaných vrcholů rozeznáváme tři varianty průchodu do hloubky. preorder Zpracuj vrchol v, zpracuj levý podstrom vrcholu v, zpracuj pravý podstrom vrcholu v. inorder Zpracuj levý podstrom vrcholu v, zpracuj vrchol v, zpracuj pravý podstrom vrcholu v. postorder Zpracuj levý podstrom vrcholu v, zpracuj pravý podstrom vrcholu v, zpracuj vrchol v. Předešlý algoritmus zařídí výpis v pořadí inorder. Ostatní varianty lze provést snadnou modifikací, my si je ukážeme později v části o rekurzi. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 55 / 117
Průchod stromu do šířky Konkrétní úloha: Vypsat uzly binárního stromu v pořadí podle jejich hloubky. Realizace pomocí fronty (tady nejde rekurzi moc dobře použít). Petr Kučera () Texty k Programování na VŠFS 29. května 2006 56 / 117
Průchod stromu do šířky algoritmus 1 F prázdná fronta 2 put(f, kořen stromu) 3 Dokud není F prázdná, opakuj následující kroky: 1 v get(f ) 2 vypiš v 3 Má-li v levého syna l, put(f, l). 4 Má-li v pravého syna r, put(f, r). Petr Kučera () Texty k Programování na VŠFS 29. května 2006 57 / 117
Průchod grafu do hloubky Chceme navštívit každý vrchol grafu právě jednou a to tak, že jsme-li ve vrcholu v, který má následníky u 1,..., u k, chceme nejprve navštívit všechny vrcholy, do nichž vede cesta z u 1, potom všechny vrcholy, do nichž vede cesta z u 2 atd. Stejně jako při průchodu stromem, použijeme zásobník. Například chceme zjistit komponenty souvislosti grafu (komponenta souvislosti je množina vrcholů, které jsou všechny spojené cestami. Navíc už je maximální, nejde k ní tedy nic přidat.) Petr Kučera () Texty k Programování na VŠFS 29. května 2006 58 / 117
Průchod grafu do hloubky Tento algoritmus označí (nastaví K[i] na 1) všechny vrcholy, do nichž vede cesta z vrcholu s. N označuje počet vrcholů. 1 Z prázdný zásobník 2 push(z, počáteční vrchol s) 3 K: array [1..N] of integer 4 K[i] 0 pro i = 1... N. 5 K[s] 1 6 Dokud není Z prázdný, opakuj následující kroky: 1 v pop(z) 2 Pro každého následníka u vrcholu v, pro nějž K[u] = 0, proveď: 1 push(z, u). 2 K[u] 1 Petr Kučera () Texty k Programování na VŠFS 29. května 2006 59 / 117
Průchod grafu do hloubky poznámky Graf je vhodné reprezentovat pomocí seznamu následníků. Pokud chceme najít všechny komponenty, voláme tento algoritmus, dokud je pro nějaký vrchol K[v] = 0 s tím, že jako počáteční vrchol použijeme vrchol v. Číslo komponenty postupně zvyšujeme, v algoritmu bylo 1. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 60 / 117
Průchod grafu do šířky Chceme navštívit každý vrchol grafu právě jednou a to tak, že jsme-li ve vrcholu v, který má následníky u 1,..., u k, chceme nejprve navštívit vrcholy u 1,..., u k a pak teprve jejich následníky atd. Stejně jako při průchodu stromem, použijeme frontu. Například vycházíme z vrcholu s a chceme najít pro každý vrchol v délku nejkratší cesty (co do počtu hran) z s do v. Případně i tuto cestu. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 61 / 117
Průchod grafu do šířky algoritmus Tento algoritmus spočítá pro každý vrchol délku nejkratší cesty (počet hran) z vrcholu s. Délku uloží do pole D, předchůdce na této cestě do P. 1 F prázdná fronta 2 put(f, počáteční vrchol s) 3 D, P: array [1..N] of integer 4 D[i] N + 1, P[i] N + 1 pro i = 1... N. 5 D[s] 0 6 Dokud není F prázdná, opakuj následující kroky: 1 v get(f ) 2 Pro každého následníka u vrcholu v, pro nějž D[u] = N + 1, proveď: 1 put(f, u). 2 D[u] D[v] + 1 3 P[u] v Petr Kučera () Texty k Programování na VŠFS 29. května 2006 62 / 117
Průchod grafu do šířky poznámky Vhodnou reprezentací grafu je opět seznam sousedů. Cestu do vrcholu zrekonstruujeme pomocí předchůdců. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 63 / 117
Cvičení 1 Implementujte algoritmus pro vypsání uzlů binárního vyhledávacího stromu uspořádané podle klíče. 2 Implementujte průchod stromem do šířky. 3 Implementujte průchod grafem do hloubky a hledání komponent souvislosti grafu. 4 Implementujte průchod grafem do šířky a počítání nejkratších cest ze zadaného vrcholu (co do počtu hran). 5 Implementujte průchod stromem v pořadí preorder, inorder, postorder. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 64 / 117
Část V Vstup a výstup do souboru Petr Kučera () Texty k Programování na VŠFS 29. května 2006 65 / 117
Vstup a výstup do souboru 18 Typy souborů 19 Otevření a uzavření souboru 20 Pohyb v souboru 21 Textové soubory 22 Netextové soubory 23 Cvičení Petr Kučera () Texty k Programování na VŠFS 29. května 2006 66 / 117
Typy souborů Soubor s udaným typem, např.: var f1: file of integer; Soubor bez udaného typu, základní položkou je byte, např.: var f2: file; Textový soubor, čte se obvykle po znacích nebo po řádcích, např.: var f3: text; Standardní vstup, obvykle jde o vstup z klávesnice, je to textový soubor jen pro čtení, který je vždy otevřený po spuštění programu. Čte z něj pomocí funkcí read, readln a podobných, u nichž se nespecifikuje soubor parametrem. Standardní výstup, obvykle jde o výstup na obrazovku, je to textový soubor jen pro zápis, který je vždy otevřený po spuštění programu. Zapisuje se do něj pomocí funkcí write, writeln a podobných. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 67 / 117
Otevření a uzavření souboru Nejprve je nutné asociovat soubor se jménem: assign (f1, "cisla"); Poté je možné soubor otevřít: reset soubor musí existovat, textové soubory jsou otevřeny pouze pro čtení. rewrite pokud soubor existoval, smaže nejprve jeho obsah, pokud neexistoval, vytvoří jej. append otevírá textový soubor pro zápis, soubor musí existovat a zapisovaný text bude přidáván na konec souboru. Například: reset (f1); rewrite (f2); append (f3); Nakonec je slušné soubor zavřít. close (f1); Další manipulace se soubory a adresáři viz. nápověda a dokumentace. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 68 / 117
Pohyb v souboru V každý okamžik je program v souboru na určité pozici, tj. za nějakým znakem, či složkou daného typu. Funkce eof vrací true, pokud je tato pozice na konci souboru, false jinak. Pohyb je vykonáván jednak přirozeně čtením a zápisem, jednak pomocí dalších funkcí, které se liší podle typu souboru. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 69 / 117
Pohyb v textovém souboru U všech funkcí pro textové soubory, pokud není udán soubor, míní se standardní vstup případně výstup. eoln vrací true, pokud je pozice souboru na znaku konce řádku, jinak false. seekeof má stejný význam jako eof, ale ignoruje všechny mezery, tabelátory a znaky konce řádku. Změní pozici na první znak za těmito znaky a pak vrátí, co by vrátilo eof. seekeoln má stejný význam jako eoln, ale ignoruje všechny mezery a tabelátory. Změní pozici na první pozici za těmito znaky a pak vrátí, co by vrátilo eoln Petr Kučera () Texty k Programování na VŠFS 29. května 2006 70 / 117
Čtení z textového souboru Procedury read, readln. Prvním parametrem může být textový soubor, jinak se bere standardní vstup. Další parametry mohou být typu char, celočíselného, reálného nebo string, ze souboru se přečte příslušná hodnota (znak, reprezentace čísla nebo řetězec příslušné délky) a načte se do daného parametru. readln se liší jen tím, že nakonec přejde na nový řádek. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 71 / 117
Zápis do textového souboru Procedury write, writeln Prvním parametrem může být opět textový soubor, jinak se míní standardní výstup. Další parametry mohou být stejného typu jako u read. Parametry je možné zadat pomocí: hodnota [ : minšířka [ : početmíst]] Potom: minšířka specifikuje, na kolik pozic se má výstup zarovnat. početmíst specifikuje počet desetinných míst je za desetinnou tečkou (jen reálné parametry). writeln se liší jen tím, že na konec přidá znak konce řádku. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 72 / 117
Buffery Všechny operace se bufferují. Velikost bufferu je možné změnit pomocí settextbuf. flush vyprázdní buffer a přitom zapíše dosud nezapsané znaky na disk. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 73 / 117
Příklad práce s textovým souborem (začátek) program textsoub; var vstup: text; vystup: text; soucet: integer; scitanec: integer; begin assign (vstup, vstup.txt ); assign (vystup, vystup.txt ); reset (vstup); rewrite (vystup); Petr Kučera () Texty k Programování na VŠFS 29. května 2006 74 / 117
Příklad práce s textovým souborem (dokončení) while not seekeof (vstup) do begin soucet := 0; while not seekeoln (vstup) do begin read (vstup, scitanec); soucet := soucet + scitanec; end; writeln (vystup, soucet:10); readln (vstup); end; close (vstup); close (vystup); end. Petr Kučera () Texty k Programování na VŠFS 29. května 2006 75 / 117