KOLEJKA (QUEUE) (lista fifo first in, first out) Kolejki są listami, których elementy można wstawiać z jednego końca (rear-tył) a usuwać z drugiego (front - przód). Operacje: 1. MAKENULL(Q) czyni kolejkę pustą 2. FRONT(Q) zwraca pierwszy element kolejki 3. ENQUEUE(x,Q) wstaw x do kolejki 4. DEQUEUE(Q) usuwa pierwszy element z kolejki 5. EMPTY(Q) zwraca true gdy kolejka jest pusta, w przeciwnym wypadku zwraca false Przykład Załóżmy, że wstawiliśmy element a. Teraz chcemy wstawić kolejny element b. Ponieważ do kolejki wstawiamy zawsze na koniec, więc element b znajdzie się na końcu za elementem a. Gdybyśmy chcieli wstawić kolejny element c, to znajdzie się on na końcu za elementem b. Kolejka wygląda następująco: a b c Możemy usunąć tylko element z przodu kolejki (on ma pierwszeństwo, pozostałe elementy muszą czekać na swoją kolej). Tak więc możemy usunąć tylko element a. Kolejka po usunięciu elementu a wygląda następująco: b c Teraz element b jest pierwszym elementem możemy go usunąć
Wskaźnikowa implementacja kolejki z wykorzystaniem operacji na ADT List ) typedef int elementtype; typedef struct celltype Elementtype element; Celltype * next; Typedef Celltype * position; class Lista protected : position l;//wskaźnik do pierwszego elementu public: void Insert(elementtype x, position p); void Delete(position p); elementtype Retrieve(position p); position Locate(elementtype x); position First(); position Next(position p); position Previous(position p); position END(); ; Tworzymy obiekt tej klasy: List *Q=new Lista; Gdy mamy już obiekt Q typu Lista, chcemy na nim wykonać żądane operacje: Front: Enqueue: Dequeue, Makenull, Empty
FRONT zwraca pierwszy element kolejki Q->Retrieve(Q->First()); ENQUEUE(x) wstaw x do kolejki Q->Insert(x,Q->End()) DEQUEUE(Q) usuwa pierwszy element z kolejki Q->Delete(Q->First()) Empty, Makenull bez zmian
Tablicowa implementacja kolejki Kolejkę można zaimplementować jako listę zrealizowaną tablicowo, ale jest to bardzo kosztowne. (usuwając element trzeba przesunąć wszystkie pozostałe elementy). Efektywniejszym rozwiązaniem jest traktowanie tablicy jako koło, w którym pierwszy element tablicy jest następnym po ostatnim. Wtedy dołączenie elementu do kolejki powoduje przesunięcie rear o jedną pozycję zgodnie z ruchem wskazówek zegara.
Natomiast usunięcie elementu powoduje przesuniecie front o jedną pozycję zgodnie z ruchem wskazówek zegara. Zakładamy, że kolejka może zawierać tylko maxlen-1 elementów ( żeby rozróżnić kolejkę pustą od pełnej )
typedef int elementtype; typedef kolejka = struct elementtype Queue [maxlenght]; int front; int rear; Kolejka jest pusta, jeżeli rear jest tuż za front, na przykład: Kolejka jest pełna, jeżeli pomiędzy rear a front jest jedena pusta komórka. Na przykład:
const maxlength=50; typedef int elementtype; typedef int position; class Kolejka protected : elementtype Queue[maxlength]; position Front; // Indeks elementu czołowego position Rear; // Indeks ostatniego elementu public: Kolejka(); ~Kolejka(); int AddOne(int i); void Init(); void Enqueue(elementtype x); void Dequeue(); elementtype FrontElem(); void Makenul(); bool Empty(); ;
Operator Init powinien tworzyć obiekt oraz inicjować zmienne front i rear tak, aby kolejka była pusta. Przyjmujemy front=0, rear=maxlenght-1 // Zainicjuj kolejkę void Kolejka::Init1() Front=0; Rear= maxlength-1; Funkcja AddOne zwiększa o jeden zgodnie z relacją modulo. // Do 'i' dodaj '1' modulo Maxlength int Kolejka::AddOne(int i) return ((i + 1) % maxlength); //dzielenie modulo, daje resztę z dzielenia
Operacja EMPTY zwraca wartość true, gdy kolejka jest pusta, zwraca wartość false, gdy nie jest pusta // Sprawdź, czy kolejka jest pusta bool Kolejka::Empty() return (AddOne(Rear) == Front);
Operacja Enqueue wstawia element do kolejki (wstawia na koniec). Przykład. Załóżmy, że mamy kolejkę pustą wyglądającą następująco: Chcemy wstawić element x. W tym celu wołamy metodę O->Enqueue(x); Kolejka będzie wyglądać następująco
Po wywołaniu Q->Enqueue(y): Po wywołaniu Enqueue(z):
Metoda Enqueue powinna: -sprawdzać, czy jest miejsce w kolejce (czy kolejka nie jest pełna) Kolejka jest pełna, gdy: ( AddOne(AddOne(Rear)) = Front) - gdy nie jest pełna, to wstawiać element Wstawienie elementu polega na: Zaktualizowaniu pola rear (pole to wskazuje na ostatnio wstawiony element, chcemy, aby wskazywał na pierwszą wolną komórkę) Wstawieniu elementu void Kolejka::Enqueue(elementtype x) if ( AddOne(AddOne(Rear))!= Front) jest pełna Rear = AddOne(Rear); Queue[Rear] = x; // Kolejka nie
Operacja Dequeue usunięcie elementu z kolejki. Uwaga: Usuwamy element z początku kolejki!!! Załóżmy, że mamy kolejkę: Po wywołaniu metody Dequeue mamy:
Po kolejnym wywołaniu metody Dequeue mamy: Metoda Dequeue powinna: - sprawdzić, czy kolejka nie jest pusta - jeśli nie jest pusta, to zmodyfikować: Front void Kolejka ::Dequeue() if (!Empty()) Front= AddOne(Front);
Pozostałe operacje: // Zwróć pierwszy element z kolejki. Kolejka::FrontElem() if (!Empty()) return Queue[Front]; // Sprawdź, czy kolejka jest pusta bool Kolejka::Empty() return (AddOne(Rear) == Front); void Kolejka::Makenul() Front=0; Rear= maxlength-1;