棧是一種後進先出 (LIFO) 的線性表,僅允許在表的一端(棧頂)進行插入(入棧 Push)和刪除(出棧 Pop)操作。核心操作:
init():初始化空棧push(x):元素 x 入棧pop():彈出並返回棧頂元素top():獲取棧頂元素 (不彈出)empty():判斷棧是否為空size():返回元素個數使用單向鏈表實現,棧頂對應鏈表頭節點。每個節點包含數據域 num 和指針域 next。引入頭節點 簡化邊界條件處理(空棧時 head->next == nullptr)。
[頭節點] → [棧頂元素] → [次頂元素] → ... → [棧底元素] → NULL head top ... (head->next 指向棧頂)
#include <iostream>
#include <stdexcept>
#include <memory> // for std::unique_ptr (可選)
class LinkStack {
private:
// 內部節點結構
struct Node {
int data;
Node* next;
Node(int val, Node* nxt = nullptr) : data(val), next(nxt) {}
};
Node* head_; // 頭節點 (哨兵)
size_t size_; // 元素計數
// 禁用拷貝 (淺拷貝導致雙重釋放),如需拷貝請實現深拷貝
LinkStack(const LinkStack&) = delete;
LinkStack& operator=(const LinkStack&) = delete;
public:
// 構造函數:創建頭節點
LinkStack() : head_(new Node(0)), size_(0) {} // 頭節點數據無意義
// 析構函數:釋放所有節點
~LinkStack() {
clear();
delete head_;
}
// 移動語義支持
LinkStack(LinkStack&& other) noexcept : head_(other.head_), size_(other.size_) {
other.head_ = new Node(0);
other.size_ = 0;
}
LinkStack& operator=(LinkStack&& other) noexcept {
if (this != &other) {
clear();
delete head_;
head_ = other.head_;
size_ = other.size_;
other.head_ = new Node(0);
other.size_ = 0;
}
return *this;
}
// 入棧:頭插法 O(1)
void push(int x) {
head_->next = new Node(x, head_->next);
++size_;
}
// 出棧:刪除頭節點後首個節點 O(1)
int pop() {
if (empty()) throw std::underflow_error("Stack underflow: pop from empty stack");
Node* topNode = head_->next;
int val = topNode->data;
head_->next = topNode->next;
delete topNode;
--size_;
return val;
}
// 獲取棧頂元素 (不修改棧)
int top() const {
if (empty()) throw std::underflow_error("Stack empty: no top element");
return head_->next->data;
}
// 判空
bool empty() const { return head_->next == nullptr; }
// 大小
size_t size() const { return size_; }
// 清空棧
void clear() {
while (!empty()) pop(); // 或手動遍歷刪除
}
// 顯示棧內元素 (從棧頂到棧底)
void display() const {
Node* cur = head_->next;
std::cout << "Stack (top -> bottom): ";
while (cur) {
std::cout << cur->data << " ";
cur = cur->next;
}
std::cout << std::endl;
}
};
// 測試主程序
int main() {
LinkStack st;
int choice, val;
std::cout << "=== 鏈式棧演示程序 ===\n";
while (true) {
std::cout << "\n1. 入棧 2. 出棧 3. 顯示棧頂 4. 顯示所有 5. 退出\n選擇: ";
if (!(std::cin >> choice)) break;
try {
switch (choice) {
case 1:
std::cout << "輸入入棧值: ";
std::cin >> val;
st.push(val);
std::cout << "入棧成功。";
break;
case 2:
val = st.pop();
std::cout << "出棧元素: " << val;
break;
case 3:
std::cout << "棧頂元素: " << st.top();
break;
case 4:
st.display();
break;
case 5:
return 0;
default:
std::cout << "無效選項。";
}
} catch (const std::exception& e) {
std::cerr << "\n錯誤: " << e.what();
}
}
return 0;
}
head_ 不存儲有效數據,其 next 指向棧頂。優勢:
push/pop 統一操作 head_->next,無需特判空棧。empty() 簡單判斷 head_->next == nullptr。pop() 先保存數據,再調整指針,最後 delete,防止內存洩漏。std::underflow_error,調用者可 try-catch 處理。delete;提供移動語義支持高效轉移所有權。| 操作 | 時間 | 空間 |
|---|---|---|
| push | O(1) | O(1) 新節點 |
| pop | O(1) | O(1) 釋放節點 |
| top | O(1) | O(1) |
| empty/size | O(1) | O(1) |
| 特性 | 順序棧 (動態數組) | 鏈式棧 |
|---|---|---|
| 內存分配 | 連續,需擴容 (amortized O(1)) | 離散,按需分配 |
| 容量限制 | 受限於連續內存可用性 | 幾乎無限制 (僅受總內存限制) |
| 緩存局部性 | 極佳 (連續訪問) | 較差 (指針跳轉) |
| push/pop 性能 | 極快 (無分配開銷,擴容除外) | 每次 new/delete 有開銷 |
| 適用場景 | 高頻操作、已知大致規模 | 大小動態變化大、內存碎片化環境 |