10 . dynamické datové struktury

31
10 10 . Dynamické datové struktury . Dynamické datové struktury Statická datová struktura - její rozsah se během provádění programu nemění - pole, záznamy. Dynamická datová struktura - její rozsah se během výpočtu mění. Vytváří se pomocí dynamických proměnných, jejich spojováním do seznamů. Dynamická proměnná je typu záznam a obsahuje jeden jeden nebo více ukazatelu na tento typ záznam. Nejjednodušší dynamickou datovou strukturou je lineární seznam, ve kterém každá dynamická proměnná ukazuje na svého následníka a jedna proměnná typu ukazatel ukazuje na jeho začátek. zacatek /

Upload: lemuel

Post on 21-Mar-2016

57 views

Category:

Documents


2 download

DESCRIPTION

10 . Dynamické datové struktury. Statická datová struktura - její rozsah se během provádění programu nemění - pole, záznamy. Dynamická datová struktura - její rozsah se během výpočtu mění. Vytváří se pomocí dynamických proměnných, jejich spojováním do seznamů. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 10 . Dynamické datové struktury

1010. Dynamické datové struktury. Dynamické datové struktury Statická datová struktura - její rozsah se během provádění programu nemění - pole, záznamy.

Dynamická datová struktura - její rozsah se během výpočtu mění. Vytváří se pomocí dynamických proměnných, jejich spojováním do seznamů.

Dynamická proměnná je typu záznam a obsahuje jeden jeden nebo více ukazatelu na tento typ záznam.

Nejjednodušší dynamickou datovou strukturou je lineární seznam, ve kterém každá dynamická proměnná ukazuje na svého následníka a jedna proměnná typu ukazatel ukazuje na jeho začátek.

zacatek /

Page 2: 10 . Dynamické datové struktury

Operace nad spojovým seznamem:

- přidání prvku na začátek seznamu:

prvek^.dalsi:=zacatek;zacatek:=prvek;

- odebrání prvního prvku v seznamu:

zacatek:=zacatek^.dalsi;

- provedení operace pro všechny prvky v seznamu:

p:=zacatek; s:=0;while p <> nil do begin s:=s + p^.hodnota; {proveden operace s p^} p:=p^.dalsi;end;

prvek

/ zacatek

/ zacatek

Page 3: 10 . Dynamické datové struktury

- přidání prvku na konec seznamu:- přidání prvku na konec seznamu:

posledni^.dalsi:=prvek;posledni^.dalsi:=prvek;posledni:=prvek;posledni:=prvek;

- vložení prvku za označený prvek- vložení prvku za označený prvek

prvek^.dalsi:=za^.dalsi;prvek^.dalsi:=za^.dalsi;za^.dalsi:=prvek;za^.dalsi:=prvek;

prvek

zacatek

/

/

posledni

prvek

zacatek

za

/

Page 4: 10 . Dynamické datové struktury

- vložení prvku před označený prvek- vložení prvku před označený prvek

Procedura vyhledá prvek předcházející prvek před který chceme vložit a provede Procedura vyhledá prvek předcházející prvek před který chceme vložit a provede vložení za tento prvek.vložení za tento prvek.

procedure vlozpred(var zacatek:spoj; prvek,pred:spoj);procedure vlozpred(var zacatek:spoj; prvek,pred:spoj);var p:spoj;var p:spoj;beginbegin if pred=zacatek then beginif pred=zacatek then begin prvek^.dalsi:=zacatek; prvek^.dalsi:=zacatek; zacatek:=prvekzacatek:=prvek endend else beginelse begin p:=zacatek;p:=zacatek; while p^.dalsi<>pred do p:=p^.dalsi;while p^.dalsi<>pred do p:=p^.dalsi; prvek^.dalsi:=pred;prvek^.dalsi:=pred; p^.dalsi:=prvek;p^.dalsi:=prvek; end;end;end;end;

prvek

zacatek

p

/

pred

Page 5: 10 . Dynamické datové struktury

