Queue (Warteschlange)
Zuletzt aktualisiert
Eine Queue hat zwei aktive Enden. Neue Werte reihen sich hinten ein, entnommen wird vorne, sodass immer der Wert zuerst drankommt, der am längsten gewartet hat. Das ist FIFO, und genau so verhält sich eine Schlange an der Kasse: Hinten anstellen und vorne bedient werden ist das, was das Warten fair macht. Drücke oben auf Abspielen und sieh zu, wie Werte auf der einen Seite hineingehen und auf der anderen wieder heraus.
Weil jedes Ende seinen eigenen Index oder Zeiger hat, kosten beide Operationen O(1), und keine von beiden verschiebt die übrigen Daten. Deshalb steckt eine Queue unter allem, was Arbeit in der Reihenfolge ihres Eintreffens abarbeitet: Druckaufträge, Task- und Message-Queues, Anfragepuffer und die Breitensuche, die einen Graphen genau deshalb Ebene für Ebene besucht, weil sie ihre Front in einer Queue hält. Verschiebe das Ende, an dem entfernt wird, nach hinten, und du hast stattdessen einen Stack.
Zeit- und Speicherkomplexität
Für eine Queue auf Basis eines Ringpuffers oder einer verketteten Liste, die beiden üblichen Implementierungen:
| Operation | Komplexität | Hinweise |
|---|---|---|
| Enqueue (einreihen) | O(1) | Hinten schreiben und den hinteren Index weiterrücken. |
| Dequeue (entnehmen) | O(1) | Vorne lesen und den vorderen Index weiterrücken, ohne etwas zu verschieben. |
| Peek (vorne ansehen) | O(1) | Den vordersten Wert lesen, ohne ihn zu entfernen. |
| Suchen | O(n) | Dafür ist eine Queue nicht gedacht: Du musst sie leeren, um hineinzusehen. |
| Speicher | O(n) | Ein Platz pro wartendem Wert. |
Schritt für Schritt
| Schritt | Was passiert |
|---|---|
| 1 | Die Queue startet leer, vorne und hinten zeigen auf denselben Platz. |
| 2 | Enqueue schreibt den Wert hinten und rückt das hintere Ende danach um eins weiter. |
| 3 | Jedes weitere Enqueue landet hinter den Werten, die schon warten. |
| 4 | Dequeue liest den Wert vorne und rückt das vordere Ende danach um eins weiter. |
| 5 | Zurück kommt immer der Wert, der am längsten gewartet hat. |
| 6 | Treffen vorne und hinten aufeinander, ist die Queue wieder leer, und ein weiteres Dequeue ist ein Fehler. |
Durchgerechnetes Beispiel
3, 7 und 5 einreihen und die Queue danach leeren:
| Operation | Queue (vorne nach hinten) | Gibt zurück |
|---|---|---|
enqueue(3) | [3] | nichts |
enqueue(7) | [3, 7] | nichts |
enqueue(5) | [3, 7, 5] | nichts |
dequeue() | [7, 5] | 3, der älteste Wert |
dequeue() | [5] | 7 |
dequeue() | [] | 5, der neueste Wert, zuletzt |
Wann man eine Queue verwendet
| Verwenden, wenn | Vermeiden, wenn |
|---|---|
| Arbeit in der Reihenfolge ihres Eintreffens erledigt werden muss: Job-Queues, Anfragepuffer, Druckerspooler | Du das zuletzt hinzugefügte Element zuerst brauchst, dafür ist ein Stack da |
| Du Ebene für Ebene erkundest, so wie es die Breitensuche tut | Elemente nach Priorität statt nach Eintreffen bedient werden müssen, wofür ein Heap passt |
| Ein Producer und ein Consumer unterschiedlich schnell laufen und einen Puffer dazwischen brauchen | Du in der Mitte der Daten suchen oder indizieren musst |
Du O(1) beim Einfügen und Entfernen willst, ohne Elemente zu verschieben | Du sie umsetzen würdest, indem du bei jedem Dequeue ein Array verschiebst, was sie O(n) macht |
Queue-Code
Eine saubere, lauffähige Queue-Implementierung in Python, JavaScript, Java, C++, C. Wähle eine Sprache, kopiere den Code oder öffne ihn vorgeladen im Coddy-Playground.
Queue-Code in 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-Code in 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-Code in 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-Code in 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-Code in 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}Queue FAQ
Was bedeutet FIFO?
Was ist der Unterschied zwischen einer Queue und einem Stack?
O(1) hinzu; eine Queue entfernt vorne (FIFO), ein Stack an demselben Ende, an dem er hinzugefügt hat (LIFO). Ihre Komplexitätstabellen sind ansonsten identisch.Was sind die wichtigsten Queue-Operationen?
enqueue hängt einen Wert hinten an, dequeue entfernt den vordersten Wert und gibt ihn zurück, peek (oder front) liest den vordersten Wert, ohne ihn zu entfernen, und is_empty meldet, ob noch etwas wartet. Alle vier sind O(1).Warum ist dequeue langsam, wenn ich ein einfaches Array verwende?
O(n) macht. Echte Implementierungen vermeiden das mit einem Ringpuffer, der einen vorderen Index weiterrückt, oder mit einer verketteten Liste mit Kopfzeiger. Pythons collections.deque und Javas ArrayDeque erledigen das für dich, list.pop(0) nicht.Was ist eine zirkuläre Queue?
n unbegrenzt weiterarbeitet, statt am Ende des Arrays anzustoßen.