Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Další abstraktní datové typy. Abstraktní datové typy (ADT) abstraktní datový typ –datová struktura + operace nad touto strukturou jsou většinou dynamické.

Podobné prezentace


Prezentace na téma: "Další abstraktní datové typy. Abstraktní datové typy (ADT) abstraktní datový typ –datová struktura + operace nad touto strukturou jsou většinou dynamické."— Transkript prezentace:

1 Další abstraktní datové typy

2 Abstraktní datové typy (ADT) abstraktní datový typ –datová struktura + operace nad touto strukturou jsou většinou dynamické –velikost (počet prvků) se dynamicky mění vkládání výběr prvků

3 ADT spojový seznam fronta zásobník množina strom obecný graf

4 Fronta datová struktura typu FIFO –First In - First Out prvky se odebírají v pořadí, jak byly vkládány

5 Možné implementace jako spojový seznam –„neomezená“ velikost fronty (omezená pouze velikostí dostupné paměti) –operace vložení prvku znamená vložení prvku na konec seznamu –operace výběr prvku je výběr z čela fronty polem pevné délky –fronta s omezenou velikostí –implementuje se jako tzv. kruhová fronta

6 Implementace pomocí spoj. seznamu fronta reprezentována ukazatelem na čelo fronty a poslední prvek ve frontě prvek fronty nese hodnotu a ukazatel na další prvek ve frontě

7 typedef struct TPrvek { int x; TPrvek *dalsi; } TPrvek; typedef struct { TPrvek *celo; TPrvek *konec; } TFronta;

8 35108 celo konec NULL vloz(3) vloz(5) konec vloz(10) konec vyjmi() – vrátí hodnotu z čela fronty - 3 vloz(8) konec

9 Operace nad frontou void init(TFronta *f), eventuálně TFronta *init() –inicializace, vytvoří prázdnou frontu int je_prazdna(TFronta *f) –testuje na prázdnou frontu void vloz(TFronta *f, int prvek) –vloží prvek na konec fronty int vyjmi(TFronta *f) –vyjme prvek z čela fronty void zrus(TFronta *f)

10 int vyjmi(TFronta *f) { int prvek; TPrvek *pom; if (f->celo == NULL) return -1; prvek = f->celo->x; pom = f->celo; f->celo = f->celo->dalsi; free(pom); return prvek; }

11 spoléhat se na návratovou hodnotu -1, pokud je fronta prázdná, není nejlepší řešení –nepoznám, zda byla vyjmuta -1 nebo byla prázdná fronta –lépe by bylo např. nastavovat chybovou proměnnou odstranění problému je ve správném používání knihovních funkcí –před každým voláním funkce vyjmi() aplikace otestuje, zda není fronta prázdná –např. while (!je_prazdna(&f))

12 Implementace pomocí pole pevné délky fronta je reprezentována polem a indexem čela a konce fronty fronta má pevnou délku a je implementována jako kruhová (modulo délka pole) je nutné implementovat ještě dotaz, zda je fronta plná

13 prázdná plná celo: konec: Fronta délky n = 3 vloz(3) vloz(5) 2 vyjmi() – vrátí hodnotu z čela fronty vloz(8) 8 0 vloz(10) 10 1

14 index celo ukazuje na prvek pole na čele fronty, který bude odebrán index konec ukazuje na prvek v poli, kam se zapíše nový prvek pokud se indexy celo a konec rovnají, je fronta buď plná nebo prázdná –podle rovnosti indexů není možné rozlišit stav fronty –musím tedy tyto stavy uchovávat zvlášť –pravidlo: pokud se po přidání prvku indexy rovnají, je fronta plná (obdobně: prázdná)

15 typedef struct { int *pole; int n; /* velikost pole */ int celo, konec; int plna, prazdna; } TFronta2; int init(TFronta2 *f, int vel) { f->prazdna = 1; f->plna = 0; f->n = vel; f->celo = f->konec = 0; f->pole = (int*)malloc(sizeof(int)*vel); if(f->pole!=NULL) return 1; return 0; }

16 int je_prazdna(TFronta2 *f) { return f->prazdna; } int vloz(TFronta2 *f, int prvek) { if (!f->plna) { f->prazdna = 0; f->pole[f->konec]=prvek; f->konec = (f->konec+1)% f->vel; if (f->konec == f->celo) f->plna=1; return 1; } return 0; }

17 void zrus(TFronta2 *f) { free(f->pole); } Poznámka: implementace pomocí pole, které by se zvětšovalo dynamicky při zaplnění fronty, by byla mírně komplikovaná. Proč?

18 Zásobník (Stack) datová struktura typu LIFO –Last In - First Out nejprve se vybírá prvek, který byl vložen na vrchol (top) zásobníku jako poslední operace: – PUSH (uložení hodnoty na vrchol zásobníku) – POP (odebrání hodnoty z vrcholu zásobníku)

