Implementace seznamů do prostředí DELPHI pomocí lineárního seznamu



Podobné dokumenty
1. D Y N A M I C K É DAT O V É STRUKTUR Y

Abstraktní datové typy FRONTA

Radomíra Duží L Datový typ množina

1. Implementace funkce počet vrcholů. Předmět: Algoritmizace praktické aplikace (3ALGA)

Implementace binárního stromu směrníky

Předmět: Algoritmizace praktické aplikace

O datových typech a jejich kontrole

Kolekce ArrayList. Deklarace proměnných. Import. Vytvoření prázdné kolekce. napsal Pajclín

Čtvrtek 8. prosince. Pascal - opakování základů. Struktura programu:

Binární vyhledávací strom pomocí směrníků Miroslav Hostaša L06620

Dynamické datové typy a struktury

NMIN102 Programování /2 Z, Zk

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

Sada 1 - Základy programování

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

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

Pascal. Katedra aplikované kybernetiky. Ing. Miroslav Vavroušek. Verze 7

Maturitní otázky z předmětu PROGRAMOVÁNÍ

Homer. prvky. délka. přední 0 zadní 4. Použití fronty BUS STOP. 3 Lisa. 2 Bart. 4 Maggie. 1 Marge. Grafické znázornění předchozí animace:

Náznak ukázky syntaxe a sémantiky pro projekt. 1 Syntaktické prvky. Poslední aktualizace: 8.

Programovací jazyk Pascal

Interpret jazyka IFJ2011

Programovací jazyk. - norma PASCAL (1974) - implementace Turbo Pascal, Borland Pascal FreePascal Object Pascal (Delphi)

7. OBJEKTOVĚ ORIENTOVANÉ PROGRAMOVÁNÍ

Vyučovací hodina. 1vyučovací hodina: 2vyučovací hodiny: Opakování z minulé hodiny. Procvičení nové látky

Programování II. Návrh programu I 2018/19

Implementace aritmetického stromu pomocí směrníků

Řízení toku programu Programátor musí být schopen nějak ovlivňovat běh programu a k tomu má několik možností:

4.3 Operace nad ordin ln mi datov mi typy Operace nad logick m datov m typem Operace nad celo seln mi datov mi typy

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

Implementace LL(1) překladů

dovolují dělení velkých úloh na menší = dekompozice

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.

V každém kroku se a + b zmenší o min(a, b), tedy vždy alespoň o 1. Jestliže jsme na začátku dostali 2

10 Algoritmizace Příklad 2 Word 2007/ VBA

Anotace. Dynamické programování, diskrétní simulace.

Řešení: PŘENESVĚŽ (N, A, B, C) = přenes N disků z A na B pomocí C

Sada 1 - Základy programování

Anotace. Soubory a práce s nimi, rekurze podruhé, struktury (datový typ record), Martin Pergel,

Programování 2 (NMIN102) Soubory. RNDr. Michal Žemlička, Ph.D.

Algoritmizace I. Ak. rok 2015/2016 vbp 1. ze 132

LED_007.c Strana: 1/5 C:\Michal\AVR\Výukové programy\archiv\ Poslední změna: :01:48

IB108 Sada 1, Příklad 1 Vypracovali: Tomáš Krajča (255676), Martin Milata (256615)

Anotace. Pointery. Martin Pergel,

VÝUKOVÝ MATERIÁL. Bratislavská 2166, Varnsdorf, IČO: tel Číslo projektu

Šablony, kontejnery a iterátory

Algoritmizace. 1. Úvod. Algoritmus

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

type Obdelnik = array [1..3, 1..4] of integer; var M: Obdelnik;

PHP tutoriál (základy PHP snadno a rychle)

Object Pascal je přísně typový procedurální jazyk, který umožňuje jak strukturované, tak objektově orientované programování.

TGH07 - Chytré stromové datové struktury

Intervalové stromy. Představme si, že máme posloupnost celých čísel p 0, p 1,... p N 1, se kterou budeme. 1. Změna jednoho čísla v posloupnosti.

