Stos w jezyku C++ - Instytut Sterowania i Systemów Informatycznych
Transkrypt
Stos w jezyku C++ - Instytut Sterowania i Systemów Informatycznych
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 strukturalnego • Obecność wskaźnika głównego na element struktury • Dodawanie nowego elementu do struktury • Usuwanie elementu ze struktury 1.1 Definicja typu strukturalnego 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 W bardziej zaawansowanych strukturach takich wskaźników może być więcej, niż jeden. Definicja typu strukturalnego, służącego do przechowywania liczb typu int w najprostszym przypadku ma zatem postać struct TTypStrukturalny { int DANE ; TTypStrukturalny * Nastepny ; }; 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 (typu TTypStrukturalny), deklarowany jako zwykła zmienna (niedynamiczna). Wskaźnik taki jest zwykłym wskaźnikiem na typ TTypStrukturalny, dlatego tworzony jest w następujący sposób: TTypStrukturalny * Wierzcholek ; 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. Wskaźnik Śmieci 1.3 Tworzenie elementarnej stuktury: Każdą tworzoną dynamicznie strukturę należy utworzyć przy pomocy instrukcji Wierzcholek = new TTypStrukturalny ; Wskaźnik 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ść NULL. 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. Wierzcholek - > Nastepny = NULL ; 2 Wskaźnik DANE Następny 1 NULL 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. Aby usunąć element o wartości w Wartość NULL 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 NULL Rysunek 1: Gotowy stos 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 Wierzcholek = new TTypStrukturalny ; Wierzcholek - > DANE = 1; Wierzcholek - > Nastepny = NULL ; To już jest stos, tyle, że nieco mało użyteczny. . . . Wskaźnik DANE Następny 1 3 NULL 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 TTypStrukturalny), która zostanie dodany do stosu. Aby to uczynić niezbędne jest zadeklarowanie pomocniczego wskaźnika Tmp TTypStrukturalny * Tmp ; Tmp Śmieci Przy pomocy wskaźnika Tmp tworzona jest nowa zmienna dynamiczna Tmp = new TTypStrukturalny ; Tmp - > DANE = 2; Tmp - > Nastepny = NULL ; // To przypisanie w zasadzie nie jest // konieczne , gdyż zaraz Tmp - > Nastepny // zostanie zmieniony Wierzcholek DANE Następny Tmp DANE Następny 2 1 NULL NULL Następnie można połączyć tę nową ”cegiełkę” z resztą stosu: Tmp - > Nastepny = Wierzcholek ; Tmp DANE Następny Wierzcholek 2 DANE Następny 1 NULL Ponieważ w przypadku stosu KONIECZNE jest zapewnienie, aby Wierzcholek 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 ; 4 Wierzcholek DANE Następny 2 DANE Następny 1 NULL (Gotowy stos; Zmienna Tmp nie jest już potrzebna i można ją wykorzystać do innych celów) 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 Tmp = new TtypStrukturalny ; Tmp - > DANE = 3; Wierzcholek DANE Następny Tmp DANE Następny 2 DANE Następny 1 NULL 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 NULL • przeniesienie wskaźnika Wierzcholek na wierzcholek stosu. Wierzcholek = Tmp ; Wierzcholek Tmp DANE Następny 3 DANE Następny 5 2 DANE Następny 1 NULL Wskaźniki Wierzcholek oraz Tmp wskazują na tę samą zmienną dynamiczną. Wierzcholek DANE Następny 3 DANE Następny 2 DANE Następny 1 NULL 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 NULL: Tmp = NULL ; Tmp NULL Dodawanie pozostałych elementów następuje analogicznie. 3 Usuwanie elementów ze stosu Przy usuwaniu elementu ze stosu również niezbędne będzie użycie pomocniczego wskaźnika Tmp: TTypStrukturalny * Tmp ; 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: printf ( " % d " , 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: int wartoscZwracana ; /* ........ */ wartoscZwracana = Wierzcholek - > DANE ; /* usuwanie elementu */ return wartoscZwracana ; Przy pomocy zmiennej tymczasowej Tmp należy przejść na następny element stosu: Tmp = Wierzcholek - > Następny ; 6 Tmp Wierzcholek DANE Następny 3 DANE Następny 2 DANE Następny 1 NULL 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 NULL. 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!!!). Wierzchołek - > Następny = NULL ; Tmp DANE Następny Wierzcholek DANE Następny 3 2 DANE Następny 1 NULL NULL Pojedyncza zmienna dynamiczna, na którą wskazuje wskaźnik Wierzcholek została ”odcięta” od reszty stosu. Na pozostałą część stosu wskazuje zmienna Tmp Teraz można już usunąć element na wierzchołku stosu: delete 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) lub 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 Wierzcholek dowolnej wartości (np. Wierzcholek=NULL). Po wykonaniu instrukcji delete Wierzcholek; wskaźnik Wierzcholek wskazuje na bezużyteczne dane: Tmp DANE Następny Wierzcholek 2 Śmieci 7 DANE Następny 1 NULL Ponieważ wskaźnik Wierzcholek 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 1 NULL Usuwanie pozostałych elementów realizuje się analogicznie. 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,3,5,7,9,11,13,15,17,19. Następnie usunąć wszystkie elementy. Po usunięciu każdego z nich wypisać wartość liczby na ekranie. 8