棧是僅允許在表尾(棧頂)進行插入/刪除的線性表,遵循後進先出(LIFO)原則。本文基於嚴蔚敏《數據結構》(C 語言版)接口規範,提供順序棧(動態擴容)與鏈式棧兩種完整實現,包含初始化、銷毀、清空、判空、求長、取頂、入棧、出棧、遍歷等九大核心操作,代碼符合生產級健壯性標準。
ADT Stack {
數據對象: D = {a₀, a₁, ..., aₙ₋₁} (n≥0)
數據關係: R = { | 1≤i
#define STACK_INIT_SIZE 100 // 初始容量
#define STACK_INCREMENT 10 // 擴容增量
typedef int DataType; // 元素類型,可按需修改
typedef struct {
DataType *base; // 棧底指針,指向動態數組首地址
DataType *top; // 棧頂指針,指向下一個可用位置
int stackSize; // 當前分配容量
} SqStack, *PSqStack;
約定:top == base 表示空棧;top - base == stackSize 觸發擴容;base == NULL 表示棧不存在。
PSqStack InitStack(PSqStack s) {
s = (PSqStack)malloc(sizeof(SqStack));
if (!s) return NULL;
s->base = (DataType*)malloc(STACK_INIT_SIZE * sizeof(DataType));
if (!s->base) { free(s); return NULL; }
s->top = s->base;
s->stackSize = STACK_INIT_SIZE;
return s;
}
int Push(PSqStack s, DataType e) {
if (!s) return 0;
// 滿棧檢查與擴容
if (s->top - s->base >= s->stackSize) {
DataType *newBase = (DataType*)realloc(s->base,
(s->stackSize + STACK_INCREMENT) * sizeof(DataType));
if (!newBase) return 0; // 擴容失敗
s->top = newBase + (s->top - s->base); // 修正 top 指針
s->base = newBase;
s->stackSize += STACK_INCREMENT;
}
*s->top++ = e; // 賦值並後移 top
return 1;
}
int Pop(PSqStack s, DataType *e) {
if (!s || s->top == s->base) return 0; // 空棧
*e = *--s->top; // 先回退再取值
return 1;
}
int StackEmpty(PSqStack s) { return s && s->top == s->base; }
int StackLength(PSqStack s) { return s ? s->top - s->base : -1; }
int GetTop(PSqStack s, DataType *e) {
if (!s || s->top == s->base) return 0;
*e = *(s->top - 1);
return 1;
}
void ClearStack(PSqStack s) { if (s) s->top = s->base; }
void DestroyStack(PSqStack s) {
if (s) { free(s->base); free(s); }
}
void StackTraverse(PSqStack s, void (*visit)(DataType)) {
if (!s) return;
for (DataType *p = s->base; p < s->top; ++p) visit(*p);
}
typedef struct LNode {
DataType data;
struct LNode *next;
} LNode, *LinkStack;
約定:頭節點不存數據,head->next 指向棧頂節點。空棧時 head->next == NULL。此設計統一了插入/刪除邏輯,無需特判首節點。
LinkStack InitStack() {
LinkStack s = (LinkStack)malloc(sizeof(LNode));
if (s) s->next = NULL;
return s;
}
int Push(LinkStack s, DataType e) {
if (!s) return 0;
LNode *node = (LNode*)malloc(sizeof(LNode));
if (!node) return 0;
node->data = e;
node->next = s->next;
s->next = node;
return 1;
}
int Pop(LinkStack s, DataType *e) {
if (!s || !s->next) return 0;
LNode *top = s->next;
*e = top->data;
s->next = top->next;
free(top);
return 1;
}
int StackEmpty(LinkStack s) { return s && !s->next; }
int StackLength(LinkStack s) {
int len = 0;
for (LNode *p = s ? s->next : NULL; p; p = p->next) ++len;
return len;
}
int GetTop(LinkStack s, DataType *e) {
if (!s || !s->next) return 0;
*e = s->next->data;
return 1;
}
void ClearStack(LinkStack s) {
if (!s) return;
LNode *p = s->next;
while (p) {
LNode *tmp = p;
p = p->next;
free(tmp);
}
s->next = NULL;
}
void DestroyStack(LinkStack *s) { // 雙重指針修改頭指針
if (!s || !*s) return;
ClearStack(*s);
free(*s);
*s = NULL;
}
void StackTraverse(LinkStack s, void (*visit)(DataType)) {
for (LNode *p = s ? s->next : NULL; p; p = p->next) visit(p->data);
}
| 維度 | 順序棧 | 鏈式棧 |
|---|---|---|
| 空間分配 | 連續記憶體,預分配+擴容 | 離散節點,按需分配 |
| 空間利用率 | 可能有預留閒置 | 零碎片,但每節點額外指針開銷 |
| 擴容性能 | 偶發 O(n) realloc 拷貝 | 每次 O(1) 分配,無擴容 |
| 訪問速度 | 緩存友好,CPU 預取優勢 | 指針追蹤,緩存未命中率高 |
| 最大容量 | 受限於連續記憶體可用性 | 僅受限於總堆內存 |
| 適用場景 | 高頻操作、容量可預估、對性能敏感 | 容量極不確定、長壽命、內存碎片化環境 |
void PrintElement(DataType e) { printf("%d ", e); }
int main() {
printf("=== 順序棧測試 ===\n");
PSqStack sq = InitStack(NULL);
for (int i = 1; i <= 5; ++i) Push(sq, i*10);
printf("長度: %d\n", StackLength(sq));
StackTraverse(sq, PrintElement); putchar('\n');
DataType e; Pop(sq, &e); printf("出棧: %d\n", e);
StackTraverse(sq, PrintElement); putchar('\n');
DestroyStack(sq);
printf("\n=== 鏈式棧測試 ===\n");
LinkStack lk = InitStack();
for (int i = 1; i <= 5; ++i) Push(lk, i*100);
printf("長度: %d\n", StackLength(lk));
StackTraverse(lk, PrintElement); putchar('\n');
Pop(lk, &e); printf("出棧: %d\n", e);
StackTraverse(lk, PrintElement); putchar('\n');
DestroyStack(&lk);
return 0;
}
void* 元素 + 銷毀回調函數,支持任意類型pthread_mutex_t 實現並發訪問保護StackIterator 替代回調遍歷,支持中斷/嵌套棥雖簡單,卻是編譯器語法分析、函數調用棧、表達式求值、瀏覽器前進後退、撤銷重做等無數系統的核心骨架。掌握其兩種經典實現及優劣權衡,是從「會用庫」進階為「能造輪子」的關鍵一步。