Je-li seznam rozsáhlý, vkládáme nový prvek tak, že vyměníme hodnoty nového a Je-li seznam rozsáhlý, vkládáme nový prvek tak, že vyměníme hodnoty nového a označeného prvku a nový prvek pak vložíme za označený:označeného prvku a nový prvek pak vložíme za označený:

procedure vlozpred(pred,prvek:spoj);var pom:integer;begin pom:=pred.hodnota; pred^.hodnota:=prvek^.hodnota; prvek^.hodnota:=pom; prvek^.dalsi:=pred^.dalsi; pred^.dalsi:=prvek;end;

prvek

zacatek

pred

/

Page 6: 10 . Dynamické datové struktury

- odebrání označeného prvku:- odebrání označeného prvku:

1. Najdeme prvek, který v seznamu předchází označený prvek1. Najdeme prvek, který v seznamu předchází označený prvek

procedure odeber(var zacatek:spoj; co:spoj);var p:spoj;begin if co=zacatek then zacatek:=zacatek^.dalsi else begin p:=zacatek; while p^.dalsi<>co do p:=p^.dalsi; p^.dalsi:=co^.dalsi; end;end;

p

zacatek

co

/

Page 7: 10 . Dynamické datové struktury

2. Do označeného prvku přesuneme hodnotu následujícího prvku a tento následující 2. Do označeného prvku přesuneme hodnotu následujícího prvku a tento následující prvek odebereme (nelze pro poslední prvek).prvek odebereme (nelze pro poslední prvek).

procedure odeber(co:spoj);begin if co^.dalsi<>nil then begin co^.hodnota:=co^.dalsi^.hodnota; co^.dalsi:=co^.dalsi^.dalsi; end;end;

zacatek

co

/

Page 8: 10 . Dynamické datové struktury

Lineární seznam může být spojen ukazateli obousměrně - kromě ukazatele na další prvek obsahuje proměnná typu objekt také ukazatel na předchozí prvek.

type spoj=^objekt objekt=record / /zacatek konec hodnota:integer; dalsi,predchozi:spoj; end;

Taková datová struktura je vhodná například pro tvorbu textového editoru.

Speciálním případem jednosměrně vázaného lineárního seznamu je zásobník nebo fronta.

/ / zacatek konec

Page 9: 10 . Dynamické datové struktury

Zásobník

Zásobníkem rozumíme takovou dynamickou datovou strukturu, z níž se prvky vybírají v opačném pořadí než v jakém se do ní vkládají. Vybíráme tedy vždy poslední vložený prvek (podle toho se zásobníky také nazývají struktury LIFO - z anglického Last-In-First-Out).

Příkladem použití zásobníku je historie v internetových prohlížečích. Když kliknete na zpět, otevře se posledně otevřená stránka.Pro zásobník můžeme definovat tyto základní operace:

• vytvoření prázdného zásobníku • vložení prvku na vrchol zásobníku • odebrání prvku z vrcholu zásobníku • testování prázdnosti zásobníku

Zásobník můžeme v Pascalu implementovat (vytvořit) buď pomocí pole, tedy jako statickou datovou strukturu, nebo pomocí ukazatele, tedy jako dynamickou datovou strukturu.

Page 10: 10 . Dynamické datové struktury

Implementace pomocí pole:

Při této implementaci jsme nuceni omezit maximální velikost zásobníku a překročení této velikosti musíme v programu ošetřit. Také musíme ošetřit opačný stav, kdy chceme ze zásobníku prvek odebrat, ale zásobník je již prázdný.

const Maxdelka = N; {N je číslo omezující maximální délka zásobníku} var zasobnik : array[1..Maxdelka] of datovytyp; vrchol : 1..Maxdelka; procedure Vytvor; begin vrchol := 0; end;

function Jeprazdny: boolean; begin if vrchol = 0 then Jeprazdny := true; else Jeprazdny := false; end;

Page 11: 10 . Dynamické datové struktury