19 PUSHPOP vrchol někdy bývá implementována také operace TOP –zjištění hodnoty na vrcholu zásobníku bez odebrání prvku

20 Možné implementace na principu spojového seznamu –„neomezená“ velikost zásobníku (omezená pouze velikostí dostupné paměti) –operace vložení prvku znamená vložení prvku na konec seznamu –operace výběr prvku je výběr z konce seznamu polem (pevné nebo proměnné délky) –kruhová implementace není potřebná

21 Implementace na principu spojového seznamu zásobník reprezentován ukazatelem na vrchol zásobníku prvek nese hodnotu a ukazatel na předchozí prvek v zásobníku

22 typedef struct TPrvek { int x; TPrvek *predch; } TPrvek; typedef struct { TPrvek *vrchol; } TZasob;

23 241 vrchol NULL vloz(2) vloz(4) vrchol vloz(1) vrchol vyjmi() – vyjme a vrátí hodnotu z vrcholu zásobníku - 1 vloz(1)

24 Operace nad zásobníkem void init(TZasob *z), eventuálně TZasob *init() –inicializace, vytvoří prázdný zásobník int je_prazdny(TZasob *z) –test na prázdný zásobník void push(TZasob *z, int prvek) –vloží prvek na vrchol zásobníku int pop(TZasob *z) –vyjme prvek z vrcholu zásobníku void zrus(TZasob *z)

25 TZasob* init() { TZasob *zas =(TZasob*)malloc(sizeof(TZasob)); zas->vrchol = NULL; return zas; } int pop(TZasob *z) { int prvek; TPrvek *pom; if (z->vrchol != NULL) { prvek = z->vrchol->x; pom = z->vrchol->predch; free(z->vrchol); z->vrchol = pom; return prvek; } return -1; }

26 Použití zásobníku ( v této implementaci): TZasob *zas; zas = init(); push(zas,3); push(zas,4); pop(zas); zrus(zas); free(zas);

27 Implementace pomocí pole obdobně jako fronta implementace je snazší –není třeba implementovat „kruhově“ –stačí index na vrchol zde je efektivní i implementace s dynamicky se měnící velikostí pole při přidávání prvku

28 Množina jazyk C neobsahuje datový typ množina Požadavek: implementovat množinu takovým způsobem, aby operace vložení a vyjmutí prvku, testu, zda je prvek v daném množině, měly operační složitost O(1) – konstantní prvek je v množině obsažen „pouze jednou“

29 běžně se množina reprezentuje bitovým polem prvek s indexem i je prvkem množiny, je-li bit nahozen (tzv. charakteristická funkce) tato reprezentace umožňuje snadnou implementaci operací: –vložení prvku nahozením bitu pomocí operace or –vyjmutí prvku nulováním bitu pomocí operace and –test na existenci prvku pomocí operace and –vytvoření doplňku pomocí operace negace –sjednocení pomocí operace or –atd.

30 protože je velikost paměti počítače konečná, musí být i univerzum konečné implementaci množiny si budeme demonstrovat na množině ASCII znaků

31 počet ASCII znaků je 256 –bitové pole musí mít 256 bitů, tj. 256/8 = 32 bajtů (slabik) typedef struct { unsigned char pole[32]; } TMnozZnaku; Poznámka: –efektivnější by bylo použít pole typu int o velikosti 256/(sizeof(int)*8), z pedagogických důvodů použijeme typ char, kde je velikost pole dána konstantou

32 Vyprázdnění množiny operace vyprázdnění množiny představuje vynulování všech prvků pole void vyprazdni(TMnozZnaku *m) { memset(m->pole,0,32); } Doplněk množiny doplněk množiny vytvoříme bitovou negací celého pole

33 void doplnek(TMnozZnaku *m) { for(int i=0;i<32;i++) m->pole[i] = ~(m->pole[i]); } Vložení prvku je třeba nastavit příslušný bit –pomocí operace logického součtu s konstantou pro výpočet adresy nastavovaného bitu musíme vytvořit mapovací funkci

34 kód znakuslabikabit ve slabice Příklad mapovací funkce - tabulka

35 adresa slabiky –celočíselné dělení číslem 8 sl = i / 8 adresa bitu ve slabice –zbytek po celočíselném dělení číslem 8 bit = i % 8 s příslušným bitem ve slabice budeme pracovat pomocí masky, kterou získáme operací posuvu konstanty 1 o počet bitů, který je roven adrese bitu ve slabice

36 void vloz(TMnozZnaku *m, unsigned char znak) { int sl = znak / 8; unsigned char maska = 1 << (znak % 8); m->pole[sl] |= maska; } Vyjmutí prvku při vyjímání prvku je maska negována a provádí se operace logického součinu (and, &)