Binární vyhledávací stromy

PES lib (C + PASCAL) KNIHOVNY KOMUNIKAÈNÍCH FUNKCÍ 03/ PESlib KOMUNIKAČNÍ KNIHOVNY C, PASCAL 03/ stran 1

Úvod do programování

POČÍTAČE A PROGRAMOVÁNÍ

Základní způsoby: -Statické (přidělění paměti v čase překladu) -Dynamické (přiděleno v run time) v zásobníku na haldě

Delphi podstata, koncepce a metody MDI aplikace

Šablony, kontejnery a iterátory

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

9. lekce Úvod do jazyka C 4. část Funkce, rekurze Editace, kompilace, spuštění Miroslav Jílek

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

PL/SQL. Jazyk SQL je jazykem deklarativním, který neobsahuje procedurální příkazy jako jsou cykly, podmínky, procedury, funkce, atd.

Úvod do programování - Java. Cvičení č.4

2. blok část B Základní syntaxe příkazů SELECT, INSERT, UPDATE, DELETE

Časová a prostorová složitost algoritmů

Lineární spojový seznam (úvod do dynamických datových struktur)

Základní způsoby: -Statické (přidělění paměti v čase překladu) -Dynamické (přiděleno v run time) v zásobníku na haldě

Dynamické datové struktury III.

Programování. Debugging a testování. Martin Urza

for (int i = 0; i < sizeof(hodnoty) / sizeof(int); i++) { cout<<hodonoty[i]<< endl; } cin.get(); return 0; }

Informatika Algoritmy

Zpracování deklarací a přidělování paměti

CZ.1.07/1.5.00/

Poslední nenulová číslice faktoriálu

Skripta ke školení. Základy VBA. vypracoval: Tomáš Herout. tel:

Syntaktická analýza. Implementace LL(1) překladů. Šárka Vavrečková. Ústav informatiky, FPF SU Opava

Cvičení 7: Delphi objekty CheckedBox, Radio- Button, EditBox

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

Katedra kybernetiky skupina Inteligentní Datové Analýzy (IDA) 9. dubna Filip Železný (ČVUT) Vytěžování dat 9.

Funkce pokročilé možnosti. Úvod do programování 2 Tomáš Kühr

Rekurze. Pavel Töpfer, 2017 Programování 1-8 1

Tabulka symbolů. Vazba (binding) Vazba - příklad. Deklarace a definice. Miroslav Beneš Dušan Kolář

PROGRAMOVÁNÍ. Cílem předmětu Programování je seznámit posluchače se způsoby, jak algoritmizovat základní programátorské techniky.

3 Co je algoritmus? Trocha historie Definice algoritmu Vlastnosti algoritmu... 3

Datové typy a struktury

Je n O(n 2 )? Je n 2 O(n)? Je 3n 5 +2n Θ(n 5 )? Je n 1000 O(2 n )? Je 2 n O(n 2000 )? Cvičení s kartami aneb jak rychle roste exponenciála.

Datové struktury. alg12 1

2.1 Podmínka typu case Cykly Cyklus s podmínkou na začátku Cyklus s podmínkou na konci... 5

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

NPRG030 Programování I, 2015/16 1 / :25:32

Anotace. Pointery, dynamické proměnné

Inovace a zkvalitnění výuky prostřednictvím ICT Vytváření aplikací pro systém Windows Víceřádkové vstupy komponenta Memo

Funkce, podmíněný příkaz if-else, příkaz cyklu for

Binární vyhledávací strom. Proč binární? Vyhledávání

OSTRAVSKÁ UNIVERSITA V OSTRAVĚ Pedagogická fakulta Obor informační technologie ve vzdělávání Kombinované studium

Programy v prostředí operačního systému

Knihovna DataBoxLib TXV první vydání prosinec 2010 změny vyhrazeny

Anotace. Spojové seznamy, haldy. AVL-stromy, A-B stromy. Martin Pergel,