procedure Vloz(X: datovytyp); begin if vrchol = Maxdelka then begin {ošetření překročení maximální velikosti} write('Pozor - zasobnik je plny.'); Halt; end; else begin vrchol := vrchol + 1; zasobnik[vrchol] := X; end; end; procedure Odeber(var X: datovytyp); begin if vrchol = 0 then begin {ošetření prázdnosti zásobníku} write('Pozor - zasobnik je prazdny.'); Halt; end; else begin X := zasobnik[vrchol]; vrchol := vrchol - 1; end; end;

Page 12: 10 . Dynamické datové struktury

Implementace pomocí ukazatele:

Pokud budeme chtít implementovat zásobník pomocí ukazatele, nemusíme řešit maximální velikost zásobníku. Ta je dána velikostí té části operační paměti, ve které se vytvářejí dynamické proměnné - hromada (heap). Prvky se vkládají a odebírají na začátku jednosměrně vázaného lineárního spojového seznamu.

type spoj = ^objekt objekt = record hodnota: datovytyp; dalsi: spoj; end; var zasobnik : spoj; procedure Vytvor; begin zasobnik := nil; end;

function Jeprazdny: boolean; begin if zasobnik = nil then Jeprazdny := true; else Jeprazdny := false; end;

Page 13: 10 . Dynamické datové struktury

procedure Vloz(X: datovytyp); var pom: spoj; begin new(pom); with pom^ do begin hodnota := X; dalsi := zasobnik; end; zasobnik := pom; end; procedure Odeber(var X: datovytyp); var pom: spoj; begin if zasobnik = nil then begin write('Pozor - zasobnik je prazdny.'); Halt; end; else begin X := zasobnik^.hodnota; pom := zasobnik; zasobnik := zasobnik^.dalsi; dispose(pom); end; end;

Page 14: 10 . Dynamické datové struktury

Fronta

Fronta je dynamická datová struktura podobná zásobníku, rozdíl je pouze v tom, že prvky se z fronty odebírají v tom pořadí, v jakém se do fronty vkládají. Jako příklad si můžeme představit frontu nakupujících v obchodě. Podle toho se datový typ fronta také nazývá struktura FIFO - z anglického First-In-First-Out. Pro frontu můžeme definovat tyto operace:

• vytvoření prázdné fronty • vložení prvku na konec fronty • odebrání prvku ze začátku fronty • test prázdnosti fronty

Frontu stejně jako zásobník můžeme implementovat buď pomocí pole, nebo pomocí ukazatele.

Page 15: 10 . Dynamické datové struktury

Implementace pomocí pole:

Při této implementaci musíme neustále vědět, kde fronta začíná, kde končí a kolik má prvků. Také co se týče velikosti, musíme omezit maximální velikost fronty a překročení této velikosti musíme ošetřit. Pokud budeme vkládat prvky do prázdné fronty, bude situace vypadat následovně:

Vidíme, že do fronty byly postupně vloženy hodnoty Vidíme, že do fronty byly postupně vloženy hodnoty aa až až ff. .

Page 16: 10 . Dynamické datové struktury

. Pokud budeme s frontou pracovat tak, že staré prvky z ní budeme podle potřeby vybírat, nové do ní budeme vkládat, zjistíme, že pro nové vložení nám nemusí zbýt políčko ve frontě, zatímco po odebraných prvcích ze začátku fronty nám zbývají volná a nevyužitá políčka. Situace může vypadat např. takto:

Takovýto problém můžeme vyřešit např. tím, že po každém výběru prvku ze začátku fronty můžeme celou frontu přesypat na začátek, neboli všechny prvky budeme přesunovat o jedno místo dopředu.

Page 17: 10 . Dynamické datové struktury

Jednodušším řešením, které můžeme přirovnat k předchozímu řešení, je použití tzv. kruhové fronty. Za následníka posledního prvku považujeme u takovéto kruhové fronty první prvek.

