Uniwersytet Zielonogórski
Instytut Sterowania i Systemów Informatycznych Metody i języki programowania
Lista 5 – Typy dynamiczne – kolejka
1 Wprowadzenie
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 wykorzysta- nie 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.
3 Tworzenie kolejki na podstawie struktur dynamicz- nych
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ć:
t y p e
TWskaznikNaStrukture = ˆ TTypStruktury ; TTypStruktury = r e c o r d
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 kolej- ki. 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.
v a r 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).
P o c z a t e k := n i l ; Koniec := n i l ;
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:
v a r 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 ;
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.
P o c z a t e k := 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 ) ;
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ć wykorzy- stując wskaźnik koniec.
Koniec ˆ . Nastepny := TMP;
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 ele- ment. 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ą:
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:
P o c z a t e k := P o c z a t e k ˆ . 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.
D i s p o s e (Tmp ) ;
Rysunek 18: Krok 4 - usuwanie pierwszego elementu z kolejki
Po wykonaniu poprzednich kroków otrzywaliśmy kolejkę jednoelemntową.
Rysunek 19: Krok 5 - usuwanie pierwszego elementu z kolejki
Procedura usuwania elementu będzie wyglądała identycznie dla dłuższej kolejki. W przy- padku usuwania elementu z kolejki jednoelementowej należy zmienić odpowiednio wskaźnik Koniec. Poniżej znajduje się procedura zawierająca ten warunek. W literaturze usuwanie ele- mentu kolejki zwyczajowo nazywa się DEQUEUE
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 przy- padku 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 nadaw- cy. Program powinien pełnić rolę automatycznej sekretarki. Domyślnie powinien wyświe- tlać 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 do- dawania i usuwania elementów z takiej kolejki.