目錄
🍉引言
🍉棧的本質和特點
🍈棧的基本操作
🍈棧的特點
🍍后進先出
🍍操作受限
🍍動態調整
🍈棧的優缺點
🍍優點
🍍缺點
🍉棧的應用
棧的代碼實現
代碼說明
圖解棧的出入過程
壓棧過程
出棧過程
棧的實際應用實例
括號匹配
瀏覽器前進后退功能
代碼說明
🍉棧的實現細節和優化
🍈數組實現棧的優化
🍉引言
棧(Stack)是一種常見的數據結構,在計算機科學中具有重要的應用價值。棧的操作受限于后進先出(LIFO, Last In First Out)的原則,這種特點使得棧在處理特定類型的問題時非常高效。本文將詳細解析棧的本質和特點,并通過生活中的例子和代碼實現來深入理解棧的應用。
🍉棧的本質和特點
- 棧是一種線性數據結構,只允許在一端進行插入和刪除操作,這一端稱為棧頂(Top)。與棧頂相對的另一端稱為棧底(Bottom),棧底是固定的,不進行操作
🍈棧的基本操作
棧有幾種基本操作:
- 壓棧(Push):將一個元素添加到棧頂。
- 出棧(Pop):移除并返回棧頂元素。
- 取棧頂元素(Peek or Top):返回棧頂元素但不移除它。
- 檢查棧是否為空(isEmpty):返回布爾值,指示棧是否為空。
- 檢查棧是否已滿(isFull):返回布爾值,指示棧是否已滿(主要用于固定大小的棧)。
🍈棧的特點
🍍后進先出
- 棧的最主要特點是后進先出,即最新加入的元素最先被移除。這種特性使得棧特別適用于某些特定的應用場景。
🍍操作受限
- 與其他數據結構相比,棧的操作比較受限。只能在棧頂進行壓棧和出棧操作,不能直接訪問棧中的任意元素。
🍍動態調整
- 棧可以是固定大小的,也可以是動態調整大小的。動態棧會根據需要自動調整其容量。
🍈棧的優缺點
🍍優點
簡單高效: 棧的操作簡單,只需考慮棧頂元素,因此執行速度較快。
內存管理: 棧內存的分配和釋放是自動進行的,不需要手動管理內存,避免了內存泄漏和垃圾回收的問題。
遞歸調用: 棧結構天然適合處理遞歸調用,函數的調用和返回都可以利用棧的特性。
🍍缺點
大小限制: 棧的大小通常是固定的,當數據量超過棧的容量時會導致棧溢出。
數據不靈活: 棧是一種后進先出(LIFO)的數據結構,只能在棧頂進行操作,不適合需要隨機訪問數據的場景。
局部性: 棧中的數據只能按照特定的順序訪問,缺乏靈活性,不能滿足所有的數據操作需求。
🍉棧的應用
棧在現實生活和計算機科學中都有廣泛的應用:
- 函數調用:計算機系統使用棧來管理函數調用。每次函數調用時,當前函數的狀態(如局部變量、返回地址等)會被壓入棧中。當函數返回時,狀態從棧中彈出并恢復。
- 表達式求值和語法解析:棧用于將中綴表達式轉換為后綴表達式或前綴表達式,并且在表達式求值過程中,棧也扮演重要角色。
- 瀏覽器的前進后退功能:瀏覽器使用兩個棧來管理用戶的瀏覽歷史,一個棧存儲前進的頁面,另一個棧存儲后退的頁面。
- 撤銷操作:許多軟件(如文本編輯器、圖像編輯器等)使用棧來實現撤銷和恢復功能。
棧的代碼實現
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef struct Stack {int *items;int top;int capacity;
} Stack;// 初始化棧
Stack* createStack(int capacity) {Stack *stack = (Stack *)malloc(sizeof(Stack));stack->capacity = capacity;stack->top = -1;stack->items = (int *)malloc(capacity * sizeof(int));return stack;
}// 檢查棧是否為空
bool is_empty(Stack *stack) {return stack->top == -1;
}// 壓入元素到棧
void push(Stack *stack, int item) {if (stack->top == stack->capacity - 1) {printf("棧已滿,無法壓入元素\n");return;}stack->items[++stack->top] = item;
}// 彈出棧頂元素
int pop(Stack *stack) {if (is_empty(stack)) {printf("棧為空,無法彈出元素\n");exit(EXIT_FAILURE);}return stack->items[stack->top--];
}// 獲取棧頂元素
int peek(Stack *stack) {if (is_empty(stack)) {printf("棧為空,無法獲取棧頂元素\n");exit(EXIT_FAILURE);}return stack->items[stack->top];
}// 獲取棧的大小
int size(Stack *stack) {return stack->top + 1;
}int main() {Stack *stack = createStack(100); // 創建一個容量為100的棧push(stack, 1);push(stack, 2);push(stack, 3);printf("棧頂元素:%d\n", peek(stack)); // 輸出 3printf("出棧元素:%d\n", pop(stack)); // 輸出 3printf("棧頂元素:%d\n", peek(stack)); // 輸出 2printf("棧大小:%d\n", size(stack)); // 輸出 2// 釋放分配的內存free(stack->items);free(stack);return 0;
}
代碼說明
Stack
結構體包含一個整數數組items
,一個表示棧頂索引的top
,和一個表示棧容量的capacity
。createStack
函數用于初始化棧并分配內存。is_empty
函數用于檢查棧是否為空。push
函數用于將元素壓入棧,并在棧滿時打印錯誤信息。pop
函數用于彈出棧頂元素,并在棧為空時打印錯誤信息并退出程序。peek
函數用于獲取棧頂元素,并在棧為空時打印錯誤信息并退出程序。size
函數用于獲取棧的大小(當前存儲的元素數量)。main
函數演示了棧的使用,類似于您提供的 Python 代碼示例。
圖解棧的出入過程
- 為了更直觀地理解棧的操作過程,我們可以使用圖解的方式來展示棧的壓棧和出棧操作。
壓棧過程
- 初始狀態:棧為空
棧: []
- ?壓棧 1:
壓棧 1
棧: [1]
- 壓棧 2:
壓棧 2
棧: [1, 2]
- 壓棧 3:
壓棧 3
棧: [1, 2, 3]
出棧過程
- 初始狀態:棧頂元素為 3
棧: [1, 2, 3]
- 出棧 3:
出棧 3
棧: [1, 2]
- 出棧 2:
出棧 2
棧: [1]
- 出棧 1:
出棧 1
棧: []
棧的實際應用實例
括號匹配
- 括號匹配問題是棧的經典應用之一,常用于編譯器和解釋器的語法解析。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 棧結構定義
typedef struct Stack {int top;unsigned capacity;char* array;
} Stack;// 創建棧
Stack* createStack(unsigned capacity) {Stack* stack = (Stack*) malloc(sizeof(Stack));stack->capacity = capacity;stack->top = -1;stack->array = (char*) malloc(stack->capacity * sizeof(char));return stack;
}// 判斷棧是否為空
int isEmpty(Stack* stack) {return stack->top == -1;
}// 入棧
void push(Stack* stack, char item) {stack->array[++stack->top] = item;
}// 出棧
char pop(Stack* stack) {if (isEmpty(stack))return '\0';return stack->array[stack->top--];
}// 獲取棧頂元素
char peek(Stack* stack) {if (isEmpty(stack))return '\0';return stack->array[stack->top];
}// 判斷括號是否平衡
int isBalanced(const char* expression) {Stack* stack = createStack(strlen(expression));char pairs[256] = { 0 };pairs[')'] = '(';pairs[']'] = '[';pairs['}'] = '{';for (int i = 0; i < strlen(expression); i++) {char char = expression[i];if (char == '(' || char == '{' || char == '[') {push(stack, char);} else if (char == ')' || char == '}' || char == ']') {if (isEmpty(stack) || pop(stack) != pairs[char]) {free(stack->array);free(stack);return 0; // false}}}int balanced = isEmpty(stack);free(stack->array);free(stack);return balanced;
}// 測試函數
int main() {const char* expression1 = "{[()()]}";printf("%s\n", isBalanced(expression1) ? "True" : "False");const char* expression2 = "{[(])}";printf("%s\n", isBalanced(expression2) ? "True" : "False");return 0;
}
瀏覽器前進后退功能
瀏覽器的前進和后退功能可以通過兩個棧來實現,一個棧用于存儲前進頁面,另一個棧用于存儲后退頁面:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct Node {char* data;struct Node* next;
} Node;typedef struct Stack {Node* top;
} Stack;// 初始化棧
void init_stack(Stack* stack) {stack->top = NULL;
}// 檢查棧是否為空
int is_empty(Stack* stack) {return stack->top == NULL;
}// 推入元素到棧
void push(Stack* stack, const char* data) {Node* new_node = (Node*)malloc(sizeof(Node));new_node->data = (char*)malloc(strlen(data) + 1);strcpy(new_node->data, data);new_node->next = stack->top;stack->top = new_node;
}// 彈出棧頂元素
char* pop(Stack* stack) {if (is_empty(stack)) {return NULL;}Node* temp = stack->top;char* data = temp->data;stack->top = stack->top->next;free(temp);return data;
}// 瀏覽器歷史記錄結構體
typedef struct BrowserHistory {Stack forward_stack;Stack backward_stack;char* current_page;
} BrowserHistory;// 初始化瀏覽器歷史記錄
void init_browser_history(BrowserHistory* history) {init_stack(&history->forward_stack);init_stack(&history->backward_stack);history->current_page = NULL;
}// 訪問新頁面
void visit(BrowserHistory* history, const char* page) {if (history->current_page != NULL) {push(&history->backward_stack, history->current_page);}history->current_page = (char*)malloc(strlen(page) + 1);strcpy(history->current_page, page);// 清空前進棧while (!is_empty(&history->forward_stack)) {free(pop(&history->forward_stack));}
}// 后退
char* back(BrowserHistory* history) {if (!is_empty(&history->backward_stack)) {push(&history->forward_stack, history->current_page);history->current_page = pop(&history->backward_stack);return history->current_page;} else {return NULL; // 無法后退}
}// 前進
char* forward(BrowserHistory* history) {if (!is_empty(&history->forward_stack)) {push(&history->backward_stack, history->current_page);history->current_page = pop(&history->forward_stack);return history->current_page;} else {return NULL; // 無法前進}
}int main() {BrowserHistory history;init_browser_history(&history);visit(&history, "Page1");visit(&history, "Page2");visit(&history, "Page3");printf("%s\n", back(&history)); // 輸出 Page2printf("%s\n", back(&history)); // 輸出 Page1printf("%s\n", forward(&history)); // 輸出 Page2return 0;
}
代碼說明
- 棧的實現:我們用
Node
結構體來表示棧中的節點,用Stack
結構體來管理棧頂節點。提供了初始化、檢查空棧、推入和彈出元素的函數。- 瀏覽器歷史記錄:用
BrowserHistory
結構體來管理當前頁面和兩個棧。提供了初始化、訪問新頁面、后退和前進的函數。- 內存管理:使用
malloc
和free
來動態分配和釋放內存,以避免內存泄漏。
🍉棧的實現細節和優化
- 在實際應用中,棧的實現可以有多種方式,包括使用數組(列表)或鏈表。每種方式都有其優缺點:
- 數組實現棧:簡單高效,但需要預先確定棧的最大容量,可能會導致空間浪費或溢出。
- 鏈表實現棧:靈活,無需預先確定棧的容量,但每個節點需要額外的指針存儲空間,操作相對復雜。
🍈數組實現棧的優化
- 為了解決數組實現棧的容量限制問題,可以使用動態數組,它會在需要時自動擴展容量:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef struct {int *items;int capacity;int size;
} DynamicArrayStack;// Initialize the stack
DynamicArrayStack* createStack() {DynamicArrayStack* stack = (DynamicArrayStack*)malloc(sizeof(DynamicArrayStack));stack->capacity = 1;stack->size = 0;stack->items = (int*)malloc(stack->capacity * sizeof(int));return stack;
}// Check if the stack is empty
bool isEmpty(DynamicArrayStack* stack) {return stack->size == 0;
}// Push an item onto the stack
void push(DynamicArrayStack* stack, int item) {if (stack->size == stack->capacity) {stack->capacity *= 2;stack->items = (int*)realloc(stack->items, stack->capacity * sizeof(int));}stack->items[stack->size++] = item;
}// Pop an item from the stack
int pop(DynamicArrayStack* stack) {if (isEmpty(stack)) {fprintf(stderr, "pop from empty stack\n");exit(EXIT_FAILURE);}return stack->items[--stack->size];
}// Peek at the top item of the stack
int peek(DynamicArrayStack* stack) {if (isEmpty(stack)) {fprintf(stderr, "peek from empty stack\n");exit(EXIT_FAILURE);}return stack->items[stack->size - 1];
}// Get the size of the stack
int stackSize(DynamicArrayStack* stack) {return stack->size;
}// Free the stack
void freeStack(DynamicArrayStack* stack) {free(stack->items);free(stack);
}int main() {// Create and use the dynamic array stackDynamicArrayStack* dynamicStack = createStack();push(dynamicStack, 1);push(dynamicStack, 2);push(dynamicStack, 3);printf("棧頂元素:%d\n", peek(dynamicStack)); // 輸出 3printf("出棧元素:%d\n", pop(dynamicStack)); // 輸出 3printf("棧頂元素:%d\n", peek(dynamicStack)); // 輸出 2printf("棧大小:%d\n", stackSize(dynamicStack)); // 輸出 2freeStack(dynamicStack);return 0;
}
希望這些能對剛學習算法的同學們提供些幫助哦!!!