1 Przebieg ćwiczenia - Instytut Sterowania i Systemów

Transkrypt

1 Przebieg ćwiczenia - Instytut Sterowania i Systemów
Uniwersytet Zielonogórski
Instytut Sterowania i Systemów Informatycznych
Ćwiczenie 3 — stos
Laboratorium Metod i Języków Programowania
Celem ćwiczenia jest zapoznanie studentów z najprostszą dynamiczną strukturą danych:
stosem.
1
Przebieg ćwiczenia
Uwaga: Typy danych przechowywanych w dowolnego rodzaju dynamicznych strukturach danych są oczywiście dowolne (np. liczby całkowite itd. ).
Następujące operacje są wspólne dla większości dynamicznych struktur danych, nie tylko
dla stosów:
• Definicja typu rekordowego
• Obecność wskaźnika głównego na element struktury
• Dodawanie nowego elementu do struktury
• Usuwanie elementu ze struktury
1.1
Definicja typu rekordowego
Dynamiczne struktury danych składają się z połączonych ze sobą przy pomocy wskaźników
zmiennych dynamicznych. Dodatkowo każda zmienna dynamiczna przechowuje dane (co jest
podstawowym przeznaczeniem dynamicznej struktury danych). Z tego powodu każda struktura
dynamiczna posiada co najmniej:
• Składową przechowującą dane
• Wskaźnik na (następny) element struktury dynamicznej
W bardziej zaawansowanych strukturach takich wskaźników może być więcej, niż jeden.
Definicja typu rekordowego, służącego do przechowywania liczb typu integer w najprostszym
przypadku ma zatem postać
type
T W s k aznikNaStrukture = ^ TTypStruktury ;
TTypStruktury = record
DANE : integer ;
Nastepny : TWskaznikNaSt r u k t u r e ;
end ;
1
1.2
Tworzenie wskaźnika głównego
Przed rozpoczęciem tworzenia konieczne jest określenie sposobu odwoływania się do dynamicznej struktury danych. W tym celu najczęściej wykorzystuje się zwykły wskaźnik do struktury (zmienną typu TWskaznikNaStrukture), deklarowany jako zwykła zmienna (niedynamiczna).
Wskaźnik taki jest zwykłym wskaźnikiem na typ TWskaznikNaStrukture, dlatego tworzony jest w
następujący sposób:
var Wierzcholek : TWskaznikN a S tr u k t u r e ;
Należy zauważyć, że wskaźnik ten nie wskazuje na żadne użyteczne dane. Z tego powodu w tym
momencie odwołanie się do tego, na co wskazuje ten wskaźnik jest błędne. Zostanie on użyty
do utworzenia nowej zmiennej dynamicznej.
Wierzcholek
Śmieci
1.3
Tworzenie elementarnej stuktury:
Każdą tworzoną dynamicznie strukturę należy utworzyć przy pomocy instrukcji
new ( Wierzcholek );
Wierzcholek
DANE
Następny
??
Śmieci
Następnie należy przypisać polom nowoutworzonej struktury dynamicznej określone dane
(w przykładzie jest to liczba 1).
Wierzcholek ^. DANE :=1;
Wierzcholek
DANE
Następny
1
Śmieci
Dodatkowo można również przypisać wskaźnikowi Wierzcholek^.Nastepny wartość NIL. Stanowi
to dodatkowe zabezpieczenie przed popełnieniem błędu przez programistę podczas dalszego
tworzenia dynamicznej struktury danych. W przypadku stosu linia ta nie jest jednak konieczna,
gdyż w następnym kroku jego tworzenia wskaźnik ten wskaże na następny element stosu.
2
Wierzcholek ^. Nastepny := NIL ;
Wierzcholek
DANE
Następny
1
NIL
Jedną z najprostszych w implementacji dynamicznych struktur danych jest stos.
2
Tworzenie stosu
Stos jest dynamiczną strukturą danych, w której dołączanie i usuwanie elementów zachodzi
tylko w jednym miejscu: na wierzchołku. Stos można wyobrazić sobie jako stos książek:
chcąc wyjąć jedną, należy wcześniej wyjąć wszystkie znajdujące się powyżej: Wskaźnik główny
(Wierzcholek) ma zawsze wskazywać na wierzchołek stosu.
Wartość NIL oznaczająca koniec stosu
Wskaźnik wskazujący zawsze
na wierzchołek stosu
Dodawanie nowych danych
Usuwanie danych
Wierzcholek
DANE
Następny
3
DANE
Następny
2
DANE
Następny
1
NIL
Rysunek 1: Gotowy stos
Aby usunąć element o wartości w polu DANE równej 2 należy najpierw usunąć wszystkie
elementy znajdujące się przed usuwanym elementem (w przykładzie będzie to element o wartości
3 w polu DANE).
2.1
Tworzenie pierwszego elementu
new ( Wierzcholek );
Wierzcholek ^. DANE := 1;
Wierzcholek ^. Nastepny := NIL ;
To już jest stos, tyle, że nieco mało użyteczny. . . .
Wierzcholek
DANE
Następny
1
3
NIL
2.2
Następne elementy
Od teraz dodawanie każdego nowego elementu będzie przebiegało identycznie:
Najpierw należy utworzyć elementarną ”cegiełkę” — nową zmienną dynamiczną (typu
TTypStruktury), która zostanie dodany do stosu. Aby to uczynić niezbędne jest zadeklarowanie pomocniczego wskaźnika Tmp
var Tmp : TWskaznikNaStruktu r e ;
Tmp
Śmieci
Przy pomocy wskaźnika Tmp tworzona jest nowa zmienna dynamiczna
new ( Tmp );
Tmp ^. DANE := 2;
Tmp ^. Nastepny := NIL ; { To przypisanie w zasadzie nie jest
konieczne , gdyż zaraz Tmp ^. Nastepny
zostanie zmieniony }
Wierzcholek
DANE
Następny
Tmp
DANE
Następny
2
1
NIL
NIL
Następnie można połączyć tę nową ”cegiełkę” z resztą stosu:
Tmp ^. Nastepny := Wierzcholek ;
Tmp
Wierzcholek
DANE
Następny
2
DANE
Następny
1
NIL
Ponieważ w przypadku stosu KONIECZNE jest zapewnienie, aby Wierzchołek wskazywał
na rzeczywisty wierzchołek stosu, dlatego należy go przesunąć. Przy tej operacji wystarczy
zauważyć, że wskaźnik Tmp wskazuje na wierzchołek stosu.
Wierzcholek := Tmp ;
Wierzcholek
DANE
Następny
2
DANE
Następny
1
NIL
(Gotowy stos; Zmienna Tmp nie jest już potrzebna i można ją wykorzystać do innych celów)
4
2.3
Dodawanie nowego elementu do stosu o dwóch elementach
Należy zauważyć, że dodawanie danych do stosu o dowolnym rozmiarze (od pustego stosu aż do
utworzenia potrzebnej liczby elementów) przebiega zawsze w ten sam, omówiony w poprzednich
podpunktach, sposób:
• utworzenie nowej zmiennej dynamicznej
new ( Tmp );
Tmp ^. DANE := 3;
Wierzcholek
DANE
Następny
Tmp
DANE
Następny
2
DANE
Następny
1
NIL
3
Śmieci
• podpięcie jej na wierzchołek stosu:
Tmp ^. Nastepny := Wierzcholek ;
Tmp
Wierzcholek
DANE
Następny
3
DANE
Następny
2
DANE
Następny
1
NIL
• przeniesienie wskaźnika Wierzcholek na wierzcholek stosu.
Wierzcholek := Tmp ;
Wierzcholek
Tmp
DANE
Następny
3
DANE
Następny
2
DANE
Następny
1
NIL
Gotowy stos. Zmienna Tmp nie jest już potrzebna i można ją wykorzystać do innych celów. Aby
ułatwić sprawdzanie błędów należy jednak ustawić ten wskaźnik na NIL:
Tmp := NIL ;
Tmp
NIL
Dodawanie pozostałych elementów następuje analogicznie.
5
3
Usuwanie elementów ze stosu
Przy usuwaniu elementu ze stosu również niezbędne będzie użycie pomocniczego wskaźnika Tmp:
var Tmp : TWskaznikNaStrukture ;
Tmp
Śmieci
Przed usunięciem elementu należy odczytać przechowywaną przez niego (w polu DANE) wartość
— po usunięciu zmiennej dynamicznej nie będzie to możliwe. Jako przykład wykorzystania
wartości w tym polu przyjmijmy jego wypisanie na ekranie:
writeln ( Wierzcholek ^. DANE );
Najczęściej usuwanie danych ze stosu realizowanej jest przy pomocy funkcji zwracającej usunięty element. Z tego powodu najczęściej wpisuje się Wierzcholek^.DANE do zmiennej pomocniczej,
której wartość używana jest jako wartość zwracana z funkcji:
function UsunZeStosu ( var Wierzcholek : T W s k a z n i k N a St r u k t u r e ) : Integer ;
var wartoscZwracana : Integer ;
{ ........ }
wartoscZwracana := Wierzcholek ^. DANE ;
{ usuwanie elementu }
UsunZeStosu := wartoscZwracana ;
Przy pomocy zmiennej tymczasowej Tmp należy przejść na następny element stosu:
Tmp := Wierzcholek ^. Następny ;
Tmp
Wierzcholek
DANE
Następny
3
DANE
Następny
2
DANE
Następny
1
NIL
Przygotowanie danych do usunięcia.
Zmienna dynamiczna na którą wskazuje wskaźnik Wierzcholek zostanie za chwilę usunięta. Dla
ułatwienia sprawdzania błędów można najpierw ustawić wskaźnik Wierzcholek^.Następny (na
poniższym rysunku oznaczony kolorem czerwonym) na NIL.
UWAGA: PRZED ”odcięciem” zmiennej dynamicznej, na którą wskazuje wskaźnik Wierzcholek
od reszty stosu KONIECZNE jest zapewnienie, że na resztę struktury wskazuje jakiś inny
wskaźnik — tutaj Tmp. Jeśli tak nie będzie, wtedy NIE MA MOŻLIWOŚCI NAPRAWY TEGO
BŁĘDU — STRUKTURA ZOSTANIE NIEODWRACALNIE UTRACONA!!!).
Wierzcholek ^. Następny := NIL ;
6
Tmp
DANE
Następny
Wierzcholek
DANE
Następny
3
2
DANE
Następny
1
NIL
NIL
Pojedyncza zmienna dynamiczna, na którą wskazuje wskaźnik Wierzchołek została ”odcięta” od
reszty stosu. Na pozostałą część stosu wskazuje zmienna Tmp
Teraz można już usunąć element na wierzchołku stosu:
dispose ( Wierzcholek ); { Usunięcie zmiennej dynamicznej , na którą wskazuje
wskaźnik Wierzcholek . }
Po wykonaniu tej linii, wskaźnik Wierzcholek NIE wskazuje na żaden użyteczny obszar pamięci.
Napisanie więc Wierzcholek^.cokolwiek jest BŁĘDNE).
Można jednak oczywiście nadal zmieniać wartość wskaźnika (to, na co ma wskazywać
wskaźnik). Poprawne jest więc przypisanie wskaźnikowi Wierzchołek dowolnej wartości (np.
Wierzchołek:=NIL). Po wykonaniu instrukcji dispose(Wierzcholek); wskaźnik Wierzchołek wskazuje na bezużyteczne dane:
Tmp
DANE
Następny
Wierzcholek
2
DANE
Następny
1
NIL
Śmieci
Ponieważ wskaźnik Wierzchołek powinien zawsze wskazywać na wierzchołek stosu, dlatego konieczne jest jego ponowne ustawienie na wierzchołek pozostałej części stosu (na którą aktualnie
wskazuje wskaźnik Tmp):
Wierzcholek := Tmp ;
Teraz Wierzcholek znowu wskazuje na użyteczną zmienną dynamiczną - na początek stosu.
Zmienna TMP nie jest już potrzebna i może zostać użyta do innych celów.
Wierzcholek
DANE
Następny
2
DANE
Następny
Usuwanie pozostałych elementów realizuje się analogicznie.
7
1
NIL
4
Zadania
1. Zaimplementować funkcję służącą do dodawania elementów do stosu
2. Zaimplementować funkcję służącą do usuwania elementów stosu. W przypadku próby
usunięcia elementu z pustego stosu należy wypisać komunikat o błędzie.
3. Przy pomocy funkcji z p. 1 i 2, dodać do stosu po kolei liczby: 1,2,3,4,5,6,7,8,9,10. Następnie usunąć wszystkie elementy. Po usunięciu każdego z nich wypisać wartość liczby
na ekranie.
4. Zaimplementować program służący do wyznaczania wartości wyrażenia arytmetycznego
podanego w formie ONP (Odwrotnej Notacji Polskiej). Do wykonania zadania użyć stosu.
Założyć, że wyrażenie jest:
• Podane z klawiatury jako łańcuch znaków
• Wczytywane z pliku tekstowego
8