Temat: Zmienne dynamiczne – tablica wskaźników i stos

Transkrypt

Temat: Zmienne dynamiczne – tablica wskaźników i stos
Instrukcja
Podstawy programowania 2
laboratoryjna
Temat: Zmienne dynamiczne – tablica wskaźników
i stos dynamiczny
2
Przygotował: mgr inż. Tomasz Michno
1 Wstęp teoretyczny
1.1 Tablice wskaźników
Tablice wskaźników, są zwykłymi tablicami, które zamiast zmiennych przechowują
wskaźniki. Sposób ich deklaracji jest identyczny do deklaracji zwykłych tablic (typ należy
poprzedzić znakiem ^):
nazwa : Array[od..do] of ^typ_wskaźnika;
Tablice wskaźników mogą być wykorzystane, np. gdy mamy zamiar przechowywać dane, które
mogą zająć dużo pamięci operacyjnej lub nie będą potrzebne przez cały czas działania programu.
1.2 Stos
Stos jest jedną z najprostszych i najczęściej wykorzystywanych struktur danych, służy do
przechowywania danych. Dane są w nim odkładane „na sobie”, co powoduje że można pobrać
jedynie element znajdujący się na szczycie stosu (tzn. aby pobrać element znajdujący się na dnie
stosu, musimy zdjąć wszystkie elementy).
Odkładanie danych na stos (Instrukcja push):
Przypuśćmy, że chcemy odłożyć na stosie 3 elementy: A, 0 i @.
1. Stos jest pusty. Odkładamy element A (pierwsza instrukcja push)
A
Stos
2. Odkładamy element 0 (druga instrukcja push)
0
@
0
A
@
Stos
3. Odkładamy element @ (trzecia instrukcja push):
0
A
@
Stos
Stos po odłożeniu wszystkich trzech elementów (można zauważyć, że dane są ułożone „na sobie”):
@
0
A
Stos
Pobieranie ze stosu (instrukcja pop):
Chcemy teraz pobrać ze stosu element 0, zostawiając na nim resztę elementów. Ponieważ jest to
stos, mamy jedynie dostęp do elementu znajdującego się na szczycie stosu. Dlatego w celu pobrania
elementu 0 musimy najpierw zdjąć ze stosu element @.
1. Zdejmujemy ze stosu element @ (instrukcja pop), który w przypadku programu należałoby
zapamiętać np. w zmiennej tymczasowej:
0
A
Stos
@
2. Zdejmujemy ze stosu element 0 – element o który nam chodziło (instrukcja pop):
0
A
@
Stos
3. Ponieważ chcemy resztę elementów stosu pozostawić bez zmian, musimy na niego
z powrotem odłożyć element @ (instrukcja push):
A
@
Stos
Stos po wszystkich operacjach wygląda następująco:
@
A
Stos
Jak można było zauważyć, w stosie nie ma możliwości bezpośredniego dostępu do danych
znajdujących się pod szczytowym elementem.
Szczegóły implementacyjne:
Stos można zaimplementować na dwa sposoby: używając tablicy lub używając specjalnie
stworzonej struktury dynamicznej.
Stos oparty o tablicę:
Stos oparty o tablicę jest najprostszą implementacją stosu, posiada jednak wadę w postaci
stałego rozmiaru (którym jest rozmiar tablicy) oraz gorszego zarządzania pamięcią w porównaniu
z w pełni dynamicznym stosem (tablica łącznie ze wszystkimi jej elementami zajmuje pamięć
nawet gdy stos jest pusty). Struktura takiego stosu jest bardzo prosta – składa się z tablicy oraz
zmiennej przechowującej indeks ostatnio dodanego elementu (szczytu stosu). Na przykładzie 1
został przedstawiony stos przechowujący znaki. Dla większej czytelności i wygody używania został
zadeklarowany typ rekordowy STOS, w którego środku znajduje się tablica i zmienna wskazująca
na szczyt stosu. Pełny kod przykładu został przedstawiony w oddzielnym pliku.
Przykład 1.
type
STOS = record
stosTab
: Array [1..STOS_MAX] of char;
{Na stosie bedziemy
przechowywali znaki}
szczytStosu : integer; {przechowuje indeks elementu, ktory jest na
szczycie stosu, 0 oznacza, ze stos jest pusty}
end;
Działanie instrukcji push() dla stosu tablicowego:
1. sprawdź, czy nie osiągnięto maksymalnej pojemności (STOS_MAX) (jeśli tak, zakończ)
2. zwiększ wartość zmiennej szczytStosu (jest to indeks w tablicy)
3. zapamiętaj w stosTab[szczytStosu] wartość zmiennej do odłożenia na stos.
Działanie instrukcji pop() dla stosu tablicowego:
1. sprawdź, czy stos nie jest pusty (sprawdź, czy wartość zmiennej szczytStosu nie jest
mniejsza niż minimalny indeks w tablicy – w tym przykładzie wartość 1) (jeśli tak, zakończ)
2. zapamiętaj w dodatkowej zmiennej wartość stosTab[szczytStosu]
3. zmniejsz wartość zmiennej szczytStosu o 1
Wynik działania:
Na rysunku można zauważyć typowe działanie stosu – na stos odłożyliśmy napis PASCAL,
natomiast po zdjęciu go ze stosu otrzymaliśmy odwrócenie: LACSAP. Jest to jeden z przykładów
zastosowania stosu – odwracanie napisów lub tablic. Maksymalny rozmiar stosu został ustawiony
na 6 elementów, dlatego znak '!' nie mógł być już dodany.
Stos w wersji dynamicznej:
Stos budowany w oparciu o struktury dynamiczne jest zazwyczaj o wiele lepszym
rozwiązaniem niż stos stworzony z użyciem tablicy. Dzięki nieznacznie bardziej skomplikowanej
implementacji otrzymujemy dużo większą kontrolę nad zużyciem pamięci (gdy stos jest pusty
praktycznie nie zajmuje pamięci). Dodatkowo wielkość stosu jest ograniczona jedynie poprzez ilość
wolnej pamięci, w przeciwieństwie do wersji tablicowej. W przykładzie 2 została przedstawiona
struktura stosu przechowującego znaki (podobnie jak w poprzednim przykładzie został stworzony
nowy typ). Pełny kod przykładu został przedstawiony w oddzielnym pliku.
Przykład 2.
type
WSKDSTOS = ^DSTOS;
DSTOS = record
{typ rekordowy stosu}
znak : char;
{przechowywany typ - znaki}
wskPonizej : WSKDSTOS;
{wskaznik na element znajdujacy sie
ponizej w stosie}
end;
Typ DSTOS jest zwykłym rekordem, który służy do przechowywania pojedynczego elementu
stosu. Każdy element składa się z dwóch zmiennych: przechowywanego znaku oraz wskaźnika
na element znajdujący się bezpośrednio pod nim (w przypadku tablicy byłby to element o indeksie
mniejszym o 1). Dzięki wskaźnikowi otrzymujemy powiązanie pomiędzy wszystkimi elementami
odłożonymi na stosie bez użycia tablicy. Jak można zauważyć na poniższym rysunku, pod
elementem znajdującym się na dnie stosu nie ma już żadnego innego elementu i jego wskaźnik ma
wartość nil1.
@
wskaźnik
0
wskaźnik
A
wskaźnik
nil
Stos
Oznaczenia:
szczyt stosu – wskaźnik na element, który znajduje się na szczycie stosu. Dzięki zaproponowanej
strukturze jest to jedyny wskaźnik, jaki musimy posiadać dla stosu, aby z niego korzystać.
Działanie instrukcji push():
1. utwórz nowy element
2. zapisz w element^.znak znak do odłożenia na stos
3. ustaw wskaźnik element^.wskPonizej na aktualny szczyt stosu
4. przypisz do wskaźnika szczyt stosu wskaźnik na nowo utworzony element
Działanie instrukcji pop() dla stosu tablicowego:
1. sprawdź, czy stos nie jest pusty (sprawdź czy szczyt stosu nie jest równy nil) (jeśli tak,
zakończ)
2. odczytaj/zapamiętaj w dodatkowej zmiennej wartość znaku z szczyt stosu^.znak
3. zapamiętaj wskaźnik na element poniżej szczytu stosu (szczyt stosu^.wskPonizej)
4. usuń element wskazywany przez szczyt stosu (Dispose(szczyt stosu))
5. do wskaźnika szczyt stosu przypisz zapamiętany wskaźnik z punktu 3.
Wynik działania programu jest praktycznie identyczny do wyniku z przykładu 1. Jedyną różnicą
jest to, że było możliwe dodanie znaku '!' (dzięki rezygnacji z tablicy i jej stałego rozmiaru).
1 nil jest to wartość w języku Pascal, która informuje że wskaźnik na nic nie wskazuje
2 Zadania
1.
Uruchom przykładowe programy i prześledź sposób ich działania. Napisz z wykorzystaniem
stosu dynamicznego program, który będzie odwracał ciąg liczb podanych przez
użytkownika (aplikacja ma wyświetlać odwrócony ciąg).
2.
Dany jest zestaw informacji o n studentach: nazwisko, imię, rok urodzenia, średnia ocen.
Napisać program, który umożliwi umieszczenie tych danych w pamięci komputera w taki
sposób, że dostęp do nich będzie się odbywał za pośrednictwem tablicy zawierającej
wskaźniki do informacji o poszczególnych osobach.
3.
Uzupełnić program z punktu 1 o funkcję wyznaczającą średnią arytmetyczną ocen
wszystkich wprowadzonych studentów oraz o procedurę uwalniającą pamięć zajętą przez
dane studentów. Sprawdzić dostępność danych po odblokowaniu przypisanej im pamięci.
4.
Zmodyfikować rozwiązanie zadania z punktu 1 poprzez wykorzystanie stosu dynamicznego
do przechowywania danych.
5.
Uzupełnić program z punktu 3 o procedurę wyświetlającą zawartość stosu bez jego
likwidacji.

Podobne dokumenty