V případě jazyka Java bychom abstraktní datový typ Time reprezentující čas mohli definovat pomocí třídy takto:



Podobné dokumenty
Časová složitost algoritmů

Časová složitost algoritmů, řazení a vyhledávání

Datové struktury. alg12 1

Algoritmy I, složitost

Datové struktury. Obsah přednášky: Definice pojmů. Abstraktní datové typy a jejich implementace. Algoritmizace (Y36ALG), Šumperk - 12.

Základní datové struktury

Abstraktní datové typy: zásobník

Algoritmizace prostorových úloh

Algoritmizace prostorových úloh

IAJCE Přednáška č. 9. int[] pole = new int[pocet] int max = pole[0]; int id; for(int i =1; i< pole.length; i++) { // nikoli 0 if (Pole[i] > max) {

Algoritmizace prostorových úloh

ABSTRAKTNÍ DATOVÉ TYPY (ADT)

Maturitní téma: Programovací jazyk JAVA

Dynamické datové struktury I.

DobSort. Úvod do programování. DobSort Implementace 1/3. DobSort Implementace 2/3. DobSort - Příklad. DobSort Implementace 3/3

Da D to t v o é v ty t py IB111: Datové typy

Náplň. v Jednoduché příklady na práci s poli v C - Vlastnosti třídění - Způsoby (algoritmy) třídění

Dynamické datové struktury III.

Základní pojmy. Úvod do programování. Základní pojmy. Zápis algoritmu. Výraz. Základní pojmy

součet cvičení celkem. známka. Úloha č.: max. bodů: skut. bodů:

5 Rekurze a zásobník. Rekurzivní volání metody

Dynamické datové struktury IV.

Amortizovaná složitost. Prioritní fronty, haldy (binární, d- regulární, binomiální, Fibonacciho), operace nad nimi a jejich složitost

Abstraktní datové typy

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Konstruktory a destruktory

Algoritmizace řazení Bubble Sort

Prioritní fronta, halda (heap), řazení

Algoritmy a datové struktury

Stromy, haldy, prioritní fronty

Šablony, kontejnery a iterátory

Zdůvodněte, proč funkce n lg(n) roste alespoň stejně rychle nebo rychleji než než funkce lg(n!). Symbolem lg značíme logaritmus o základu 2.

Algoritmizace a programování

Lineární datové struktury

Stromy. Strom: souvislý graf bez kružnic využití: počítačová grafika seznam objektů efektivní vyhledávání výpočetní stromy rozhodovací stromy

int t1, t2, t3, t4, t5, t6, t7, prumer; t1=sys.readint();... t7=sys.readint(); prume pru r = r = ( 1+t 1+t t3+ t3+ t4 t5+ t5+ +t7 +t7 )/ ;

Spojové struktury. Spojová struktura (linked structure):

Datový typ prioritní fronta Semestrální práce z předmětu 36PT

ABSTRAKTNÍ DATOVÉ TYPY

Prioritní fronta, halda

ÚVODNÍ ZNALOSTI. datové struktury. správnost programů. analýza algoritmů

Programování v C++ 2, 4. cvičení

Fronta (Queue) Úvod do programování. Fronta implementace. Fronta implementace pomocí pole 1/4. Fronta implementace pomocí pole 3/4

Vyvažování a rotace v BVS, všude se předpokládá AVL strom

Základní datové struktury III: Stromy, haldy

IB111 Úvod do programování skrze Python

Zadání k 2. programovacímu testu

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Kolekce, cyklus foreach

přirozený algoritmus seřadí prvky 1,3,2,8,9,7 a prvky 4,5,6 nechává Metody řazení se dělí:

A4B33ALG 2010/05 ALG 07. Selection sort (Select sort) Insertion sort (Insert sort) Bubble sort deprecated. Quicksort.

Šablony, kontejnery a iterátory

ADT/ADS = abstraktní datové typy / struktury

Datové struktury 1: Základní datové struktury

Rozklad problému na podproblémy

Různé algoritmy mají různou složitost

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Rekurze a zásobník. Jak se vypočítá rekurzivní program? volání metody. vyšší adresy. main(){... fa(); //push ret1... } ret1

Lineární datové struktury

Test prvočíselnosti. Úkol: otestovat dané číslo N, zda je prvočíslem

1. Převeďte dané číslo do dvojkové, osmičkové a šestnáctkové soustavy: a) b)

Seznamy a iterátory. Kolekce obecně. Rozhraní kolekce. Procházení kolekcí

Základy řazení. Karel Richta a kol.

Bubble sort. příklad. Shaker sort