Jak je vidět z obrázku, prvním prvkem je zde prvek s indexem 0 a poslední prvek má index N-1 vzhledem k tomu, že přidávání a odebírání prvků do a z fronty, resp. výpočet indexu prvku, je realizováno pomocí operace modulo N.

Page 18: 10 . Dynamické datové struktury

V následujícím výpisu jsou procedury vytvoření prázdné fronty, přidání prvku do fronty, odebrání prvku z fronty a funkce testování prázdnosti fronty.

const MaxDelka = N; {N je číslo omezující délku fronty} MaxIndex = N-1; var fronta: array[1..MaxDelka] of datovytyp; zacatek, konec: MaxIndex; delka: MaxDelka; procedure Vytvor; begin zacatek := 0; konec := 0; delka := 0; end;

function JePrazdna: boolean; begin if delka = 0 then JePrazdna := true else JePrazdna := false; end;

Page 19: 10 . Dynamické datové struktury

procedure Vloz(X: datovytyp); begin if delka = MaxDelka then begin {ošetření překročení maximální velikosti} write('Pozor - fronta je plna.'); Halt; end; else begin fronta[konec] := X; konec := (konec + 1) mod MaxDelka; delka := delka + 1; end; end; procedure Odeber(var X: datovytyp); begin if delka = 0 then begin {ošetření prázdnosti fronty} write('Pozor - fronta je prazdna.'); Halt; end; else begin X := fronta[zacatek]; zacatek := (zacatek + 1) mod MaxDelka; delka := delka - 1; end; end;

Page 20: 10 . Dynamické datové struktury

Implementace pomocí ukazatele:

Podobně jako u zásobníku, kontrola maximální velikosti fronty odpadá a maximální velikost fronty je dána velikostí hromady, ve které je fronta dynamicky vytvořena. V proceduře Odeber je použita pomocná proměnná pom typu spoj, kterou využijeme k odstranění prvního prvku fronty.

type spoj = ^objekt; objekt = record hodnota: datovytyp; dalsi: spoj; end; var zacatek, konec: spoj; procedure Vytvor; begin new(zacatek); konec := zacatek; end;

Page 21: 10 . Dynamické datové struktury

procedure Vloz(X: datovytyp); begin konec^.hodnota := X; new(konec^.dalsi); konec := konec^.dalsi; end; procedure Odeber(var X: datovytyp); var pom: spoj; begin if zacatek = konec then begin write('Pozor - fronta je prazdna.'); Halt; end else begin X := zacatek^.hodnota; pom := zacatek; zacatek := zacatek^.dalsi; dispose(pom); end; end; function JePrazdna: boolean; begin if zacatek = konec then JePrazdna := true else JePrazdna := false; end;

Page 22: 10 . Dynamické datové struktury

Binární stromBinární strom

Složitější dynamickou datovou strukturou je binární strom, ve kterém z každého uzlu Složitější dynamickou datovou strukturou je binární strom, ve kterém z každého uzlu vycházejí nejvýše dvě hrany.vycházejí nejvýše dvě hrany.

Vytváří se pomocí typu uzel:Vytváří se pomocí typu uzel:

typetype spoj=^uzel;spoj=^uzel; uzel=recorduzel=record hodnota:integer;hodnota:integer; levy,levy, pravy:spoj;pravy:spoj; end;end;

E F

H I G C

A D B

Page 23: 10 . Dynamické datové struktury

Průchod stromem za účelem provedení nějaké operace pro všechny uzly stromu se Průchod stromem za účelem provedení nějaké operace pro všechny uzly stromu se řeší pomocí rekurze. řeší pomocí rekurze. Existuje šest způsobů průchodu stromem, které se liší v pořadí provedení těchto Existuje šest způsobů průchodu stromem, které se liší v pořadí provedení těchto kroků:kroků:

a) proveď operaci v uzlu Ua) proveď operaci v uzlu Ub) projdi levý podstromb) projdi levý podstromc) projdi pravý podstrom.c) projdi pravý podstrom.

