在這一章我們來了解兩個很特殊的數據結構:堆棧 (Stack) 和隊列 (Queue)。這兩個數據結構類似垃圾桶和隊伍,棧是先進后出型,隊列是先進先出型。
堆棧(Stack)
概念
堆棧是一種常用的數據結構,這種數據結構的存儲方式和垃圾桶一樣,后面放進去的元素可以先取出來,而最早放入的元素會被壓在最下面,最后才能被拿出來。我們也可以把棧的儲存方式簡單理解為堆盤子,后面加入的盤子會被堆到最上面,最早堆入的盤子在最下面:

所以棧是一種后進先出(Last In First Out)的數據結構,后入的元素先出,先入的元素后出。
堆棧主要支持以下兩種操作:
- 入棧(Push):將一個元素放入棧,用來加入數據。
- 出棧(Pop):將一個元素彈出棧,用來刪除數據。
還有以下兩種輔助操作:
- Peek:查看最頂部的元素。
- isEmpty:查看棧是否為空。

數組棧實現
鏈表有兩種實現方式,一種是數組,另一種是鏈表。數據的實現方式很簡單,以下是數組棧的定義:
public class Stack { static final int CAPACITY = 1000; int top; int stack[]; public Stack() { top = -1; stack = new int[CAPACITY]; } }
在數組棧中,我們使用數組來儲存數據,其中包含一個CAPACITY來限制棧的容量,並使用指針top來記錄最頂端元素的位置,top初始化為-1,代表數組棧沒有任何元素。以下是push和pop方法的定義:
public boolean push(int val) { if (top >= (CAPACITY - 1)) { System.out.println("Stack Overflow."); return false; } stack[++top] = val; return true; } public int pop() { if (top < 0) { System.out.println("Stack Underflow."); return 0; } int element = stack[top--]; return element; }
在push中,我們需要檢查頂部元素是否達到容量限制,如果是,輸出“溢出棧”錯誤。否則移動top指針,加入新的元素。在pop中,也要查看棧是否為空,如果是,那么輸出“棧下溢”錯誤。否則將top指針減一。另外還有peek和isEmpty的實現:
public int peek() { if (top < 0) { System.out.println("Stack Underflow"); return 0; } int element = stack[top]; return element; } public boolean isEmpty() { return top < 0; }
peek也要查看棧是否為空,如果不是,直接返回top指向的元素。isEmpty只要查看top是否小於0即可。
鏈式棧實現
除了數組棧,我們也可以使用鏈表來實現棧,以下是鏈式棧的定義:
public class ListStack { static class StackNode { int val; StackNode next; StackNode(int val) { this.val = val; } } StackNode top; public ListStack() { top = null; } }
在鏈式棧中,我們先定義節點StackNode,節點中包含數值和下一個節點的指針。在鏈式棧中,我們只需要記錄top節點,在初始化時定義為null。以下是push和pop的定義:
public void push(int val) { StackNode newNode = new StackNode(val); if (top == null) { top = newNode; } else { StackNode temp = top; top = newNode; newNode.next = temp; } System.out.println(val + " is pushed to stack."); } public int pop() { if (top == null) { System.out.println("Stack is Empty."); return Integer.MIN_VALUE; } int popped = top.val; top = top.next; return popped; }
在push中,我們先創建新的節點newNode,如果棧為空,那么直接將newNode賦給top。如果不為空,就將新元素的下一節點指向當前的top,並將newNode更新為top節點。在pop中,也要先檢查棧是否為空,不為空的話,記錄下top的數據作為返回值,並將top更新為自己的下一個節點。以下是鏈式棧peek和isEmpty的定義:
public int peek() { if (top == null) { System.out.println("Stack is empty."); return Integer.MIN_VALUE; } return top.val; } public boolean isEmpty() { return top == null; }
在棧不為空的情況下,peek只需查看top的值即可,isEmpty也只要查看top是否是null就可以了。不管是用數組還是鏈表來實現棧,我們都只要處理頭節點top,所以棧的所有操作都為O(1)。
隊列(Queue)
概念
隊列是很好理解的一種數據結構,顧名思義,隊列數據結構就和我們平時排隊一樣,先進入的元素先出,后進入的元素后出。隊列的兩端都是開的,一段負責插入新元素,另一端負責刪除元素。