2 Datové struktury. Pole Seznam Zásobník Fronty FIFO Haldy a prioritní fronty Stromy Hash tabulky Slovníky

ALG 14. Vícedimenzionální data. Řazení vícedimenzionálních dat. Experimentální porovnání řadících algoritmů na vícedimenzionálních datech

Základy algoritmizace a programování

Cílem kapitoly je seznámit studenta se seznamem a stromem. Jejich konstrukci, užití a základní vlastnosti.

Stromy. Příklady. Rekurzivní datové struktury. Základní pojmy

bin arn ı vyhled av an ı a bst Karel Hor ak, Petr Ryˇsav y 23. bˇrezna 2016 Katedra poˇ c ıtaˇ c u, FEL, ˇ CVUT

Algoritmy vyhledávání a řazení. Zatím nad lineární datovou strukturou (polem)

Složitost algoritmů. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta a kol.

Složitosti základních operací B + stromu

Reprezentace aritmetického výrazu - binární strom reprezentující aritmetický výraz

boolean hasnext() Object next() void remove() Kolekce

Dynamicky vázané metody. Pozdní vazba, virtuální metody

NPRG030 Programování I, 2018/19 1 / :03:07

5. Vyhledávání a řazení 1

Základy algoritmizace c2005, 2007 Michal Krátký, Jiří Dvorský1/39

3 Algoritmy řazení. prvku a 1 je rovněž seřazená.

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

AVL stromy. pro každý uzel u stromu platí, že rozdíl mezi výškou jeho levého a pravého podstromu je nejvýše 1 stromy jsou samovyvažující

Dynamické programování

TÉMATICKÝ OKRUH TZD, DIS a TIS

Rekurzivní algoritmy

Obecná informatika. Matematicko-fyzikální fakulta Univerzity Karlovy v Praze. Podzim 2012

Dynamické struktury a Abstraktní Datový Typy (ADT)

Část 1 Spojové struktury (stromy) Dynamické struktury a Abstraktní Datový Typy (ADT) Část 2 Abstraktní datový typ. Část 3 Příklad ADT Prioritní fronta

ALG 04. Zásobník Fronta Operace Enqueue, Dequeue, Front, Empty... Cyklická implementace fronty. Průchod stromem do šířky

Adresní vyhledávání (přímý přístup, zřetězené a otevřené rozptylování, rozptylovací funkce)

Pole a kolekce. v C#, Javě a C++

Řazení. Uspořádat množinu prvků obsahujících klíč podle definovaného kriteria.

Grafy. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 13.

Další příklady. Katedra softwarového inženýrství. Katedra teoretické informatiky, Fakulta informačních technologii, ČVUT v Praze. Karel Müller, 2011

Paměť počítače. alg2 1

Programování v C++, 2. cvičení

Michal Krátký. Úvod do programování. Cíl kurzu. Podmínky získání zápočtu III/III

Binární soubory (datové, typované)

bfs, dfs, fronta, zásobník, prioritní fronta, halda

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Transkript:

20. Programovací techniky: Abstraktní datový typ, jeho specifikace a implementace. Datový typ zásobník, fronta, tabulka, strom, seznam. Základní algoritmy řazení a vyhledávání. Složitost algoritmů. Abstraktní datový typ Pokud nabízíme omezené rozhraní datového typu pomocí specifikované sady operací, nazýváme takový typ abstraktní datový typ (ADT). Vzhledem k tomu, že odpovídající konkrétní datový typ pak není uživateli dostupný a jedinou možností jak s hodnotami pracovat je právě prostřednictvím nabízených operací, získáme na tento typ mnohem abstraktnější pohled. Další výhodou použití abstraktních datových typů, a tedy především oddělení rozhraní datového typu od jeho implementace, je to, že můžeme vytvořit různé implementace téhož datového typu, aniž se tato změna projeví při jeho používání. Pro vytváření různých implementací můžeme mít více důvodů, např.: Implementace se mohou od sebe lišit efektivitou jednotlivých operací; uživatel si zvolí tu implementaci, která je z hlediska efektivity v dané situaci nejvýhodnější. Může jít o různé vývojové verze téže implementace. Může jít o implementace poskytnuté různými autory. V prostředí objektově orientovaných programovacích jazyků jsou pro definici abstraktních datových typů k dispozici třídy a v některých jazycích máme dokonce přímo konstrukce, umožňující definovat abstraktní rozhraní. V případě jazyka Java bychom abstraktní datový typ Time reprezentující čas mohli definovat pomocí třídy takto: public class Time { public Time(int hours, int minutes) { this.hours = hours; this.minutes = minutes; public int gethours() { return hours; public int getminutes() { return minutes; public void increment(int delta) { int newmins = minutes + delta; minutes = newmins % 60; hours += newmins / 60; private int hours; private int minutes; Povšimněte si, že třída Time obsahuje ve svých veřejných členech pouze metody gethours(), getminutes() a increment(). Konstruktor createtime() je nahrazen skutečným konstruktorem a samotná hodnota je vnitřně reprezentovaná dvojicí proměnných hours a minutes pro počet hodin a minut. Rozdíl ve vnitřní reprezentaci proti předchozí ukázce se navenek nijak viditelně neprojeví, neboť uživatel pracuje pouze s veřejnými metodami objektu.

Abstraktní datové typy můžeme použít pro nejrůznější účely a jejich struktura se bude lišit podle konkrétní aplikace. Ovšem při vytváření programů se s některými abstrakcemi setkáváme relativně často - například s různými kolekcemi hodnot, grafovými strukturami nebo vyhledávacími tabulkami. V následujících částech této kapitoly se budeme těmito často se vyskytujícími abstrakcemi zabývat podrobněji. Ukážeme si, jakým způsobem je můžeme implementovat, případně jak můžeme využít nabízené standardní knihovny, které jejich podporu již často obsahují. K základním abstraktním datovým typům můžeme zařadit následující konstrukce (v závorkách jsou uvedeny i obvykle používané anglické termíny): zásobník (stack), fronta (queue), seznam (list), množina (set), strom (tree), zobrazení (map). Datové struktury Zásobník ( Stack ) Je to dynamická datová struktura, umožňující vkládání a odebírání hodnot, přičemž naposledy vložená hodnota se odebere jako první Je to paměť typu LIFO (zkratka z angl. Last-In First-Out, poslední dovnitř, první ven) vložení hodnoty na vrchol odebrání hodnoty z vrcholu Základní operace: vložení hodnoty na vrchol zásobníku odebrání hodnoty z vrcholu zásobníku test na prázdnost zásobníku Příklad třídy realizující zásobník znaků: class ZasobnikZnaku { public ZasobnikZnaku() {... public void vloz(char z) {... public char odeber() {... public boolean jeprazdny() {...... Poznámky

v angličtině se operace nad zásobníkem obvykle jmenují push, pop a isempty pro zásobník může být definována ještě operace čtení hodnoty z vrcholu zásobníku bez jejího odebrání ( v angl. top či peek ) Implementace zásobníku polem public void vloz(char z) { if ( vrchol==max_index ) throw new RuntimeException( "plný zásobník" ); pole[++vrchol] = z; public char odeber() { if ( vrchol<0 ) throw new RuntimeException( "prázdný zásobník" ); return pole[vrchol--]; public boolean jeprazdny() { return vrchol<0; Implementace zásobníku spojovým seznamem class Prvek { char hodn; Prvek dalsi; public Prvek(char h, Prvek p) { hodn = h; dalsi = p; class ZasobnikZnaku { private Prvek vrchol; public ZasobnikZnaku() { vrchol = null; public void vloz(char z) { vrchol = new Prvek(z, vrchol); public char odeber() { if (vrchol==null) throw new RuntimeException( "prazdny zasobnik" ); char vysl = vrchol.hodn; vrchol = vrchol.dalsi; return vysl; public boolean jeprazdny() { return vrchol==null; Fronta ( Queue ) Je to datová struktura v níž se hodnoty odebírají v tom pořadí, v jakém byly vloženy Je to paměť typu FIFO ( zkratka z angl. First-In First-Out, první dovnitř, první ven )

Implementace fronty: pomocí pole pomocí spojového seznamu Implementace fronty spojovým seznamem class PrvekFronty { PrvekFronty dalsi; int hodn; public class FrontaCisel { private PrvekFronty celo; private PrvekFronty volny; public FrontaCisel() { celo = new PrvekFronty(); volny = celo; public void vloz(int x) { volny.hodn = x; volny.dalsi = new PrvekFronty(); volny = volny.dalsi; public int odeber() { if (jeprazdna()) throw new RuntimeException( " prazdna fronta" ); int vysl = celo.hodn; celo = celo.dalsi; return vysl; public boolean jeprazdna() { return celo == volny; Stromy Lineární spojová struktura ( spojový seznam ) každý prvek má nanejvýš jednoho následníka Nelineární spojová struktura ( strom ): každý prvek může mít více následníků Binární strom: každý prvek ( uzel ) má nanejvýš dva následníky Některé pojmy: kořen stromu, levý podstrom, pravý podstrom, list

Pole jako tabulka Pole lze použít též pro realizaci tabulky (zobrazení), která hodnotám typu indexu (v jazyku Java to je pouze interval celých čísel počínaje nulou) přiřazuje hodnoty nějakého typu Příklad: přečíst řadu čísel zakončených nulou a vypsat tabulku četnosti čísel od 1do 100 (ostatní čísla ignorovat). Tabulka četnosti bude pole 100 prvků typu int, počet výskytů čísla x, kde 1 x 100, bude hodnotou prvku s indexem x-1 Seznam - obsahuje prvky uspořádané. Seznam (list) je datová struktura, která umožňuje kromě základních operací nad obecnou kolekcí také přistupovat k libovolnému prvku na základě pořadového čísla (indexu) a vkládat a rušit prvky na libovolné pozici. Představuje vlastně nejobecnější kolekci, pomocí které můžeme implementovat kolekce další včetně zásobníku a fronty. Z tohoto důvodu jsou v mnoha programovacích jazycích seznamy k dispozici jako součást standardních knihoven (Java, C++, C#). Více na http://www.cs.vsb.cz/benes/vyuka/pte/texty/adt/ch01s04.html Časová složitost algoritmů Důležitou vlastností algoritmu je časová náročnost výpočtů provedené podle daného algoritmu Ta se nezískává měřením doby výpočtu pro různá data, ale analýzou algoritmu, jejímž výsledkem je časová složitost algoritmu Časová složitost algoritmu vyjadřuje závislost času potřebného pro provedení výpočtu na rozsahu ( velikosti ) vstupních dat Čas se však neměří sekundách, ale počtem provedených operací, přičemž trvání každé operace se chápe jako bezrozměrná jednotka Příklad: součet prvků pole static int soucet(int[] pole) { int s = 0; for (int i=0; i<pole.length; i++ ) s = s + pole[i] ; return s; Považujme za operace podtržené konstrukce, pak časová složitost je: C(n) = 2 + (n+1) + n + n = 3 + 3n kde n je počet prvků pole. Doba výpočtu obvykle nezávisí jen na rozsahu vstupních dat, ale též na konkrétních hodnotách Obecně proto rozlišujeme časovou složitost v nejlepším, nejhorším a průměrném případě Příklad: sekvenční hledání prvku pole s danou hodnotou static int hledej(int[] pole, int x) { for (int i=0; i<pole.length; i++ ) if ( x==pole[i] ) return i; return -1; Analýza: nejlepší případ: první prvek má hodnotu x Cmin(n) = 3 nejhorší případ: žádný prvek nemá hodnotu x Cmax(n) = 1 + (n+1) + n + n = 2 + 3n

průměrný případ Cprum(n) = 2.5 + 1.5n Přesné určení počtu operací při analýze složitosti algoritmu bývá velmi složité Zvlášť komplikované, ba i nemožné, bývá určení počtu operací v průměrném případě; proto se většinou omezujeme jen na analýzu nejhoršího případu Zpravidla nás nezajímají konkrétní počty operací pro různé rozsahy vstupních dat n, ale tendence jejich růstu při zvětšujícím se n Pro tento účel lze výrazy udávající složitost zjednodušit: stačí uvažovat pouze složky s nejvyšším řádem růstu a i u nich lze zanedbat multiplikativní konstanty Příklad: řád růstu časové složitosti předchozích algoritmů je n ( časová složitost je lineární ) Časovou složitost vyjadřujeme pomocí tzv. asymptotické notace: O dvou funkcích f a g definovaných na množině přirozených čísel a s nezáporným oborem hodnot říkáme, že f roste řádově nejvýš tak rychle, jako g a píšeme f(n) = O(g(n)) pokud existují přirozená čísla K a n1 tak, že platí f(n) K.g(n) pro všechna n > n1 Algoritmy řazení pole Algoritmy řazení pole jsou algoritmy, které přeskupí prvky pole tak, aby upravené pole bylo seřazené Pole p je vzestupně seřazené, jestliže platí p[i-1] <= p[i] pro i = 1 počet prvků pole 1 Pole p je sestupně seřazené, jestliže platí p[i-1] >= p[i] pro i = 1 počet prvků pole 1 Principy některých algoritmů řazení ukažme na řazení pole prvků typu int Následující metody vzestupně řadí všechny prvky pole daného parametrem: bubblesort( ) selectsort( ) insertsort( ) mergesort( ) Řazení zaměňováním ( Bubble Sort ) Při řazení zaměňováním postupně porovnáváme sousední prvky a pokud jejich hodnoty nejsou v požadované relaci, vyměníme je; to je třeba provést několikrát Hrubé řešení: for ( n=a.length-1; n>0; n-- ) for ( i=0; i<n; i++ ) if ( a[i]>a[i+1] ) vyměň a[i] a a[i+1] Podrobné řešení : static void bubblesort(int[] a) { int pom, n, i; for ( n=a.length-1; n>0; n-- ) for ( i=0; i<n; i++ ) if ( a[i]>a[i+1] ) { pom = a[i]; a[i] = a[i+1]; a[i+1] = pom; Časová složitost je O( n2 )

Řazení výběrem ( Select Sort ) Při řazení výběrem se opakovaně hledá nejmenší prvek Hrubé řešení: for (i=0; i<a.length-1; i++) { "najdi nejmenší prvek mezi a[i] až a[a.length-1] ; "vyměň hodnotu nalezeného prvku s a[i] ; Podrobné řešení: public static void selectsort(int[] a) { int i, j, imin, pom; for (i=0; i<a.length-1; i++) { imin = i; for (j=i+1; j<a.length; j++) if (a[j]<a[imin]) imin = j; if (imin!=i) { pom = a[imin]; a[imin] = a[i]; a[i] = pom; Časová složitost algoritmu SelectSort: O(n2) Řazení vkládáním ( Insert Sort ) Pole lze seřadit opakovaným vkládání prvku do seřazeného úseku pole Hrubé řešení: for (n=1; n<a.length; n++) { úsek pole od a[0] do a[n-1] je seřazen vlož do tohoto úseku délky n hodnotu a[n] Podrobné řešení: private static void vloz(int[] a, int n, int x) { int i; for (i=n-1; i>=0 && a[i]>x; i--) a[i+1]=a[i]; // odsun a[i+1] = x; // vlozeni public static void insertsort(int[] a) { for (int n=1; n<a.length ; n++) vloz(a, n, a[n]); Časová složitost algoritmu InsertSort: O(n2) Řazení slučováním ( Merge Sort ) Problém slučování lze obecně formulovat takto: ze dvou seřazených ( monotonních ) posloupností a a b máme vytvořit novou posloupnost obsahující všechny prvky z a i b, která je rovněž seřazená Příklad: a: 2 3 6 8 10 34 b: 3 7 12 13 55 výsledek: 2 3 3 6 7 8 10 12 13 34 55 Princip slučování: postupně porovnáváme prvky zdrojových posloupností a do výsledné posloupnosti přesouváme menší z nich nakonec zkopírujeme do výsledné posloupnosti zbytek první nebo druhé posloupnosti Efektivnější algoritmy řazení mají časovou složitost O( n log a )

Jedním z nich je algoritmus řazení slučováním ( MergeSort ), který je založen na opakovaném slučování seřazených úseků do úseků větší délky Lze jej popsat rekurzívně: řazený úsek pole rozděl na dvě části seřaď levý úsek a pravý úsek přepiš řazený úsek pole sloučením levého a pravého úseku Rekurzivní funkce řazení úseku pole: private static void mergesort( int[] a, int[] pom, int prvni, int posl ) { if ( prvni<posl ) { int stred = (prvni+posl)/2; mergesort(a, pom, prvni, stred); mergesort(a, pom, stred+1, posl); merge(a, pom, prvni, stred+1, posl); for (int i=prvni; i<=posl; i++) a[i] = pom[i]; Výsledná funkce: public static void mergesort(int[] a) { int[] pom = new int[a.length]; mergesort(a, pom, 0, a.length-1); Rekurzívní MergeSort se volá rekurzívně tak dlouho, dokud délka úseku pole není 1; pak začne slučování sousedních úseků do dvakrát většího úseku v pomocném poli, který je třeba zkopírovat do původního pole Rozdělení pole na úseky, které se postupně slučují, je dáno postupným půlením úseků pole shora dolů. Nerekurzívní ( iterační ) algoritmus MersgeSort postupuje zdola nahoru: pole a se rozdělí na dvojice úseků délky 1, které se sloučí do seřazených úseků délky 2 v pomocném poli pom dvojice sousedních seřazených úseků délky 2 v poli pom se sloučí do seřazených úseků délky 4 v poli a dvojice sousedních úseků délky 4 v poli a se sloučí do seřazených úseků délky 8 v poli pom atd. tento postup se opakuje, pokud délka úseku je menší než velikost pole skončí-li slučování tak, že výsledek je v pomocném poli pom, je třeba jej zkopírovat do původního pole a Zdroje: [1]Algoritmizace přednášky - http://service.felk.cvut.cz/courses/x36alg/alg_prednasky/ [2]Abstraktní datové typy - http://www.cs.vsb.cz/benes/vyuka/pte/texty/adt/index.html