Praktický význam mají tři způsoby průchodu, které mají vlastní jména:Praktický význam mají tři způsoby průchodu, které mají vlastní jména:

preorderpreorder - a),b),c)- a),b),c)inorderinorder - b),a),c)- b),a),c)postorder - c),a),b)postorder - c),a),b)

Page 24: 10 . Dynamické datové struktury

Průchod stromem za účelem provedení nějaké operace pro všechny uzly stromu se Průchod stromem za účelem provedení nějaké operace pro všechny uzly stromu se řeší pomocí rekurze. řeší pomocí rekurze. Existuje šest způsobů průchodu stromem, které se liší v pořadí provedení těchto Existuje šest způsobů průchodu stromem, které se liší v pořadí provedení těchto kroků:kroků:

a) proveď operaci v uzlu Ua) proveď operaci v uzlu Ub) projdi levý podstromb) projdi levý podstromc) projdi pravý podstrom.c) projdi pravý podstrom.

Praktický význam mají tři způsoby průchodu, které mají vlastní jména:Praktický význam mají tři způsoby průchodu, které mají vlastní jména:

preorderpreorder - a),b),c)- a),b),c)inorderinorder - b),a),c)- b),a),c)postorder - c),a),b)postorder - c),a),b)

Použitím těchto průchodů pro uvedený příklad stromu obdržíme toto poředí uzlů:Použitím těchto průchodů pro uvedený příklad stromu obdržíme toto poředí uzlů:

preorder: preorder: E B A D C F H G IE B A D C F H G Iinorder: inorder: A B C D E F G H IA B C D E F G H Ipostorder: postorder: A C D B G I H F EA C D B G I H F E

E F

H I G C

A D B

Page 25: 10 . Dynamické datové struktury

Př.: Ukázka rekurzivní procedury pro výpis hodnot všech uzlů v pořadí určeném Př.: Ukázka rekurzivní procedury pro výpis hodnot všech uzlů v pořadí určeném metodou preorder.metodou preorder.

procedure vypis(strom:spoj);procedure vypis(strom:spoj);beginbegin if strom<>nil then beginif strom<>nil then begin write(strom^.hodnota);write(strom^.hodnota); vypis(strom^.levy);vypis(strom^.levy); vypis(strom^.pravy);vypis(strom^.pravy); end;end;end;end;

Binární vyhledávací stromBinární vyhledávací strom - pro každý uzel s hodnotou x platí, že všechny hodnoty - pro každý uzel s hodnotou x platí, že všechny hodnoty v levém podstromu jsou menší a v pravém větší než x. v levém podstromu jsou menší a v pravém větší než x.

Uzly po seřazení metodou inorder tvoří vzestupně uspořádanou posloupnost.Uzly po seřazení metodou inorder tvoří vzestupně uspořádanou posloupnost.

E F

H I G C

A D B

Page 26: 10 . Dynamické datové struktury

Př.: Rekurzivní procedura pro zařazení do binárního vyhledávacího stromu.Př.: Rekurzivní procedura pro zařazení do binárního vyhledávacího stromu.

procedure zarad(var strom:spoj; x:typhodnoty);procedure zarad(var strom:spoj; x:typhodnoty);beginbegin if strom=nil then beginif strom=nil then begin new(strom);new(strom); with strom^ do beginwith strom^ do begin hodnota:=x;hodnota:=x; levy:=nil; pravy:=nil;levy:=nil; pravy:=nil; endend endend elseelse with strom^ dowith strom^ do if x<hodnota then zarad(levy,x);if x<hodnota then zarad(levy,x); else zarad(pravy,x);else zarad(pravy,x);end;end;

Page 27: 10 . Dynamické datové struktury

Př.: Nerekurzivní funkce pro vyhledání uzlu se zadanou hodnotou v binárním Př.: Nerekurzivní funkce pro vyhledání uzlu se zadanou hodnotou v binárním vyhledávacím stromu. Funkce vrací ukazatel na daný uzel nebo nil.vyhledávacím stromu. Funkce vrací ukazatel na daný uzel nebo nil.