Transkript:

Implementace seznamů do prostředí DELPHI pomocí lineárního seznamu Ukazatel a dynamické datové struktury v prostředí DELPHI Důležitým termínem a konstrukčním programovým prvkem je typ UKAZATEL. Je to vlastně datový typ proměnné, která ukazuje na jinou proměnnou, která může být několika typů integer, char, string, nebo i rekord. Vlastní obsah proměnné typu ukazatel je místo v paměti, kde se nalézá konkrétní informace, na kterou ukazatel ukazuje. Při tvorbě programu nemáme přímý přístup k adrese v paměti (k čemu taky??), můžeme pouze vytvářet (příkazem NEW), přiřazovat, nebo mazat (příkazem DISPOSE). Můžeme však dosti rozmanitě pracovat s proměnnou na kterou tento datový typ UKAZATEL ukazuje. Každému ukazateli je nejdříve nutno před jeho použitím vytvořit místo v paměti. Tuto operaci provedeme pomocí příkazu NEW. Ten vybere náhodné nevyužité místo v paměti a dosadí do ukazatele jeho adresu. Při ukončení práce s programem je vhodné tuto náhodně vybranou paměť opět uvolnit pro jiné účely. To se provede pomocí příkazu DISPOSE. Pokud se toto neprovede, lze očekávat, že některé složitější programy zvláště na méně výkonných počítačích budou kolabovat a blokovat se, jelikož paměť vyhrazena pro běh programu bude zanesena množstvím nezrušených ukazatelů a hodnot v paměti. Ukazatel se definuje pomocí znaku ^ + typ proměnné na který takto zadeklamovaný ukazatel ukazuje. Hlavní a v podstatě téměř jediné využití datového typu ukazatel je při práci s dynamickými datovými strukturami. To znamená při práci s proměnlivým množstvím normálních proměnných. Výhody tohoto datového uspořádání jsou ty, že množství hodnot se může při běhu programu měnit. Většinou se jedná o nějaký záznam (RECORD), který by měl obsahovat alespoň jeden UKAZATEL na tentýž záznam. Při definování takovýchto typů zjišťujeme, že jeden typ proměnné je definován druhým typem využíváme tedy princip rekurze. Tento typ dynamické proměnné se tak skládá ze dvou složek, z nichž jedna je hodnotová část, nesoucí údaj o hodnotě a druhá složka je adresní část, nesoucí údaje o adrese, většinou na svého následovníka a předchůdce. Tento princip se neustále opakuje. Jedno z nejjednodušších provedení tohoto příkazu je při tvorbě jednosměrného seznamu. Pod pojmem seznam rozumíme takovou dynamickou datovou strukturu, která umožňuje odebírat nebo přidávat libovolný prvek na kterékoliv pozici. Seznam si můžeme představit jako posloupnost prvků, kde vazba určuje následníka, případně předchůdce, vždy však nejvýše jednoho. Ve většině programů se zavádí ukazatel na začátek seznamu (ČELO, HLAVA apod. ) a na konec seznamu, teda na poslední obsazený prvek. (KONEC, POSLEDNÍ ). Při programové tvorbě seznamu využíváme většinou tyto operace pro manipulaci s prvky: vytvoření prázdného seznamu přidání nového prvku na konec seznamu odebrání prvku z čela seznamu test prázdnosti seznamu odstranění libovolného prvku ze seznamu vložení nového prvku na libovolné místo v seznamu 1

