Kolejka (FIFO)
Ostatnia aktualizacja
Kolejka ma dwa aktywne końce. Nowe wartości dołączają na końcu, a wychodzą z początku, więc najpierw obsługiwane jest to, co czekało najdłużej. To FIFO i dokładnie tak zachowuje się kolejka do okienka: dołączanie na końcu i obsługa od początku sprawiają, że czekanie jest sprawiedliwe. Kliknij Odtwórz powyżej i zobacz, jak wartości wchodzą z jednej strony i wychodzą z drugiej.
Ponieważ każdy koniec jest śledzony przez własny indeks lub wskaźnik, obie operacje kosztują O(1) i żadna nie przesuwa pozostałych danych. Dlatego kolejki leżą u podstaw wszystkiego, co przetwarza pracę w kolejności nadejścia: zadań drukowania, kolejek zadań i wiadomości, buforów żądań oraz przeszukiwania wszerz, które odwiedza graf poziom po poziomie właśnie dlatego, że trzyma swoją granicę w kolejce. Przenieś koniec usuwania na tył, a otrzymasz stos.
Złożoność czasowa i pamięciowa
Dla kolejki opartej na buforze cyklicznym lub liście jednokierunkowej, czyli dwóch standardowych implementacjach:
| Operacja | Złożoność | Uwagi |
|---|---|---|
| Enqueue | O(1) | Zapisz na końcu i przesuń indeks końca. |
| Dequeue | O(1) | Odczytaj z początku i przesuń indeks początku, bez przesuwania danych. |
| Peek (początek) | O(1) | Odczytaj wartość z początku bez jej usuwania. |
| Wyszukiwanie | O(n) | Kolejka nie służy do tego: aby zajrzeć do środka, trzeba ją opróżnić. |
| Pamięć | O(n) | Jedno pole na każdą czekającą wartość. |
Krok po kroku
| Krok | Co się dzieje |
|---|---|
| 1 | Kolejka jest na początku pusta, a początek i koniec wskazują to samo pole. |
| 2 | Enqueue zapisuje wartość na końcu, a potem przesuwa koniec o jeden. |
| 3 | Każde kolejne enqueue trafia za wartości, które już czekają. |
| 4 | Dequeue odczytuje wartość z początku, a potem przesuwa początek o jeden. |
| 5 | Zwracana wartość to zawsze ta, która czekała najdłużej. |
| 6 | Gdy początek spotka koniec, kolejka znów jest pusta, a dalsze dequeue to błąd. |
Przykład krok po kroku
Dodanie 3, 7, 5, a potem opróżnienie kolejki:
| Operacja | Kolejka (od początku do końca) | Zwraca |
|---|---|---|
enqueue(3) | [3] | nic |
enqueue(7) | [3, 7] | nic |
enqueue(5) | [3, 7, 5] | nic |
dequeue() | [7, 5] | 3, najstarsza wartość |
dequeue() | [5] | 7 |
dequeue() | [] | 5, najnowsza wartość, na końcu |
Kiedy używać kolejki
| Używaj, gdy | Unikaj, gdy |
|---|---|
| Pracę trzeba obsłużyć w kolejności nadejścia: kolejki zadań, bufory żądań, bufory wydruku | Potrzebujesz najpierw najnowszego elementu, a to jest stos |
| Przeglądasz dane poziom po poziomie, jak robi to przeszukiwanie wszerz | Elementy trzeba obsługiwać według priorytetu, a nie kolejności nadejścia; wtedy pasuje kopiec |
| Producent i konsument działają z różną szybkością i potrzebują bufora między sobą | Musisz wyszukiwać w środku danych lub odwoływać się do nich po indeksie |
Chcesz wstawiania i usuwania w O(1) bez przesuwania elementów | Implementujesz ją przez przesuwanie tablicy przy każdym dequeue, co daje O(n) |
Queue: kod
Przejrzysta, gotowa do uruchomienia implementacja algorytmu Queue w językach: Python, JavaScript, Java, C++, C. Wybierz język, skopiuj kod albo otwórz go od razu w edytorze online Coddy.
Queue: kod (Python)
1from collections import deque2
3queue = deque()4
5# Enqueue three values at the rear6for value in [3, 7, 5]:7 queue.append(value)8 print(f"enqueue {value} -> {list(queue)}")9
10# Dequeue them from the front: first in, first out11while queue:12 value = queue.popleft()13 print(f"dequeue {value} -> {list(queue)}")14
15print("empty:", len(queue) == 0)Queue: kod (JavaScript)
1// A plain array makes dequeue O(n): shift() moves every element left.2// Track a head index instead, the fix the queue article describes.3const queue = { items: [], head: 0 };4
5function enqueue(value) {6 queue.items.push(value);7}8
9function dequeue() {10 const value = queue.items[queue.head];11 queue.items[queue.head] = undefined; // free the slot12 queue.head += 1;13 // Reclaim space once the consumed prefix dominates.14 if (queue.head * 2 >= queue.items.length) {15 queue.items = queue.items.slice(queue.head);16 queue.head = 0;17 }18 return value;19}20
21const size = () => queue.items.length - queue.head;22
23for (const value of [3, 7, 5]) {24 enqueue(value);25 console.log(`enqueue ${value} -> size ${size()}`);26}27
28// Dequeue from the front: first in, first out, amortized O(1)29while (size() > 0) {30 console.log(`dequeue ${dequeue()} -> size ${size()}`);31}32
33console.log('empty:', size() === 0);Queue: kod (Java)
1import java.util.ArrayDeque;2import java.util.Queue;3
4public class Main {5 public static void main(String[] args) {6 Queue<Integer> queue = new ArrayDeque<>();7
8 // Enqueue three values at the rear9 for (int value : new int[] {3, 7, 5}) {10 queue.add(value);11 System.out.println("enqueue " + value + " -> " + queue);12 }13
14 // Dequeue them from the front: first in, first out15 while (!queue.isEmpty()) {16 int value = queue.remove();17 System.out.println("dequeue " + value + " -> " + queue);18 }19
20 System.out.println("empty: " + queue.isEmpty());21 }22}Queue: kod (C++)
1#include <iostream>2#include <queue>3
4int main() {5 std::queue<int> queue;6
7 // Enqueue three values at the rear8 for (int value : {3, 7, 5}) {9 queue.push(value);10 std::cout << "enqueue " << value << " -> size " << queue.size() << "\n";11 }12
13 // Dequeue them from the front: first in, first out14 while (!queue.empty()) {15 int value = queue.front();16 queue.pop();17 std::cout << "dequeue " << value << " -> size " << queue.size() << "\n";18 }19
20 std::cout << "empty: " << std::boolalpha << queue.empty() << "\n";21 return 0;22}Queue: kod (C)
1#include <stdio.h>2
3#define CAP 164
5int queue[CAP];6int front = 0;7int rear = 0; /* index of the next free slot */8
9int main(void) {10 int values[3] = {3, 7, 5};11
12 /* Enqueue three values at the rear */13 for (int i = 0; i < 3; i++) {14 queue[rear++] = values[i];15 printf("enqueue %d -> size %d\n", values[i], rear - front);16 }17
18 /* Dequeue them from the front: first in, first out */19 while (front < rear) {20 int value = queue[front++];21 printf("dequeue %d -> size %d\n", value, rear - front);22 }23
24 printf("empty: %d\n", front == rear);25 return 0;26}Kolejka: najczęstsze pytania
Co oznacza FIFO?
Czym różni się kolejka od stosu?
O(1); kolejka usuwa z początku (FIFO), a stos z tego samego końca, na który dodaje (LIFO). Poza tym ich tabele złożoności są identyczne.Jakie są podstawowe operacje na kolejce?
enqueue dodaje wartość na końcu, dequeue usuwa i zwraca wartość z początku, peek (lub front) odczytuje początek bez usuwania, a is_empty informuje, czy coś czeka. Wszystkie cztery kosztują O(1).Dlaczego dequeue jest wolne, gdy używam zwykłej tablicy?
O(n). Prawdziwe implementacje unikają tego dzięki buforowi cyklicznemu, który przesuwa indeks początku, albo liście jednokierunkowej ze wskaźnikiem na głowę. collections.deque w Pythonie i ArrayDeque w Javie robią to za ciebie, a list.pop(0) nie.Czym jest kolejka cykliczna?
n działa bez końca, zamiast wyjść poza koniec tablicy.