37 Průnik dvou množin operace logického součinu nad bitovými poli obou množin void doplnek(TMnozZnaku *m1, TMnozZnaku *m2, TMnozZnaku *m3) { for(int i=0;i<32;i++) m3->pole[i] = m1->pole[i] & m2->pole[i]; }

38 Jakou použijeme operaci při implementaci sjednocení dvou množin? logický součet Jak bude vypadat testování přítomnosti prvku v množině? if (m->pole[sl] & maska != 0)

39 Stromy Strom: souvislý graf bez kružnic využití: –počítačová grafika – seznam objektů –efektivní vyhledávání –výpočetní stromy (rozhodování,…)

40 kořen list vnitřní uzel otec (rodič, předchůdce) potomek (syn)

41 Binární strom každý uzel má maximálně dva potomky uspořádaný binární strom –uzly jsou ohodnoceny prvky (čísly,…) –potomek na levé straně má vždy menší hodnotu nebo rovnu než rodič syn –potomek na pravé straně má vždy větší hodnotu –uspořádané binární stromy se využívají zejména jako vyhledávací stromy; složitost hledání je v průměrném případě log 2 n

42 Uspořádaný binární strom

43 binární strom je nejčastěji reprezentován ukazatelem na kořen (jde o ukazatel na uzel stromu) uzel je reprezentován strukturou: –prvkem, nesoucí informaci –ukazatelem na levý podstrom –ukazatelem na pravý podstrom operace nad stromem jsou rekurzivní typedef struct TUzel { int hodnota; TUzel *levy; TUzel *pravy; } TUzel;

44 list má ukazatele na levý a pravý podstrom nastaveny na NULL operace nad stromem –procházení stromu (může být spojeno s nějakou akcí) do šířky do hloubky –hledání prvku ve stromě –vložení nového prvku jako nový list –vyjmutí prvku –rušení stromu

45 Procházení stromu do hloubky

46 Procházení stromu do šířky

47 Obecný algoritmus procházení binárního stromu do hloubky void projdi(TUzel *u) { if (u==NULL) return; akce(u->hodnota); projdi(u->levy); projdi(u->pravy); }

48 Varianty procházení stromu do hloubky left order –levý podstrom, zpracování uzlu, pravý podstrom right order –pravý podstrom, zpracování uzlu, levý podstrom preorder –zpracování uzlu, levý podstrom, pravý podstrom další permutace, mají-li smysl

49 Příklad – left order výpis prvků uspořádaného stromu void vypis(TUzel *u) { if (u==NULL) return; vypis(u->levy); printf(″%d ″,u->hodnota); vypis(u->pravy); }

50 Příklad – pre order (reprezentace výrazu stromem) *

51 strom reprezentuje výraz (2+5)*(11-7) výpis v pre-order formě (polská notace) * – 11 7 void vypis_pre(TUzel *u) { if (u==NULL) return; printf(″%c ″,u->hodnota); vypis_pre(u->levy); vypis_pre(u->pravy); }

52 Poznámka: stromová reprezentace se běžně používá v překladačích programovacích jazyků

53 Hledání prvku vrací 1, je-li hodnota nalezena int najdi(TUzel *u, int x) { if (u==NULL) return 0; if (u->hodnota==x) return 1; if (x hodnota) return najdi(u->levy,x); else return najdi(u->pravy,x); }

54 Vložení nového prvku void pridej(TUzel **u, int x) { if (*u == NULL) { *u = (TUzel*)malloc(sizeof(TUzel)); (*u) -> hodnota = x; (*u) -> levy = NULL; (*u) -> pravy = NULL; } else if (x hodnota) pridej(&((*u)->levy),x); else pridej(&((*u)->pravy),x); }

55 Zrušení stromu void zrus(TUzel *u) { if (u==NULL) return; zrus(u->levy); zrus(u->pravy); free(u); }

56 void main(void) { TUzel *strom = NULL; pridej(&strom,10); pridej(&strom,5); pridej(&strom,7); pridej(&strom,2); pridej(&strom,12); pridej(&strom,11); najdi(strom,5); zrus(strom); }

57 Jaký vznikne strom nyní? void main(void) { TUzel *strom = NULL; pridej(&strom,10); pridej(&strom,5); pridej(&strom,7); pridej(&strom,2); pridej(&strom,11); pridej(&strom,12); }

58 A nyní? void main(void) { TUzel *strom = NULL; pridej(&strom,12); pridej(&strom,11); pridej(&strom,10); pridej(&strom,7); pridej(&strom,5); pridej(&strom,2); }

59 Snaha je vytvořit vyvážený strom, kde výška levé a pravé větve se liší maximálně o 1. To musí platit pro libovolný podstrom. Pokud tomu tak není, strom se vyvažuje

60


Stáhnout ppt "Další abstraktní datové typy. Abstraktní datové typy (ADT) abstraktní datový typ –datová struktura + operace nad touto strukturou jsou většinou dynamické."

Podobné prezentace


Reklamy Google