隊列主要支持以下兩種操作:
- 入隊(enqueue):增加一個新的元素
- 出隊(dequeue):刪除一個元素
還支持其他輔助操作:
- peek – 查看隊列最前端的元素
- isFull – 查看隊列是否滿了
- isEmpty – 查看隊列是否為空
數組隊列實現
隊列和堆棧一樣,也可以使用兩種實現方式,一種是使用數組,叫做順序隊列,另一種是使用鏈表實現,叫做鏈式隊列。以下我們會先實現一種更常見的數組隊列,叫做循環隊列,它和基礎的順序隊列相比較,更能有效地利用數組空間。以下是循環隊列的定義:
public class ArrayQueue { int front, rear, size; int capacity; int array[]; public ArrayQueue(int capacity) { this.capacity = capacity; front = rear = size = 0; array = new int[capacity]; } }
在循環隊列中,我們需要capacity來限制隊列的長度,並創建兩個指針front和rear,front用來指向隊列的頭部,而rear指向隊列的尾部。隊列總是從頭部取出元素,從尾部插入新元素,在操作隊列時,我們只需要移動front和rear兩個指針即可。我們還需要一個額外的size變量來記錄元素的數量,front,rear和size都初始化為0 。
以下是enqueue和dequeue的定義:
public void enqueue(int item) { if (isFull()) return; array[rear] = item; rear = (rear + 1) % capacity; size++; System.out.println(item + " is enqueued."); } public int dequeue() { if (isEmpty()) return Integer.MIN_VALUE; int item = array[front]; front = (front + 1) % capacity; size --; return item; }
在新元素入隊的時候,我們需要先判斷隊列是否已滿(isFull的代碼在下一段)。如果未滿,那么就把元素插入rear的位置,並將rear加1,並與capacity取模,然后增加size。在出隊的時候,先要檢查隊列是否為空(isEmpty的代碼在下一段),記錄下刪除元素的值后,我們將front指針增加1,與capacity取模,然后將size減少1。以下是輔助操作的定義:
public int peek() { if (isEmpty()) return Integer.MIN_VALUE; return array[front]; } public boolean isFull() { return size == capacity; } public boolean isEmpty() { return size == 0; }
peek只要查看front指針指向的值即可,isFull要檢查size是否和容量capacity相同,isEmpty直接查看size是否等於0。
鏈式隊列實現
用鏈表實現隊列也很簡單,和數組實現相似,也需要兩個指針(front和rear)來實現。以下是ListQueue和QueueNode的定義:
public class ListQueue { QueueNode front; QueueNode rear; static class QueueNode { int value; QueueNode next; public QueueNode(int value) { this.value = value; } } }
在鏈式隊列中,我們需要定義節點QueueNode,QueueNode中含有兩個值:一個是節點的數值value,另一個是指向下一個節點的next指針。在鏈式隊列ListQueue中,我們只需要兩個節點front和rear,front用來指向隊列最前端的節點,而rear用來指向尾節點。以下是兩個重要操作enqueue和deuque的實現:
public void enqueue(int value) { QueueNode newNode = new QueueNode(value); if (this.rear == null) { // Queue is empty this.front = this.rear = newNode; return; } this.rear.next = newNode; this.rear = newNode; } public int dequeue() { if (this.front == null) { System.out.println("The queue is empty."); return Integer.MIN_VALUE; } QueueNode frontNode = this.front; this.front = this.front.next; if (this.front == null) { this.rear = null; } return frontNode.value; }
在enqueue中,我們先創建一個新的節點,如果隊列為空,那么將頭節點和尾節點同時指向新節點,結束操作。如果隊列不為空,只要尾節點的next指針指向新節點,然后將尾節點指向新節點。在dequeue中,如果頭節點front為空,直接返回默認數值。隊列不為空的情況下,記錄下front的數值作為返回值,並將頭節點更新為下一節點。
完整代碼
Leetcode相關練習
Stack相關:
Queue相關: