Prof.nadzw.dr hab.inż. Władysław Brzozowski
Politechnika Częstochowska
Instytut Elektroenergetyki
Wykłady z przedmiotu:
TECHNIKA PROGRAMOWANIA
studia magisterskie, kierunek Elektrotechnika,
specjalno** Informatyka w Elektroenergetyce, sem.VI
Wyk*ad 6. J*zyk Turbo-Pascal. Dynamiczne struktury danych w systemie Turbo-Pascal
1. Formy danych w systemie Turbo Pascal. Cel stosowania dynamicznych struktur danych
W systemie Turbo Pascal wyr**niamy dane statyczne i dynamiczne. Dane statyczne dzielimy na lokalne i globalne.
Dane statyczne lokalne zajmuj* obszar tzw. segmentu stosowego (stack) o rozmiarze okre*lanym przez programist*, ale nie mniejszym ni* 1024 i nie wi*kszym ni* 65520 bajt*w.
Dane statyczne globalne zajmuj* obszar jednego segmentu pami*ci operacyjnej, a wi*c 65520 bajt*w. W efekcie **czny maksymalny rozmiar danych statycznych jest ograniczony i w przypadku programu operuj*cego na du*ej liczbie danych nie wystarcza.
Wprowadzamy zatem do programu dane dynamiczne w postaci zmiennych wskazywanych. Dane te zajmuj* obszar tzw. sterty (heap) o rozmiarze okre*lanym przez programist*, w przedziale od 0 do 655360 bajt*w. Oznacza to, *e ca*a pami** podstawowa komputera mo*e by* zaj*ta przez dane dynamiczne. Wiemy te*, *e zmienne dynamiczne tworzymy a nast*pnie odwo*ujemy w dowolnych punktach programu. Wyobra*my sobie, *e tworzymy zmienn* wskazywan* typu tablica rekord*w:
type
RekXYZ = record
i: integer;
x: real;
t: string [34]
end;
var
tabl1: ^array [1..1000] of RekXYZ;
Na og** nie wiemy ile rekord*w przyjdzie nam zapisa* do tablicy, st*d rozmiar tej tablicy musi by* du*y, gdy* musi uwzgl*dnia* maksymaln* spodziewan* liczb* rekord*w.
Tak zorganizowana struktura jest wi*c ma*o efektywna (mo*naby j* nazwa* zdeterminowan* struktur* danych dynamicznych).
Efektywniejsza jest dynamiczna struktura danych dynamicznych - to jest taka w kt*rej liczba obiekt*w nie jest w *aden spos*b zdeterminowana - jest zmienna w poszczeg*lnych przebiegach programu.
Wyr**nia si* nast*puj*ce dynamiczne struktury danych:
Dynamiczna struktura danych
| - stos
| - kolejka
| - lista
| | - liniowa
| | | - jednokierunkowa
| | | - dwukierunkowa
| | - cykliczna
| - drzewo
| - binarne
| - wielokierunkowe
Wsp*ln* cecha powy*szych struktur danych jest to, *e elementem struktury jest zawsze rekord, zawieraj*cy:
pole podstawowe rezerwowane dla obiektu roboczego b*d*cego celem tworzenia struktury dynamicznej danych (w powy*ej przytoczonym przyk*adzie takim obiektem roboczym jest rekord RekXYZ; w og*lnym przypadku nie musi to jednak by* rekord lecz dowolna, z wyj*tkiem zmiennej plikowej, zmienna prosta lub z*o*ona), oraz
w zale*no*ci od typu struktury co najmniej jedno, a niekiedy wiele p*l wska*nikowych. W tych polach wska*nikowych zapisuje si* adresy innych element*w struktury.
Wyja*nimy te poj*cia na przyk*adzie konkretnych struktur. Trzeba tu koniecznie podkre*li*, *e omawiamy struktury wy**cznie danych w pamieci operacyjnej komputera. Nie maj* one nic wsp*lnego z pami*cia dyskowa. Po wy**czeniu komputera struktury te ulegaj* likwidacji, je*li wcze*niej nie zostan* one zlikwidowane przez program.
2. Stos
Poj*cia tego nie nale*y myli* ze stosem danych statycznych lokalnych. Tu stos jest struktur* dynamiczn* danych dynamicznych, tak* do kt*rej dok*adamy elementy zawsze przy ko*cu stosu (koniec ten nazywa si* wierzcho*kiem stosu). Usuwamy elementy ze stosu rownie* poczynaj*c od wierzcho*ka, tj. od ostatniego elementu, kolejno w d** do podstawy stosu. Odczyt element*w r*wnie* odbywa si* od wierzcho*ka, kolejno w d**. Brak jest bezpo*redniego dost*pu do element*w innych ni* ostatni element stosu.
Elementem stosu jest, jak ju* powiedziano zawsze rekord, o postaci (przyjmijmy *e obiektem roboczym jest rekord z danymi pracownik*w):
type
DanePracownika = record
Nazwisko: string [30];
Imie: string [20];
GrupaZaszeregowania: integer
end;
TypElementuStosu= ^RekordStosowy;
RekordStosowy = record
ObiektRoboczy: DanePracownika;
AdresPoprzedniegoElementuNaStosie: pointer
end;
var
ElementBiezacy: TypElementuStosu;
AdresWierzcholkaStosu,AdresPomocniczy: pointer;
Tak zorganizowany stos b*dzie mia* struktur* jak ni*ej (przedstawion* tak jakgdyby mia* on 3 elementy):
3-ci element stosu (wierzcho*ek stosu) - pole robocze - obiekt roboczy elementu nr.3 (dane pracownika nr.3) - pole wska*nikowe - adres elementu nr.2 w pami*ci operacyjnej
2-gi element stosu - pole robocze - obiekt roboczy elementu nr.2 (dane pracownika nr.2) - pole wska*nikowe - adres elementu nr.1 w pami*ci operacyjnej
1-szy element stosu - pole robocze - obiekt roboczy elementu nr.1 (dane pracownika nr.1) - pole wska*nikowe - adres poprzedniego elementu = nil (brak poprzedniego elementu na stosie)
W stosie zapisane s* adresy poszczeg*lnych element*w z wyj*tkiem ostatniego. Aby wi*c, po utworzeniu stosu, m*c znale** go w pamieci operacyjnej, nale*y zapami*ta* w odr*bnej zmiennej typu wska*nikowego adres ostatniego elementu, czyli wierzcho*ka stosu. W powy*szym przyk*adzie jest to zmienna AdresWierzcholkaStosu.
Elementy stosu w *aden spos*b nie s* w pami*ci operacyjnej uporz*dkowane. Przeciwnie: tworz*c nowy element stosu, system operacyjny przydziela mu miejsce w pami*ci operacyjnej przypadkowe, tj. takie jakie w danym momencie jest wolne. Identyfikacja stosu nast*puje wy**cznie przez adresy zapisane w kolejnych elementach.
Ci*g instrukcji przy tworzeniu stosu od pocz*tku przedstawia si* nast*puj*co:
{1} New(ElementBiezacy); {utworzenie elementu nr.1}
Instrukcj* New(ElementBiezacy) tworzymy ka*dy kolejny element stosu, a nast*pnie zapisujemy w jego polu roboczym jakie* u*yteczne dane np.
{2} ElementBiezacy^.ObiektRoboczy.Nazwisko:='Nowak'; {generowanie pola
roboczego elementu}
Jest to przyk*adowa instrukcja generowania pola roboczego w zakresie przyk*adowego pola rekordu roboczego. Nast*pnie:
{3} ElementBiezacy^.AdresPoprzedniegoElementuNaStosie:=nil; {wpisanie
w polu wskaznikowym elementu nr.1 adresu pustego nil}
Poniewa* brak elementu zerowego zatem brak adresu do zapisu w odniesieniu do elementu nr.1.
{4} AdresPomocniczy:=ElementBiezacy; {zapamietanie adresu elementu
nr.1}
Adres ten musi zosta* zapami*tany w zmiennej pomocniczej wska*nikowej, gdy* po utworzeniu elementu nr.2 tracimy mo*liwo** operowania elementem nr. 1 i jego identyfikacji w pami*ci operacyjnej.
Nale*y zwr*ci* tu uwag*, *e zmienna ElementBiezacy jest typu wska*nikowego a nie rekordowego (zatem nie ma tu niezgodno*ci typu). Zmienna ElementBiezacy zawiera wy**cznie adres zmiennej wskazywanej w pami*ci operacyjnej, tj. w tym przypadku zmiennej RekordStosowy kt*ra jest typu rekordowego. Dlatego instrukcja powy*sza realizuje wy**cznie operacj* przepisania adresu pomi*dzy dwiema zmiennymi typu wska*nikowego.
{5} AdresWierzcholkaStosu:=ElementBiezacy;
Tworz*c kolejny element stosu nale*y stale aktualizowa* adres wierzcho*ka stosu, czyli adres ostatnio wygenerowanego elementu.
{6} New(ElementBiezacy); {utworzenie elementu nr.2}
{7} ElementBiezacy^.AdresPoprzedniegoElementuNaStosie:=
AdresPomocniczy; {wpisanie w polu wskaznikowym elementu nr.2 adresu elementu nr.1}
{8} AdresWierzcholkaStosu:=ElementBiezacy;
Aktualizacja adresu wierzcho*ka stosu.
Przy likwidacji stosu ci*g instrukcji przedstawia si* nast*pujaco:
{1} ElementBiezacy:=AdresWierzcholkaStosu; {lokalizacja ostatniego
elementu czyli wierzcholka stosu}
{2}AdresPomocniczy:=ElementBiezacy^.AdresPoprzedniegoElementuNaStosie;
{ustalenie adresu poprzedniego elementu na stosie i jego zapamietanie
w komorce pomocniczej}
Gdyby*my zlikwidowali ostatni element bez powy*szej operacji to utraciliby*my zdolno** dotarcia do elementu poprzedniego.
{3} Dispose(ElementBiezacy); {likwidacja elementu ostatniego}
{4} ElementBiezacy:=AdresPomocniczy; {lokalizacja elementu
poprzedniego}
{5} AdresWierzcholkaStosu:=ElementBiezacy; {aktualizacja adresu
wierzcholka stosu}
Itd. a* do podstawy stosu, tj. tak d*ugo a* zostanie spe*niony warunek ElementBiezacy^.AdresPoprzedniegoElementuNaStosie=nil
Operowanie innymi formami dynamicznych struktur danych dynamicznych realizuje si* podobnie; zatem procedury te nie bed* tu szczego*owo przytaczane. Zostanie dokonana jedynie og*lna charakterystyka tych struktur.
3. Kolejka
Kolejka jest struktur* do kt*rej zapis nowego elementu mo*e nast*powa* wy**cznie przy ko*cu kolejki, natomiast identyfikacja/odczytanie kolejki oraz ewentualne usuni*cie elementu nast*puje od pocz*tku kolejki.
Zgodnie z powy*szym kolejk* organizujemy za pomoc* poni*szych deklaracji (u*ywajac nazw jak dla w przyk*adzie wy*ej dla stosu):
type
TypElementuKolejki= ^RekordKolejkowy;
RekordKolejkowy= record
ObiektRoboczy: DanePracownika;
AdresNastepnegoElementuWKolejce: pointer
end;
var
ElementBiezacy,ElementPoprzedni: TypElementuKolejki;
AdresPoczatkuKolejki,AdresPomocniczy: pointer;
Jak wida* nast*puje tu zmiana adres*w - w polu wska*nikowym rekordu podaje si* nie adres poprzedniego elementu, jak to by*o w przypadku stosu, lecz nast*pnego. Podobnie, celem identyfikacji kolejki trzeba zapami*ta* adres pierwszego elementu czyli adres pocz*tku kolejki. Przetwarzanie kolejki wymaga te* wi*kszej liczby zmiennych pomocniczych.
4. Lista jednokierunkowa
Lista jednokierunkowa jest struktur* podobn* do stosu (je*li identyfikujemy list* od ko*ca) lub do kolejki (je*li identyfikujemy list* od pocz*tku).
Lista tym si* jednak r**ni od tych dwu struktur, *e zapis nowego elementu lub likwidacja elementu mo*e nast*powa* w dowolnym miejscu listy, tak*e w *rodku listy.
Je*li ostatni element listy pokrywa si* z pierwszym to taka lista nazywa si* list* cykliczn*, w przeciwnym razie jest to lista liniowa. Deklaracje dla listy jednokierunkowej wygladaj* podobnie jak dla stosu lub kolejki, i nie bed* tu przytaczane.
5. Lista dwukierunkowa
Lista dwukierunkowa ma wszystkie cechy listy jednokierunkowej, a ponadto identyfikacja mo*e nast*powa* zar*wno od strony pocz*tku jak i ko*ca listy. Odpowiednio, list* dwukierunkow* organizujemy za pomoc* poni*szych deklaracji (u*ywajac nazw jak dla w przyk*adzie dla stosu):
type
TypElementuListy= ^RekordListowy;
RekordListowy= record
ObiektRoboczy: DanePracownika;
AdresPoprzedniegoElementuNaLiscie: pointer
AdresNastepnegoElementuNaLiscie: pointer
end;
var
ElementBiezacy,ElementPoprzedni,ElementNastepny: TypElementuListy;
AdresPoczatkuListy,AdresKoncaListy,AdresPomocniczy1,AdresPomocniczy2,
AdresPomocniczy3: pointer;
Jak wida* nast*puje tu dalsza zmiana adres*w - w polu wska*nikowym rekordu podaje si* adres i poprzedzaj*cego elementu i nast*puj*cego elementu.
Podobnie, celem identyfikacji kolejki trzeba zapami*ta* adres zar*wno pierwszego jak i ostatniego elementu. Przetwarzanie listy dwukierunkowej wymaga jeszcze wi*kszej liczby zmiennych pomocniczych.
6. Drzewo
Drzewo jest struktur* w kt*rej za ka*dym elementem mo*na dopisa* nie jeden, lecz dwa (je*li jest to drzewo binarne) lub wi*cej (je*li jest to drzewo wielokierunkowe) element*w. Odpowiednio, w polu wska*nikowym ka*dego elementu nale*y zapisa* liczb* element*w nast*puj*cych oraz adresy wszystkich element*w nast*puj*cych (w postaci tablicy zmiennych wska*nikowych). Nale*y te* zapami*ta* adres pierwszego elementu drzewa.
7. Przyk*adowe programy dydaktyczne ilustruj*ce dynamiczne struktury danych (stos.pas, kolejka.pas, listadwu.pas)
{plik stos.pas}
{$A+,B-,D+,E+,F-,I+,L+,N-,O-,R-,S+,V+}
{$M 16384,0,655360}
program Stos;
{Program dydaktyczny przedstawiajacy sposob generowania i czytania
dynamicznej struktury danych typu stos. Stos jest struktura do
ktorej zapis i z ktorej usuniecie moze nastepowac jedynie od konca
nazwanego wierzcholkiem stosu, tj. od ostatnio wprowadzonego
obiektu na stos. Obiektem zapisywanym na stos moze byc dowolna
zmienna prosta lub strukturalna. W przypadku niniejszego programu
obiektem jest tablica 5 liczb typu integer, ktorymi sa kolejne
liczby naturalne, tj. dla pierwszego obiektu na stosie 1,1,1,1,1,
dla drugiego 2,2,2,2,2 itd.}
uses Crt;
type
AdresObiektu = ^TypRekorduZObiektem;
TypRekorduZObiektem = record
Obiekt : array [1..5] of integer;
AdresPoprzedniegoObiektu: AdresObiektu
end;
var
BiezacyObiekt: AdresObiektu;
AdresPomocniczy: Pointer;
I,J,LiczbaObiektowDoZapisu: integer;
Znak: char;
label
ZapisNaStos,OdczytStosu,Menu;
begin
{naglowek}
ClrScr;
WriteLn('Program dydaktyczny Stos przedstawiajacy sposob generowania');
WriteLn('i odczytu dynamicznej struktury danych typu stos.');
WriteLn('Program zapisuje na stosie, a nastepnie odczytuje obiekt,');
WriteLn('ktorym jest tablica 5 liczb typu integer. Liczbami tymi sa');
WriteLn('kolejne liczby naturalne.');
{menu}
Menu:
while KeyPressed do Znak:=ReadKey;
Writeln;
Write('Wybierz: zapis (Z), odczyt (O), koniec (K): ? ');
ReadLn(Znak);
if Znak='K'
then
Halt;
if Znak='Z'
then
goto ZapisNaStos
else
goto OdczytStosu;
{zapis}
ZapisNaStos:
{okreslenie parametrow wyjsciowych}
{adres pusty obiektu zerowego - poprzedniego w stosunku do pierwszego
na stosie}
AdresPomocniczy:=nil;
{ustalenie liczby obiektow zapisywanych na stos}
while KeyPressed do Znak:=ReadKey;
Write('Podaj liczbe obiektow ktore chcesz zapisac na stos: ? ');
ReadLn(LiczbaObiektowDoZapisu);
{petla glowna zapisu}
for I:=1 to LiczbaObiektowDoZapisu do
begin
{utworzenie nowego obiektu dynamicznego na stosie}
New(BiezacyObiekt);
{wygenerowanie wartosci aktualnie zapisywanego obiektu}
for J:=1 to 5 do
BiezacyObiekt^.Obiekt[J]:=I;
{wizualizacja obiektu zapisywanego oraz adresu nowo utworzonego
obiektu dynamicznego}
Write('Num.obiektu: ',I:1,' Obiekt: ');
for J:=1 to 5 do
Write(BiezacyObiekt^.Obiekt[J]:4);
Writeln(' Adres obiektu: ',Seg(BiezacyObiekt^):5,':',
Ofs(BiezacyObiekt^):4);
{wygenerowanie aktualnie zapisywanego adresu ktorym jest adres
poprzedniego obiektu na stosie}
BiezacyObiekt^.AdresPoprzedniegoObiektu:=AdresPomocniczy;
{zapamietanie adresu nowo utworzonego obiektu dynamicznego}
AdresPomocniczy:=BiezacyObiekt
{koniec petli glownej zapisu}
end; {for I:=1 to LiczbaObiektowDoZapisu do}
{powrot do menu}
goto Menu;
{odczyt}
OdczytStosu:
{ustalenie adresu ostatniego obiektu na stosie tj. wierzcholka stosu}
AdresPomocniczy:=BiezacyObiekt;
{wizualizacja stosu}
WriteLn;
Writeln('Zawartosc stosu:');
{instrukcja iteracyjna wizualizacji i likwidacji stosu}
while AdresPomocniczy<>nil do
begin
{odczyt i wizualizacja biezacego obiektu}
for J:=1 to 5 do
Write(BiezacyObiekt^.Obiekt[J]:4);
Writeln(' Adres obiektu: ',Seg(BiezacyObiekt^):5,':',
Ofs(BiezacyObiekt^):4);
{zapamietanie adresu poprzedniego obiektu na stosie}
AdresPomocniczy:=BiezacyObiekt^.AdresPoprzedniegoObiektu;
{likwidacja biezacego obiektu na stosie}
Dispose(BiezacyObiekt);
{wskazanie adresu nowego obiektu}
BiezacyObiekt:=AdresPomocniczy
{koniec iteracji}
end; {while AdresPomocniczy<>nil do}
{powrot do menu}
goto Menu;
end. {program Stos}
{plik kolejka.pas}
{$A+,B-,D+,E+,F-,I+,L+,N-,O-,R-,S+,V+}
{$M 16384,0,655360}
program Kolejka;
{Program dydaktyczny przedstawiajacy sposob generowania, czytania i
likwidacji obiektow dynamicznej struktury danych typu kolejka.
Kolejka jest struktura do ktorej zapis moze nastepowac jedynie na koncu
natomiast usuniecie obiektu jedynie na poczatku kolejki. Obiektem
zapisywanym do listy moze byc dowolna zmienna prosta lub strukturalna.
W przypadku niniejszego programu obiektem jest tablica 5 liczb typu
integer, ktorymi sa kolejne liczby naturalne, tj. dla np. dla pewnego
obiektu na liscie 1,1,1,1,1, dla innego 2,2,2,2,2 itd.}
uses Crt;
type
AdresObiektu = ^TypRekorduZObiektem;
TypRekorduZObiektem = record
Obiekt : array [1..5] of integer;
AdresNastepnegoObiektu: AdresObiektu
end;
var
BiezacyObiekt,PoprzedniObiekt: AdresObiektu;
AdresPomocniczy,AdresPierwszegoObiektu: Pointer;
I,J,LiczbaObiektowDoZapisu: integer;
Znak: char;
label
ZapisDoKolejki,OdczytIUsuniecieZKolejki,Menu;
begin
{naglowek}
ClrScr;
WriteLn('Program dydaktyczny Kolejka przedstawiajacy sposob generowania');
WriteLn('i odczytu dynamicznej struktury danych typu kolejka.');
WriteLn('Program zapisuje do kolejki, a nastepnie odczytuje i usuwa');
WriteLn('obiekt ktorym jest tablica 5 liczb typu integer. Liczbami tymi');
WriteLn('sa kolejne liczby naturalne.');
{menu}
Menu:
while KeyPressed do Znak:=ReadKey;
Writeln;
Write('Wybierz: zapis (Z), odczyt i usuniecie (O), koniec (K): ? ');
ReadLn(Znak);
if Znak='K'
then
Halt;
if Znak='Z'
then
goto ZapisDoKolejki
else
goto OdczytiUsuniecieZKolejki;
{zapis}
ZapisDoKolejki:
{ustalenie liczby obiektow zapisywanych do kolejki}
while KeyPressed do Znak:=ReadKey;
Write('Podaj liczbe obiektow ktore chcesz zapisac do kolejki: ? ');
ReadLn(LiczbaObiektowDoZapisu);
{petla glowna zapisu}
for I:=1 to LiczbaObiektowDoZapisu do
begin
{utworzenie nowego obiektu dynamicznego w kolejce}
New(BiezacyObiekt);
{wygenerowanie wartosci aktualnie zapisywanego obiektu}
for J:=1 to 5 do
BiezacyObiekt^.Obiekt[J]:=I;
{wizualizacja obiektu zapisywanego oraz adresu nowo utworzonego
obiektu dynamicznego}
Write('Num.obiektu: ',I:1,' Obiekt: ');
for J:=1 to 5 do
Write(BiezacyObiekt^.Obiekt[J]:4);
Writeln(' Adres obiektu: ',Seg(BiezacyObiekt^):5,':',
Ofs(BiezacyObiekt^):4);
{wygenerowanie adresu pustego jako adresu nastepnego obiektu
w kolejce}
BiezacyObiekt^.AdresNastepnegoObiektu:=nil;
{zapisanie adresu biezacego obiektu dynamicznego do rekordu obiektu
poprzedniego}
if I>1
then
begin
PoprzedniObiekt:=AdresPomocniczy;
PoprzedniObiekt^.AdresNastepnegoObiektu:=BiezacyObiekt;
end; {if I>1}
{zapamietanie adresu biezacego obiektu dynamicznego}
AdresPomocniczy:=BiezacyObiekt;
if I=1
then
AdresPierwszegoObiektu:=BiezacyObiekt;
{koniec petli glownej zapisu}
end; {for I:=1 to LiczbaObiektowDoZapisu do}
{powrot do menu}
goto Menu;
{odczyt i usuniecie obiektow z kolejki}
OdczytIUsuniecieZKolejki:
{ustalenie adresu pierwszego obiektu w kolejce}
AdresPomocniczy:=AdresPierwszegoObiektu;
{wizualizacja kolejki}
WriteLn;
Writeln('Zawartosc kolejki:');
{instrukcja iteracyjna wizualizacji i likwidacji kolejki}
repeat
{ustalenie bieacego adresu}
BiezacyObiekt:=AdresPomocniczy;
{odczyt i wizualizacja biezacego obiektu}
for J:=1 to 5 do
Write(BiezacyObiekt^.Obiekt[J]:4);
Writeln(' Adres obiektu: ',Seg(BiezacyObiekt^):5,':',
Ofs(BiezacyObiekt^):4);
{zapamietanie adresu nastepnego obiektu w kolejce}
AdresPomocniczy:=BiezacyObiekt^.AdresNastepnegoObiektu;
{likwidacja biezacego obiektu w kolejce}
Dispose(BiezacyObiekt);
{koniec iteracji}
until AdresPomocniczy=nil;
{powrot do menu}
goto Menu;
end. {program Kolejka}
{plik listadwu.pas}
{$A+,B-,D+,E+,F-,I+,L+,N-,O-,R-,S+,V+}
{$M 16384,0,655360}
program ListaDwukierunkowa;
{Program dydaktyczny przedstawiajacy sposob generowania, czytania i
likwidacji obiektow dynamicznej struktury danych typu lista
dwukierunkowa. Lista dwukierunkowa jest struktura do ktorej zapis moze
nastepowac zarowno na poczatku, na koncu jak i w srodku listy. To samo
dotyczy likwidacji obiektow na liscie. Obiektem zapisywanym do listy
moze byc dowolna zmienna prosta lub strukturalna.
W przypadku niniejszego programu obiektem jest tablica 5 liczb typu
integer, ktorymi sa kolejne liczby naturalne, tj. dla np. dla pewnego
obiektu na liscie 1,1,1,1,1, dla innego 2,2,2,2,2 itd.}
uses Crt;
type
AdresObiektu = ^TypRekorduZObiektem;
TypRekorduZObiektem = record
Obiekt : array [1..5] of integer;
AdresPoprzedniegoObiektu: AdresObiektu;
AdresNastepnegoObiektu: AdresObiektu
end;
var
BiezacyObiekt,SasiedniObiekt: AdresObiektu;
AdresPomocniczy,AdresPomocniczyDrugi,AdresPomocniczyTrzeci,
AdresPierwszegoObiektu: Pointer;
I,J,LiczbyWObiekcie,AktualnaLiczbaObiektow,NumerObiektuDoZapisu,
NumerObiektuDoUsuniecia: integer;
Znak: char;
WyskokDoMenu: boolean;
label
Menu,UstalenieNumeruObiektuDoZapisu,UstalenieNumeruObiektuDoUsuniecia;
procedure OdczytLiczbyObiektow(var LiczbaObiektow: integer);
var
AdresRoboczy: Pointer;
begin
LiczbaObiektow:=0;
AdresRoboczy:=AdresPierwszegoObiektu;
while AdresRoboczy<>nil do
begin
LiczbaObiektow:=LiczbaObiektow+1;
{wskazanie adresu nowego obiektu}
BiezacyObiekt:=AdresRoboczy;
{odczytanie adresu nastepnego obiektu na liscie}
AdresRoboczy:=BiezacyObiekt^.AdresNastepnegoObiektu
end {while AdresRoboczy<>nil do}
end; {procedure OdczytLiczbyObiektow}
procedure UstalenieAdresuObiektu(NumerObiektu: integer;
var AdresObiektu: Pointer);
var
LiczbaObiektow: integer;
AdresRoboczy: Pointer;
begin
AdresObiektu:=nil;
LiczbaObiektow:=0;
AdresRoboczy:=AdresPierwszegoObiektu;
while AdresRoboczy<>nil do
begin
LiczbaObiektow:=LiczbaObiektow+1;
if LiczbaObiektow=NumerObiektu
then
begin
AdresObiektu:=AdresRoboczy;
Exit
end; {if LiczbaObiektow=NumerObiektu}
{wskazanie adresu nowego obiektu}
BiezacyObiekt:=AdresRoboczy;
{odczytanie adresu nastepnego obiektu na liscie}
AdresRoboczy:=BiezacyObiekt^.AdresNastepnegoObiektu
end {while AdresRoboczy<>nil do}
end; {procedure OdczytLiczbyObiektow}
procedure WizualizacjaListy;
var
LiczbaObiektow: integer;
AdresRoboczy: Pointer;
begin
Writeln;
Writeln('Zawartosc listy:');
LiczbaObiektow:=0;
AdresRoboczy:=AdresPierwszegoObiektu;
while AdresRoboczy<>nil do
begin
LiczbaObiektow:=LiczbaObiektow+1;
{wskazanie adresu nowego obiektu}
BiezacyObiekt:=AdresRoboczy;
{odczyt i wizualizacja danych biezacego obiektu}
for J:=1 to 5 do
Write(BiezacyObiekt^.Obiekt[J]:4);
Writeln(' Adres obiektu: ',Seg(BiezacyObiekt^):5,':',
Ofs(BiezacyObiekt^):4);
{odczytanie adresu nastepnego obiektu na liscie}
AdresRoboczy:=BiezacyObiekt^.AdresNastepnegoObiektu
end {while AdresRoboczy<>nil do}
end; {procedure OdczytLiczbyObiektow}
procedure ZapisPierwszego;
begin
{utworzenie nowego obiektu dynamicznego na liscie}
New(BiezacyObiekt);
{zapis, do rekordu nowego obiektu, danych obiektu tj. kolejnych
5 liczb naturalnych}
LiczbyWObiekcie:=LiczbyWObiekcie+1;
for I:=1 to 5 do
BiezacyObiekt^.Obiekt[I]:=LiczbyWObiekcie;
{zapis, do rekordu nowego obiektu, adresu obiektu nastepnego
ktorym jest adres dotychczas pierwszego obiektu}
BiezacyObiekt^.AdresNastepnegoObiektu:=AdresPierwszegoObiektu;
{zapis, do rekordu nowego obiektu, adresu obiektu poprzedniego
ktorym jest adres pusty}
BiezacyObiekt^.AdresPoprzedniegoObiektu:=nil;
{modyfikacja zapisu - w rekordzie poprzednio pierwszego obiektu
na liscie - adresu obiektu poprzedniego ktorym jest adres
aktualnie pierwszego obiektu na liscie}
SasiedniObiekt:=AdresPierwszegoObiektu;
SasiedniObiekt^.AdresPoprzedniegoObiektu:=BiezacyObiekt;
{uaktualnienie adresu pierwszego obiektu na liscie}
AdresPierwszegoObiektu:=BiezacyObiekt;
{informacja pomocnicza}
Writeln('Nowy obiekt zapisano.');
end; {procedure ZapisPierwszego}
procedure ZapisOstatniego;
begin
{odczyt aktualnej liczby obiektow na liscie}
OdczytLiczbyObiektow(AktualnaLiczbaObiektow);
if AktualnaLiczbaObiektow=0
then
begin
{procedura jak przy zapisie pierwszego obiektu}
ZapisPierwszego;
{powrot do menu}
Exit;
end; {if AktualnaLiczbaObiektow=0}
{ustalenie adresu ostatniego obiektu na liscie tj. obiektu
o numerze porzadkowym AktualnaLiczbaObiektow i podstawienie
tego adresu pod zmienna AdresPomocniczy}
UstalenieAdresuObiektu(AktualnaLiczbaObiektow,AdresPomocniczy);
{utworzenie nowego obiektu dynamicznego na liscie}
New(BiezacyObiekt);
{zapis, do rekordu nowego obiektu, danych obiektu tj. kolejnych
5 liczb naturalnych}
LiczbyWObiekcie:=LiczbyWObiekcie+1;
for I:=1 to 5 do
BiezacyObiekt^.Obiekt[I]:=LiczbyWObiekcie;
{zapis, do rekordu nowego obiektu, adresu obiektu nastepnego
ktorym jest adres pusty}
BiezacyObiekt^.AdresNastepnegoObiektu:=nil;
{zapis, do rekordu nowego obiektu, adresu obiektu poprzedniego
ktorym jest adres dotychczas ostatniego obiektu}
BiezacyObiekt^.AdresPoprzedniegoObiektu:=AdresPomocniczy;
{modyfikacja zapisu - w rekordzie poprzednio ostatniego obiektu
na liscie - adresu obiektu nastepnego ktorym jest adres
aktualnie ostatniego obiektu na liscie}
SasiedniObiekt:=AdresPomocniczy;
SasiedniObiekt^.AdresNastepnegoObiektu:=BiezacyObiekt;
{informacja pomocnicza}
Writeln('Nowy obiekt zapisano.');
end; {ZapisOstatniego}
procedure LiczbaObiektow0Lub1;
begin
WyskokDoMenu:=False;
{odczyt aktualnej liczby obiektow na liscie}
OdczytLiczbyObiektow(AktualnaLiczbaObiektow);
{jesli aktualna liczba obiektow jest rowna 0 to wyjscie z opcji
i powrot do menu;
jesli 1 - to likwidacja tego obiektu i ustalenie adresu aktualnie
(tj. po likwidacji) pierwszego obiektu jako adresu pustego}
if AktualnaLiczbaObiektow=0
then
begin
Writeln('Lista pusta. Nie usunieto zadnego obiektu.');
WyskokDoMenu:=True;
Exit
end; {if AktualnaLiczbaObiektow=0}
if AktualnaLiczbaObiektow=1
then
begin
{lokalizacja i likwidacja obiektu dynamicznego na liscie}
BiezacyObiekt:=AdresPierwszegoObiektu;
Dispose(BiezacyObiekt);
AdresPierwszegoObiektu:=nil;
Writeln('Lista zawierala jeden obiekt. Obiekt usunieto.');
AktualnaLiczbaObiektow:=0;
WyskokDoMenu:=True;
Exit
end; {if AktualnaLiczbaObiektow=0}
end; {procedure LiczbaObiektow0Lub1}
procedure UsunieciePierwszego;
begin
{jesli aktualna liczba obiektow > 1 to lokalizacja drugiego obiektu
na liscie i podstawienie jego adresu pod zmienna AdresPomocniczy}
UstalenieAdresuObiektu(2,AdresPomocniczy);
{lokalizacja i likwidacja pierwszego obiektu dynamicznego na liscie}
BiezacyObiekt:=AdresPierwszegoObiektu;
Dispose(BiezacyObiekt);
{modyfikacja zapisu - w rekordzie poprzednio drugiego obiektu
na liscie - adresu obiektu poprzedniego ktorym jest teraz adres
pusty}
BiezacyObiekt:=AdresPomocniczy;
BiezacyObiekt^.AdresPoprzedniegoObiektu:=nil;
{uaktualnienie adresu pierwszego obiektu na liscie}
AdresPierwszegoObiektu:=BiezacyObiekt;
{informacja pomocnicza}
Writeln('Obiekt usunieto.');
end; {procedure UsunieciePierwszego}
procedure UsuniecieOstatniego;
begin
WyskokDoMenu:=False;
{kontrola liczby obiektow, procedury specjalne gdy liczba = 0 lub 1}
LiczbaObiektow0Lub1;
if WyskokDoMenu=True
then
Exit;
{ustalenie adresu ostatniego obiektu na liscie tj. obiektu
o numerze porzadkowym AktualnaLiczbaObiektow i podstawienie
tego adresu pod zmienna AdresPomocniczy}
UstalenieAdresuObiektu(AktualnaLiczbaObiektow,AdresPomocniczy);
{ustalenie adresu przedostatniego obiektu na liscie tj. obiektu
o numerze porzadkowym AktualnaLiczbaObiektow-1 i podstawienie
tego adresu pod zmienna AdresPomocniczyDrugi}
UstalenieAdresuObiektu(AktualnaLiczbaObiektow-1,AdresPomocniczyDrugi);
{lokalizacja i likwidacja ostatniego obiektu dynamicznego na liscie}
BiezacyObiekt:=AdresPomocniczy;
Dispose(BiezacyObiekt);
{modyfikacja zapisu - w rekordzie poprzednio przedostatniego
obiektu na liscie - adresu obiektu nastepnego ktorym jest teraz
adres pusty}
BiezacyObiekt:=AdresPomocniczyDrugi;
BiezacyObiekt^.AdresNastepnegoObiektu:=nil;
{informacja pomocnicza}
Writeln('Obiekt usunieto.');
end; {procedure UsuniecieOstatniego}
begin
{naglowek}
ClrScr;
WriteLn('Program dydaktyczny ListaDwukierunkowa przedstawiajacy sposob');
WriteLn('generowania, odczytu i likwidacji obiektow w dynamicznej ');
WriteLn('strukturze danych typu lista dwukierunkowa. Program zapisuje');
WriteLn('na liste, a nastepnie odczytuje i usuwa obiekt ktorym jest');
WriteLn('tablica 5 liczb typu integer. Liczbami tymi sa kolejne liczby');
Writeln('naturalne.');
{wartosci wyjsciowe}
AdresPierwszegoObiektu:=nil;
LiczbyWObiekcie:=0;
{menu}
Menu:
while KeyPressed do Znak:=ReadKey;
Write('Wybierz: zapis (Z), odczyt listy (O), usuniecie (U), koniec (K): ? ');
ReadLn(Znak);
case Znak of
{zapis okreslonego obiektu do listy}
'Z','z': begin {zapis}
while KeyPressed do Znak:=ReadKey;
Write('Zapis obiektu: pierwszego (P), ostatniego (O), innego (I), koniec (K): ? ');
ReadLn(Znak);
case Znak of
'P','p': begin {zapis obiektu jako pierwszego na liscie}
{wywolanie procedury zapisu}
ZapisPierwszego;
{powrot do menu}
goto Menu
{koniec opcji zapisu pierwszego obiektu na liscie}
end; {'P','p'}
'O','o': begin {zapis obiektu jako ostatniego na liscie}
{wywolanie procedury}
ZapisOstatniego;
{powrot do menu}
goto Menu
{koniec opcji zapisu ostatniego obiektu na liscie}
end; {'O','o'}
'I','i': begin {zapis obiektu jako posredniego na liscie}
{odczyt aktualnej liczby obiektow na liscie}
OdczytLiczbyObiektow(AktualnaLiczbaObiektow);
{okreslenie przez uzytkownika numeru porzadkowego nowego
obiektu na liscie}
UstalenieNumeruObiektuDoZapisu:
Write('Podaj numer obiektu do zapisu na liscie: ? ');
Readln(NumerObiektuDoZapisu);
if NumerObiektuDoZapisu>AktualnaLiczbaObiektow+1
then
begin
Writeln('Numer obiektu zbyt duzy. Aktualna liczba obiektow ',
AktualnaLiczbaObiektow:4,'. Podaj nowy numer.');
goto UstalenieNumeruObiektuDoZapisu
end; {if NumerObiektuDoZapisu>AktualnaLiczbaObiektow+1}
if NumerObiektuDoZapisu=1 {wariant rownowazny z zapisem obiektu
jako pierwszego}
then
begin
{wywolanie procedury zapisu}
ZapisPierwszego;
{powrot do menu}
goto Menu
end; {if NumerObiektuDoZapisu=1}
if NumerObiektuDoZapisu>AktualnaLiczbaObiektow
then
begin
{procedura jak dla ostatniego obiektu}
ZapisOstatniego;
{powrot do menu}
goto Menu
end; {if NumerObiektuDoZapisu>AktualnaLiczbaObiektow}
{numer nowego obiektu nie przekracza liczby obiektow}
{ustalenie adresu obiektu dotychczas figurujacego na pozycji
NumerObiektuDoZapisu-1 i podstawienie go pod zmienna
AdresPomocniczy - bedzie to poprzednik nowego obiektu}
UstalenieAdresuObiektu(NumerObiektuDoZapisu-1,AdresPomocniczy);
{ustalenie adresu obiektu dotychczas figurujacego na pozycji
NumerObiektuDoZapisu i podstawienie go pod zmienna
AdresPomocniczyDrugi - bedzie to nastepnik nowego obiektu}
UstalenieAdresuObiektu(NumerObiektuDoZapisu,AdresPomocniczyDrugi);
{utworzenie nowego obiektu dynamicznego na liscie}
New(BiezacyObiekt);
{zapis, do rekordu nowego obiektu, danych obiektu tj. kolejnych
5 liczb naturalnych}
LiczbyWObiekcie:=LiczbyWObiekcie+1;
for I:=1 to 5 do
BiezacyObiekt^.Obiekt[I]:=LiczbyWObiekcie;
{zapis, do rekordu nowego obiektu, adresu obiektu poprzedniego}
BiezacyObiekt^.AdresPoprzedniegoObiektu:=AdresPomocniczy;
{zapis, do rekordu nowego obiektu, adresu obiektu nastepnego}
BiezacyObiekt^.AdresNastepnegoObiektu:=AdresPomocniczyDrugi;
{modyfikacja zapisu - w rekordzie poprzednika - adresu obiektu
nastepnego ktorym jest adres nowego obiektu na liscie}
SasiedniObiekt:=AdresPomocniczy;
SasiedniObiekt^.AdresNastepnegoObiektu:=BiezacyObiekt;
{modyfikacja zapisu - w rekordzie nastepnika - adresu obiektu
poprzedniego ktorym jest adres nowego obiektu na liscie}
SasiedniObiekt:=AdresPomocniczyDrugi;
SasiedniObiekt^.AdresPoprzedniegoObiektu:=BiezacyObiekt;
{informacja pomocnicza}
Writeln('Nowy obiekt zapisano.');
{powrot do menu}
goto Menu
{koniec opcji zapis posredniego obiektu na liscie}
end; {'I','i'}
'K','k': goto Menu {koniec zapisow, powrot do menu glownego}
end {case Znak of}
{koniec opcji zapis w menu glownym}
end; {'Z','z'}
{wizualizacja na monitorze obiektow listy - wartosci oraz adresow obiektow}
'O','o': begin {odczyt i wizualizacja calosci listy}
WizualizacjaListy;
goto Menu
end; {'O','o'}
{usuniecie okreslonego obiektu z listy}
'U','u': begin {usuniecie}
while KeyPressed do Znak:=ReadKey;
Write('Usuniecie obiektu: pierwszego (P), ostatniego (O), innego (I), koniec (K): ? ');
ReadLn(Znak);
case Znak of
'P','p': begin {usuniecie obiektu pierwszego na liscie}
{kontrola liczby obiektow, procedury specjalne gdy liczba = 0 lub 1}
LiczbaObiektow0Lub1;
if WyskokDoMenu=True
then
goto Menu;
{wywolanie procedury usuniecia jesli liczba >1}
UsunieciePierwszego;
{powrot do menu}
goto Menu
{koniec opcji usuniecie pierwszego obiektu na liscie}
end; {'P','p'}
'O','o': begin {usuniecie obiektu ostatniego na liscie}
{wywolanie procedury usuniecia}
UsuniecieOstatniego;
{powrot do menu}
goto Menu
{koniec opcji usuniecia ostatniego obiektu na liscie}
end; {'O','o'}
'I','i': begin {usuniecie obiektu posredniego na liscie}
{kontrola liczby obiektow, procedury specjalne gdy liczba = 0 lub 1}
LiczbaObiektow0Lub1;
if WyskokDoMenu=True
then
goto Menu;
{okreslenie przez uzytkownika numeru porzadkowego obiektu na liscie
do usuniecia}
UstalenieNumeruObiektuDoUsuniecia:
Write('Podaj numer obiektu na liscie do usuniecia: ? ');
Readln(NumerObiektuDoUsuniecia);
if NumerObiektuDoUsuniecia>AktualnaLiczbaObiektow
then
begin
Writeln('Numer obiektu zbyt duzy. Aktualna liczba obiektow ',
AktualnaLiczbaObiektow:4,'. Podaj nowy numer.');
goto UstalenieNumeruObiektuDoUsuniecia
end; {if NumerObiektuDoUsuniecia>AktualnaLiczbaObiektow}
if NumerObiektuDoUsuniecia=1
{podany numer jest numerem pierwszego obiektu}
then
begin
{procedura jak dla usuniecia pierwszego obiektu}
UsunieciePierwszego;
{powrot do menu}
goto Menu
end; {if NumerObiektuDoUsuniecia=1}
if NumerObiektuDoUsuniecia=AktualnaLiczbaObiektow
{podany numer jest numerem ostatniego obiektu}
then
begin
{procedura jak dla usuniecia ostatniego obiektu}
UsuniecieOstatniego;
{powrot do menu}
goto Menu
end; {if NumerObiektuDoUsuniecia=AktualnaLiczbaObiektow}
{numer obiektu do usuniecia mniejszy niz liczba obiektow}
{ustalenie adresu poprzednika obiektu do usuniecia tj. figurujacego
na pozycji NumerObiektuDoUsuniecia-1 i podstawienie go pod zmienna
AdresPomocniczy}
UstalenieAdresuObiektu(NumerObiektuDoUsuniecia-1,AdresPomocniczy);
{ustalenie adresu nastepnika obiektu do usuniecia tj. figurujacego
na pozycji NumerObiektuDoUsuniecia+1 i podstawienie go pod zmienna
AdresPomocniczyDrugi}
UstalenieAdresuObiektu(NumerObiektuDoUsuniecia+1,AdresPomocniczyDrugi);
{ustalenie adresu obiektu do usuniecia tj. figurujacego na pozycji
NumerObiektuDoUsuniecia i podstawienie go pod zmienna
AdresPomocniczyTrzeci}
UstalenieAdresuObiektu(NumerObiektuDoUsuniecia,AdresPomocniczyTrzeci);
{likwidacja obiektu}
BiezacyObiekt:=AdresPomocniczyTrzeci;
Dispose(BiezacyObiekt);
{modyfikacja zapisu - w rekordzie poprzednika obiektu do usuniecia
- adresu obiektu nastepnego ktorym jest teraz adres nastepnika}
BiezacyObiekt:=AdresPomocniczy;
BiezacyObiekt^.AdresNastepnegoObiektu:=AdresPomocniczyDrugi;
{modyfikacja zapisu - w rekordzie nastepnika obiektu do usuniecia
- adresu obiektu poprzedniego ktorym jest teraz adres poprzednika}
BiezacyObiekt:=AdresPomocniczyDrugi;
BiezacyObiekt^.AdresPoprzedniegoObiektu:=AdresPomocniczy;
{informacja pomocnicza}
Writeln('Obiekt usunieto.');
{powrot do menu}
goto Menu
{koniec opcji usuniecie posredniego obiektu na liscie}
end; {'I','i'}
{koniec opcji usuniecie}
'K','k': goto Menu {koniec usuniec, powrot do menu glownego}
end; {case Znak of}
{koniec opcji usuniecie w menu glownym - powrot do menu glownego}
goto Menu
end; {'U','u'}
{koniec przebiegu}
'K','k': Halt;
end; {case Znak of}
end. {program ListaDwukierunkowa}