function hledej(strom:spojů x:typhodnoty):spoj;function hledej(strom:spojů x:typhodnoty):spoj;var konec:boolean;var konec:boolean;beginbegin konec:=false;konec:=false; repeatrepeat if strom=nil then konec:=trueif strom=nil then konec:=true elseelse with strom^ do with strom^ do if x<hodnota then strom:=levyif x<hodnota then strom:=levy else else if x>hodnota then strom:=pravyif x>hodnota then strom:=pravy else konec:=true else konec:=true until konec;until konec; hledej:=strom;hledej:=strom;end;end;

Page 28: 10 . Dynamické datové struktury

Př.: Program pro setřídění souboru čísel pomocí zatřiďování do lineárního Př.: Program pro setřídění souboru čísel pomocí zatřiďování do lineárního dynamického seznamu.dynamického seznamu.

program dynamicke_trideni;program dynamicke_trideni;typetype spoj = ^objekt;spoj = ^objekt; objekt = recordobjekt = record hodn : integer;hodn : integer; dalsi : spoj;dalsi : spoj; end;end;varvar zacatek : spoj;zacatek : spoj;

Page 29: 10 . Dynamické datové struktury

procedure vytvor_seznam(var zacatek : spoj);procedure vytvor_seznam(var zacatek : spoj); var prvek : spoj;var prvek : spoj; procedure zarad_prvek(prvek : spoj; var zacatek : spoj);procedure zarad_prvek(prvek : spoj; var zacatek : spoj); var za,pom : spoj;var za,pom : spoj; beginbegin if prvek^.hodn < zacatek^.hodn then beginif prvek^.hodn < zacatek^.hodn then begin prvek^.dalsi := zacatek;prvek^.dalsi := zacatek; zacatek := prvek;zacatek := prvek; endend else beginelse begin pom := zacatek;pom := zacatek; repeatrepeat za := pom;za := pom; pom := pom^.dalsi;pom := pom^.dalsi; until (pom = nil)until (pom = nil)oror(prvek^.hodn < pom^.hodn(prvek^.hodn < pom^.hodn));; za^.dalsi := prvek;za^.dalsi := prvek; prvek^.dalsi := pom;prvek^.dalsi := pom; end;end; end;end;

Page 30: 10 . Dynamické datové struktury

begin {vytvor_seznam}begin {vytvor_seznam} if not eof then beginif not eof then begin new(zacatek);new(zacatek); with zacatek^ do beginwith zacatek^ do begin readln(hodn);readln(hodn); dalsi := nil;dalsi := nil; end;end; while not eof do beginwhile not eof do begin new(prvek);new(prvek); readln(prvek^.hodn);readln(prvek^.hodn); zarad_prvek(prvek,zacatek);zarad_prvek(prvek,zacatek); end;end; end;end; end; {vytvor_seznam}end; {vytvor_seznam}

Page 31: 10 . Dynamické datové struktury

procedure tiskni_seznam(zacatek : spoj);procedure tiskni_seznam(zacatek : spoj); varvar prvek : spoj;prvek : spoj; pocet : integer;pocet : integer; beginbegin if zacatek <> nil then beginif zacatek <> nil then begin write(zacatek^.hodn:5);write(zacatek^.hodn:5); pocet := 1;pocet := 1; prvek := zacatek;prvek := zacatek; while prvek^.dalsi while prvek^.dalsi <><> nil nil do begin do begin prvek := prvek^.dalsi;prvek := prvek^.dalsi; write(prvek^.hodn:5);write(prvek^.hodn:5); pocet := pocet + 1;pocet := pocet + 1; if pocet mod 5 = 0 then writelnif pocet mod 5 = 0 then writeln end;end; writeln;writeln; end;end; end;end;beginbegin vytvor_seznam(zacatek);vytvor_seznam(zacatek); tiskni_seznam(zacatek);tiskni_seznam(zacatek);end. end.