Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych Metody i języki programowania 1 Wprowadzenie Lista 5 Typy dynamiczne kolejka Kolejka jest jedną z podstawowych struktur umożliwiających przechowywanie i przetwarzanie danych w pewien przyjęty sposób. Kolejki są powszechnie wykorzystywane we współczesnych systemach komputerowych. Przykładem sprzętowym może być obsługa instrukcji programu przez procesor. W warstwie oprogramowania kolejki są powszechnie stosowane w systemach realizujących obsługę zamówień. Najprostszym przykładem kolejki jest oczywiście wykorzystanie takiej struktury przez klientów sklepów. Samoczynnie ustawiają się oni w kolejce chcąc zapewnić każdemu sprawiedliwie przydzielony czas oczekiwania na obsługę. 2 Zasady tworzenia i obsługi kolejki Budowa kolejki opiera się na prostych założeniach. 1. Elementy do kolejki dodawane są tylko na jej końcu. Element dodany do kolejki staje się automatycznie jej ostatnim elementem. 2. Jedynie element znajdujący się na początku kolejki może być obsłużony. Oznacza to jego automatyczne usunięcie z kolejki. 3. Elementy są uszeregowane w taki sposób iż kolejność obsługi wynika z miejsca położenia. Kolejka jest więc strukturą FIFO (First Input First Output). Rysunek 1: Przykładowa kolejka. T oznacza czas wstawiewienia elementu do kolejki. 1
3 Tworzenie kolejki na podstawie struktur dynamicznych Kolejka składa się z uporządkowanych elementów. Podobnie jak w stosach elementy te zawierają wskaźnik na obiekty strukturalne typu takiego samego jak ten, do którego należą. Definicja typu rekordowego wykorzystywanego w kolejce może być więc identyczna z definicją typu elementu wchodzącego w skład stosu. W przypadku przechowywania w kolejce liczb typu integer ma ona postać: type TWskaznikNaStrukture = ˆ TTypStruktury ; TTypStruktury = record DANE : i n t e g e r ; Nastepny : TWskaznikNaStrukture ; end ; 3.1 Tworzenie wskaźników początku i końca kolejki Podobnie jak w przypadku stosu, do poprawnej obsługi kolejki wystarczyłby by tylko jeden wskaźnik. W przypadku kolejki pokazanej na rys. 1 byłby to wskaźnik na początek kolejki. W oczywisty sposób wskaźnik ten jest wystarczający w przypadku usunięcia pierwszego elementu z kolejki. Jeżeli chcielibyśmy dodać element do kolejki należałoby przejść przez wszystkie elementy kolejki (od pierwszego do ostatniego) i dopiero po wyznaczeniu jej końca rozpocząć dodawanie nowego elementu. W celu przyspieszenia obsługi kolejki wykorzystuje się drugi wskaźnik, przechowywujący adres jej końca. Pierwszym krokiem, który należy wykonać przed utworzeniem nowej kolejki, jest deklaracja zmiennych przechowujących wskaźniki na początek i koniec kolejki. var Poczatek, Koniec : TWskaznikNaStrukture ; Rysunek 2: Utworzenie wskaźników do początku i końca kolejki W celu uniknięcia błędów mogących się pojawić po utworzeniu wskaźników do nieistniejącej jeszcze kolejki, należy ustawić ich wartości na NIL. Procedury (funkcje) wykonujące operacje na kolejce powinny sprawdzać czy wskaźniki mają wartości różne niż NIL w celu określenia czy kolejka istnieje. Należy także pamiętać o przypisaniu wskaźnikom wartości NIL po usunięciu ostatniego elementu kolejki (rys. 3). Poczatek := n i l ; Koniec := n i l ; 3.2 Tworzenie pierwszego elementu Po zadeklarowaniu wskaźników do początku i końca kolejki można przystąpić do utwrzenia pierwszego elementu kolejki. W celu uproszczenia procedury dodawania nowego elementu do 2
Rysunek 3: Poprawne ustawienie wartości nowo-utworzonego wskaźnika do początku i końca kolejki kolejki wykorzystamy wskaźnik tymczasowy na element kolejki (umożliwi to zastosowanie tej samej procedury dodawania elementu również w przypadku gdy kolejka zawiera elementy). Deklaracja wskaźnika tymczasowego: var Tmp: TWskaznikNaStrukture ; Utworzenie elementu: new(tmp) ; Rysunek 4: Krok 1 - dodawanie pierwszego elementu do kolejki Po utworzeniu nowego elemntu można przypisać mu dowolne (wymagane przez działanie programu) dane. Tmpˆ.DANE :=1; Rysunek 5: Krok 2 - dodawanie pierwszego elementu do kolejki Ponieważ do kolejki elementy zawsze dodajemy na jej koniec to ostatni element (nowo utworzony) nie może wskazywać na żaden kolejny element. Dlatego też do wskaźnika należy przypisać NIL. Tmpˆ. Nastepny := NIL ; 3
Rysunek 6: Krok 3 - dodawanie pierwszego elementu do kolejki Utworzony element jest pierwszym dodanym do kolejki więc należy go przypisać do wskaźnika Pierwszy. Poczatek := Tmp; Rysunek 7: Krok 4 - dodawanie pierwszego elementu do kolejki Każdy nowo dodany element do kolejki jest ostatnim jej elementem, więc należy przypisać go do wskaźnika na koniec kolejki. Koniec := Tmp; Rysunek 8: Krok 5 - dodawanie pierwszego elementu do kolejki W efekcie powyższych działań powstała kolejka zawierająca jeden element. 3.3 Następne elementy Jeżeli chcemy dodać drugi element do kolejki, należy go utworzyć wykorzystując wskaźnik tymczasowy (tak jak w przypadku dodawania pierwszego elementu do kolejki): new(tmp) ; 4
Rysunek 9: Krok 1 - dodawanie drugiego elementu do kolejki Podobnie jak wcześniej można przypisać do niego dowolne dane: Tmpˆ.DANE := 2 ; Rysunek 10: Krok 2 - dodawanie drugiego elementu do kolejki Oraz ustawić wskaźnik na następny element na wartość NIL: Podobnie jak wcześniej można przypisać do niego dowolne dane: Tmpˆ. Nastepny := NIL ; Rysunek 11: Krok 3 - dodawanie drugiego elementu do kolejki Ponieważ nowy element dodawany jest na końcu kolejki należy go z nią powiązać wykorzystując wskaźnik koniec. Koniec ˆ. Nastepny := TMP; 5
Rysunek 12: Krok 4 - dodawanie drugiego elementu do kolejki Ostatnim krokiem jest przypisanie wskaźnikowi koniec adresu ostatnio dodanego elementu: Koniec := TMP; Rysunek 13: Krok 5 - dodawanie drugiego elementu do kolejki W efekcie powstała kolejka zawierająca dwa elementy. Rysunek 14: Krok 6 - dodawanie drugiego elementu do kolejki Wszystkie następne elementy dodawane są według takiego samego schematu. Wykorzystując tą informację oraz wiedzę o tworzeniu pierwszego elementu kolejki można napisać procedurę to wykonującą. Zwyczajowo procedura ta nosi nazwę ENQUEUE. 3.4 Usuwanie pierwszego elemetnu z kolejki Usunięcie elementu z kolejki wiąże się z przesunięciem wskaźnika Poczatek na następny element. Poniżej przedstawione zostanie usunięcie elementu z kolejki dwuelementowej utworzonej w poprzednim przykładzie. Aby poprawnie usunąć pierwszy element i przesunąć wskaźnik Poczatek należy wykorzystać dodatkową zmienną tymczasową: var Tmp: TWskaznikNaStrukture ; której wartość ustawiamy na początek kolejki: Tmp := Poczatek ; 6
Rysunek 15: Krok 1 - usuwanie pierwszego elementu z kolejki Rysunek 16: Krok 2 - usuwanie pierwszego elementu z kolejki Ponieważ wskaźnik do pierwszego elementu został zapamiętany, można przesunąć wskaźnik początku na element następny: Poczatek := Poczatek ˆ. Nastepny ; Rysunek 17: Krok 3 - usuwanie pierwszego elementu z kolejki Po przesunięciu wskaźnika początku trzeba zwolnić miejsce w pamięci wykorzystywane przez usuwany element. Dispose (Tmp) ; Rysunek 18: Krok 4 - usuwanie pierwszego elementu z kolejki Po wykonaniu poprzednich kroków otrzywaliśmy kolejkę jednoelemntową. 7
Rysunek 19: Krok 5 - usuwanie pierwszego elementu z kolejki Procedura usuwania elementu będzie wyglądała identycznie dla dłuższej kolejki. W przypadku usuwania elementu z kolejki jednoelementowej należy zmienić odpowiednio wskaźnik Koniec. Poniżej znajduje się procedura zawierająca ten warunek. W literaturze usuwanie elementu kolejki zwyczajowo nazywa się DEQUEUE 8
4 Zadania 1. Zaimplementować funkcje do dodawania i usuwania elementów z kolejki. Uwzględnić możwliwość wystąpienia błędów. 2. Napisać program odczytujący kolejne znaki z klawiatury i dodające je do kolejki. W przypadku wciśnięcia klawisza Enter kolejkę należy opróżnić a elementy wypisać. 3. Utworzyć program przechowywującą krótkie wiadomości tekstowe wraz z danymi nadawcy. Program powinien pełnić rolę automatycznej sekretarki. Domyślnie powinien wyświetlać zachętę do pozostawienia wiadomości. Na podanie specjalnego hasła ma wyświetlić kolejne wiadomości. 4. Zaimplementuj kolejkę i jej obsługę, wykorzystującą tylko wskaźnik Poczatek. 5. Zaimplementować kolejkę przechowywującą elementy w tablicy. Utworzyć procedury dodawania i usuwania elementów z takiej kolejki. 9