棧
"棧"(Stack)是一種遵循後進先出(Last In First Out,LIFO)原則的抽象資料型別。以下是棧的一些基本特點和操作:
特點:
- LIFO 原則:最後加入棧的元素將是第一個被移除的元素。
- 動態大小:棧的大小可以根據需要動態變化。
- 線性結構:元素儲存在棧中的方式是線性的,但只能從一端(棧頂)訪問。
基本操作:
- Push:將一個元素新增到棧頂。
- Pop:從棧頂移除元素,並返回被移除的元素。
- Peek/Top:檢視棧頂元素,但不移除它。
- IsEmpty:檢查棧是否為空。
- Size:獲取棧中元素的數量。
示例:
在許多程式語言中,可以使用陣列或連結串列來實現棧。以下是使用陣列實現棧的簡單示例:
public class Stack<T> {
private T[] stack;
private int top;
private int capacity;
public Stack(int capacity) {
this.capacity = capacity;
this.stack = (T[]) new Object[capacity];
this.top = -1;
}
public void push(T element) {
if (top >= capacity - 1) {
throw new StackOverflowError("Stack is full");
}
stack[++top] = element;
}
public T pop() {
if (isEmpty()) {
throw new IllegalStateException("Stack is empty");
}
return stack[top--];
}
public T peek() {
if (isEmpty()) {
throw new IllegalStateException("Stack is empty");
}
return stack[top];
}
public boolean isEmpty() {
return top == -1;
}
public int size() {
return top + 1;
}
}
程式碼解釋:
Stack
類是一個泛型類,可以儲存任意型別的元素。- 構造方法接受一個容量引數,初始化棧的大小。
push
方法將元素新增到棧頂,如果棧已滿,則丟擲異常。pop
方法從棧頂移除元素並返回它,如果棧為空,則丟擲異常。peek
方法返回棧頂元素但不移除它,如果棧為空,則丟擲異常。isEmpty
方法檢查棧是否為空。size
方法返回棧中元素的數量。
應用場景:
- 函式呼叫:許多程式語言使用棧來跟蹤函式呼叫,儲存區域性變數和返回地址。
- 表示式求值:用於評估和簡化算術表示式,如字尾表示式(逆波蘭表示法)。
- 回溯演算法:如迷宮求解、八皇后問題等。
- 撤銷操作:在編輯器中實現撤銷(Undo)功能。
- 深度優先搜尋(DFS):在圖和樹的遍歷中使用。
堆
“堆”(Heap)有兩個不同的概念,一個是指資料結構,另一個是指記憶體管理中的堆。下面分別解釋這兩個概念:
1. 堆(資料結構)
堆是一種特殊的樹狀資料結構,通常用於實現優先佇列。它滿足以下特性:
- 結構特性:堆通常是一棵完全二叉樹,這意味著除了最後一層外,其他每層都被完全填滿,並且最後一層的節點儘可能地集中在左側。
- 堆序:堆中的每一個節點都必須滿足特定的順序要求。在最小堆中,父節點的值總是小於或等於其子節點的值;在最大堆中,父節點的值總是大於或等於其子節點的值。
基本操作:
- Insert(新增):將新元素新增到堆中,並維持堆的性質。
- Extract(提取):移除並返回堆中的根節點(最小堆中是最小元素,最大堆中是最大元素),然後重新調整堆以維持其性質。
- Peek/Top:返回堆頂元素,但不移除它,對於最小堆是最小元素,對於最大堆是最大元素。
- Decrease Key(降低關鍵字):將堆中某個元素的值降低,並重新調整堆以維持堆的性質。
- Increase Key(提高關鍵字):將堆中某個元素的值提高,如果違反了堆的性質,則需要進行調整。
應用場景:
- 優先佇列:堆是實現優先佇列的理想資料結構,允許快速訪問最高或最低優先順序的元素。
- 排序演算法:堆排序演算法使用堆資料結構對陣列進行排序。
2. 堆(記憶體管理)
在計算機記憶體管理中,堆是用於動態記憶體分配的記憶體區域。以下是它的一些關鍵特點:
- 動態分配:堆用於分配在執行時才知道大小的記憶體塊。
- 碎片化:由於不同大小的記憶體塊被分配和釋放,堆記憶體可能會變得碎片化。
- 垃圾回收:在有垃圾回收機制的語言(如Java、C#)中,堆記憶體由垃圾收集器定期清理,回收不再使用的物件所佔用的記憶體。
- 效能開銷:分配和釋放堆記憶體通常比棧記憶體慢,因為可能涉及記憶體管理的額外開銷,如垃圾回收。
應用場景:
- 動態記憶體分配:在程式執行期間,為物件分配記憶體。
- 垃圾回收:自動管理記憶體的生命週期,防止記憶體洩漏。
示例程式碼(堆資料結構):
import java.util.PriorityQueue;
public class HeapExample {
public static void main(String[] args) {
// 建立一個最小堆的優先佇列
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
// 新增元素到堆中
minHeap.add(20);
minHeap.add(10);
minHeap.add(15);
// 提取堆頂元素
while (!minHeap.isEmpty()) {
System.out.println(minHeap.poll()); // 將按最小堆的順序輸出元素
}
}
}
PriorityQueue
被用來建立一個最小堆,元素按照升序被新增和提取。這個特性使得 PriorityQueue
成為實現堆排序和優先佇列的理想選擇。