V programovém prostředí DELPHI by toto provedení mohlo vypadat následovně: Operace vytvoření prázdného seznamu a vložení prvku na první pozici seznamu - zde operace VYTVOR a PRIDEJ: Procedure Vytvor (var celo:spoj); Begin celo:=nil; Procedure Pridej (var celo,konec:spoj;h:thodnota); Var P:spoj; Begin NEW(P); P^.hodnota:=H; P^.dalsi:=NIL; if celo=nil then celo:=p //první vložená hodnota konec^.dalsi:=p; //ostatní vložené hodnoty konec:=p; //nový konec struktury} Tato konstrukce je obecná a níže je kód použitý v programu implementace ADT: procedure MujCREATE2; P:=nil Tento krok je klasické vytvoření prázdného seznamu, následuje fragment kódu pro vložení prvku s vlastnostmi načtenými z komponenty EDIT: procedure MujINSERT2(zb: Zbozi); var r: Pvag; new(r); // nový prvek r^.z:=zb; // naplníme zbozim r^.dalsi:=p; // připojíme k p p:=r; // p nastavíme na začátek var novy: Zbozi; //deklarace proměnné if (Edit1.Text<>'') and (Edit2.Text<>'') and (Edit3.Text<>'') and (Edit4.Text<>'') and (Edit5.Text<>'') then // musí být vyplněny všechny položky jinak vrátí chybu novy.druh:=edit1.text; //načítání údajů z komponent EDIT (1-5) novy.mnozstvi:=strtoint(edit2.text); //převedení řetězce na číslo novy.obchod:=edit3.text; novy.maxcena:=strtoint(edit4.text); novy.datumnakupu:=edit5.text; MujINSERT2(novy); Edit1.Text:=''; Edit2.Text:=''; Edit3.Text:=''; Edit4.Text:=''; 2

Edit5.Text:=''; end Memo1.Lines.Add(''); Memo1.Lines.Add('Prosím, vyplňte všechny údaje!!'); nevyplnění všech polí //hlášení při Postup pro vložení nového prvku do již existujícího seznamu prvků Pokud potřebujeme vložit nový prvek na konec seznamu můžeme použít buď jednoduchou konstrukci tohoto typu: New(P); //prosté zařazení prvku na konec seznamu, jednoduché P^.hodn := H; řešení, které ale ne vždy vyhovuje P^.dalsi := NIL; Konec:=konec^.dalsi; Nebo můžeme znovu zavolat výše uvedenou proceduru PRIDEJ. Pro zjednodušení grafická podoba nového seznamu: X1,x2,x3,. Hodnotová část Adr.na x2 Adresní část Tato procedury slouží pro vložení prvku na konec již existujícího seznamu. Pokud chceme vložit nový prvek na začátek seznamu, tzn. před prvek na který ukazuje předchozí jeho prvek v seznamu, v našem případě tedy hned za CELO a před prvek na který CELO ukazuje, mohla by být konstrukce takováto: New(P); P^.hodn := H; P^.dalsi := Celo; Celo:= P; P:= NIL; 3

Pokud chceme vkládat prvek na přímo určené místo v seznamu, musíme zavést jakousi značku, která označí ono místo podle předem daných kriterii, které si stanovíme. Nechť je pomocná značka označena jako POM, pak: New(P); P^.hodn := H; P^.dalsi := pom^.dalsi; Pom^.dalsi := P; Zde jsme prvek vložili za hodnotu x2, tzn. ukazatel POM byl nastaven na prvek X1. Při vkládání prvku před ukazatel POM je situace poněkud komplikovanější neboť potřebujeme znát adresu prvku předcházejícího, proto bychom měli zadeklamovat a umístit pomocný ukazatel - zde PŘED.Konstrukce pro vložení prvku před ukazatele POM by mohla nyní vypadat následovně: Pred := Celo; While Pred^.dalsi <> Pom DO Pred := Pred^.dalsi; New(P); P^.hodnota := H; P^.dalsi := Pom; Pred^.dalsi := P; Pred := NIL; P:= NIL; Prvek, jehož hodnota je rovna 5, byl nalezen příslušnou vyhledávací funkcí. Písmenem u je vyjádřen počátek seznamu, písmeno w označuje vkládaný prvek. Hledání prvku obsahuje porovnání čteného čísla s hodnotou aktuálního prvku. Pokud je hodnota prvku menší než přečtené číslo, postupujeme v lineárním seznamu k dalšímu prvku. Nalezená hodnota je tedy ta, která je poslední menší než přečtené číslo a za tuto hodnotu vložíme nový prvek. Odstranění prvku ze seznamu Odstranění prvního prvku ze seznamu Odstranění prvního prvku je nejjednodušší typ odstranění a použijeme proceduru VYBER. Procedure Vyber(var celo:spoj; var H:hodnota); Var p:spoj; Begin if celo = NIL then writeln('chyba') p:=celo; H:=celo^.hodnota; celo:=celo^.dalsi; dispose(p); 4

Takto provedená konstrukce obsahuje i test prázdnosti seznamu, který ale můžeme vynechat pokud víme, že seznam je určitě naplněn. Potom: P := Celo; Celo:= Celo^.dalsi; Dispose (P); Odstranění posledního prvku v seznamu Opět poněkud komplikovanější řešení, jelikož při odstraňování posledního prvku potřebujeme znát adresu předposledního prvku, je nutné deklarovat a umístit ukazatel PŘED a teprve potom provést vlastní odstranění prvku. Čili: Pred := Celo; While Pred^.dalsi <> Konec DO Pred := Pred^.dalsi; P := Konec; Konec := Pred; Konec^.dalsi := Nil; Dispose(P); Odstranění libovolného prvku ze seznamu Výše uvedené konstrukce programu platily pro odebrání dvou krajních prvků ze seznamu prvního a posledního. Při operaci odebrání prvků, které nejsou ani na jednom umístění musíme opět použít adresu prvku který předchází odebíranému prvku. V níže uvedeném provedení se tato adresa zjišťuje ze značky PRED. Zde je nutno použít tyto příkazy, které odkazují na prvek označeny jako POM a zároveň je ukazatel umístíme PŘED. Pred := Celo; While Pred^.dalsi <> Pom DO Pred := Pred^.dalsi; Pred^.dalsi := Pom^.dalsi; Dispose(Pom); Pom := Nil; Pred := Nil; Pokud potřebujeme vypsat hodnotu, nebo údaj na značce, například při zařazování položek do seznamu, lze použít tento zdrojový kód: procedure vypis_udaj_na_znacce; writeln(znacka^.hodnota); Pokud chceme zjistit adresu prvku jejíž datová část je shodná s níže uvedenou podmínkou RESULT:= použijeme právě tuto konstrukci: function Najdi (zac: UkTypPrvku; cis: integer): UkTypPrvku; if zac^.hodnota>cis then result:= nil while (zac^.dalsiprvek<>nil) and (zac^.dalsiprvek^.hodnota<cis) do zac:= zac^.dalsiprvek; result:= zac 5

Tento přiřazovací příkaz (result:=zac) má smysl i při nenalezení žádaného prvku, jelikož je funkce NAJDI deklarována v každém případě. Procedury pro výpis nebo tisk seznamu Nejprve obecná procedura: procedure vypis(zac: UkTypPrvku); while zac <> nil do //průchod lineárním seznamem// memo1.lines.add(inttostr(zac^.hodnota)); zac:= zac^.dalsiprvek; //přechod na další prvek// cislo:= strtoint(edit1.text); vloznovyprvek(zacatek, cislo); vypis; V programu ADT je použita tato konstrukce výpisu prvků: function MujFIRST2(P: Pvag): Pvag; result:=p procedure TForm2.Button6Click(Sender: TObject); var pom: zbozi; Memo1.Lines.Clear; MEmo1.Lines.Add(''); Memo1.Lines.Add(' Výpis seznamu :'); q:=mujfirst2(p); if q <> nil then //průchod seznamem while q<> nil do pom:=q^.z; //přechod na další prvek Memo1.Lines.Add(pom.Druh); //načítání vlastností Memo1.Lines.Add(IntToStr(pom.Mnozstvi)); Memo1.Lines.Add(pom.Obchod); Memo1.Lines.Add(IntToStr(pom.MaxCena)); Memo1.Lines.Add(pom.DatumNakupu); Memo1.Lines.Add(''); q:=q^.dalsi; end Algoritmus tisku prvků je konkrétním případem obecného algoritmu průchodu lineárním seznamem s provedením nějaké akce. Podmínkou je, aby byl poslední prvek seznamu odlišitelný od ostatních prvků poslední prvek má vždy hodnotu ukazatele na další prvek nil. 6

Další použité procedury pro výpis prvků v seznamu: funkce použity v ADT function MujEND2(P: Pvag): Pvag; //tato funkce vypíše poslední prvek seznamu var r: Pvag; r:=p; if r<>nil then while r^.dalsi<>nil do r:=r^.dalsi; result:=r //dokud r není poslední, pokračuj Pro funkci zjištění následující pozice za pozicí Q použitá v programu ADT function MujNEXT2(P: Pvag): Pvag; var r: Pvag; r:=q; if r<> nil then //pokud r není nil, čili pokud není r poslední prvek if r^.dalsi<>nil then r:=r^.dalsi; result:=r // pokud další prvek není nil Tato funkce zajistí nalezení předcházejícího prvku, v našem případě prvku R, ležícího před Q. function MujPREVIOUS2(P: Pvag): Pvag; var r: Pvag; r:=p; if q<>r then // pokud už neukazuje na začátek if r^.dalsi<>nil then while r^.dalsi<>q do r:=r^.dalsi; // tak dlouho dokud r není před q result:=r Když seznam už nebudeme používat, měly bychom uvolnit místo v paměti. Opět použijeme průchod lineárním seznamem. procedure TForm2.FormCreate(Sender: TObject); var pom: UkTypPrvku; while zacatek<>nil then pom:= zacatek; zacatek:= začátek^.dalsiprvek; dispose(pom); Internetové odkazy související s tématem: http://www.sweb.cz/david.padrta/pascal/6dynprom.html http://home.pf.jcu.cz/~edpo/program/kap11.html http://www.352.vsb.cz/predmety/zaklinf/dds.htm 7

Pro zájemce uvádím další možnosti použití seznamu Takhle nějak by mohl vypadat jednoduchý seznam, jehož princip je přibližně takovýto:v paměti na náhodných pozicích je uloženo pomocí příkazu RECORD, několik hodnot, nebo prvků. Na začátek seznamu ukazuje ukazatel ZACATEK a na poslední prvek ukazuje ukazatel KONEC. Každý prvek v seznamu (zde použití typu CHAR) obsahuje HODNOTU, která nese uložené informace a ukazatel na další návazný prvek v seznamu DALSI. První prvek tak ukazuje na druhy prvek, ten ukazuje na třetí prvek a tak stále dokola až na poslední prvek, který neukazuje nikam. Mezi těmito prvky se pohybuje značka, která se využívá ke čtení dat. Nevýhodou jednosměrného seznamu, jak už vyplývá z nazvu, je průchod značky jen jedním směrem od počátku na konec, nebo skokem na začátek nebo konec seznamu type ukprvek=^prvek; prvek=record hodnota:char; {nebo něco úplně jiného} dalsi:ukprvek; var zacatek,znacka,konec:ukprvek; procedure vloz_na_zacatek(s:char); var p:ukprvek; new(p); p^.hodnota:=s; p^.dalsi:=zacatek; zacatek:=p; procedure vloz_na_konec(s:char); var p:ukprvek; new(p); konec^.hodnota:=s; konec^.dalsi:=p; konec:=p; function odeber_ze_zacatku:char; var p:ukprvek; if zacatek=konec then writeln('chyba') 8

odeber_ze_zacatku:=zacatek^.hodnota; p:=zacatek; zacatek:=zacatek^.dalsi; dispose(p); procedure znacka_na_zacatek; znacka:=zacatek; procedure posun_znacku; if znacka<>konec then znacka:=znacka^.dalsi; procedure vypis_udaj_na_znacce; writeln(znacka^.hondota); {vytvořit seznam:} new(zacatek); konec:=zacatek;. 9