目錄
棧
首先需要說明本文討論的棧(Stack)是一種數據結構,而非用戶態虛擬存儲器中的空間結構。作爲數據結構的棧是一種特殊的線性表,其數據成員也與線性表一致。區別在於棧是後進先出的,而線性表允許在任意位置插入和刪除數據元素。所以,棧也被稱作後進先出的線性表,或簡稱後進先出表。
棧的一種應用場景就是改變數據元素序列的順序,其思路就是:順序的將數據元素壓棧,但卻根據需要讓數據元素按照預期的時機出棧,從而改變它們的序列。
在軟件設計中,利用棧來進行數據元素序列轉換的例子很多。例如:在編譯軟件系統中,就需要頻繁地把中綴表達式形式的算術表達式,轉換成後綴表達式形式的算術表達式。又例如:任何支持遞歸算法的程序設計語言,都是藉助棧來實現遞歸算法需要的後調用的過程先執行的要求的。
棧的特性與結構
棧的核心特性之一顯然就是 後進先出。而棧的結構具有棧底(含元素)、棧頂(不含元素)的概念,入棧和出棧都只能在棧頂完成。
棧的操作集
- 初始化棧(StackInitiate):設定棧長,並將棧頂置零。
- 非空否判斷(StackNotEmpty):若堆棧非空,則返回 1,否則返回 0。
- 入棧(StackPush):在棧頂插入數據元素。
- 出棧(StackPop):把棧頂數據元素彈出(刪除並返回)。
- 取棧頂數據元素(StackTop):取當前棧頂數據元素(返回,但不刪除)。
應用示例:括號匹配問題
假設一個算術表達式中包含圓括號、方括號和花括號三種類型的括號,編寫一個函數,用來判別表達式中括號是否正確配對,並設計一個測試主函數。
算法思路: 檢驗括號是否配對可以設置一個棧,每讀入一個括號,如果是左括號,則直接進棧,如果讀入的是右括號,並且與當前棧頂的左括號是同類型的,則說明括號是配對的,將棧頂的左括號出棧,否則不配對。如果輸入序列已經讀完,而棧中仍然有等待配對的左括號,則該括號不配對。
- stack.h
#ifndef __STACK // 避免頭文件被多次導入。
#define __STACK
#include <stdint.h>
#define STACK_MAX_SIZE 50 /* Maximum stack length. */
enum RESULT {
SUCCESS,
FAIL
};
typedef char symbol; // 定義棧數據元素的類型,示例定義的是字符類型。
typedef struct sq_stack_s {
symbol elems[STACK_MAX_SIZE]; // 棧數據元素數組。
uint8_t top; // 棧頂索引。
} sq_stack_t;
void stack_initiate(sq_stack_t* s);
int stack_not_empty(sq_stack_t* s);
int stack_push(sq_stack_t* s, symbol sym);
int stack_pop(sq_stack_t* s, symbol* sym);
int stack_top(sq_stack_t* s, symbol* sym);
#endif
- stack.c
#include <stdio.h>
#include "stack.h"
void stack_initiate(sq_stack_t* s) // 傳遞結構體時,通常採用指針的方式,這樣不需要進行對象拷貝。
{
s->top = 0;
}
int stack_not_empty(sq_stack_t* s)
{
return s->top <= 0? SUCCESS: FAIL;
}
int stack_push(sq_stack_t* s, symbol sym)
{
if (s->top >= STACK_MAX_SIZE) {
printf("The stack is full.\n");
return FAIL;
} else {
s->elems[s->top] = sym;
s->top++; // 注意 ++ 要放在後面,錯誤的寫法 ++s->top 會先執行 ++s。
return SUCCESS;
}
}
/* 通過使用指針變量的方式來返回有效數值,而非通過 return 語句,return 語句則用於返回函數的執行結果。*/
int stack_pop(sq_stack_t* s, symbol* sym)
{
if (s->top <= 0) {
printf("The stack is empty.\n");
return FAIL;
} else {
s->top--;
*sym = s->elems[s->top];
return SUCCESS;
}
}
int stack_top(sq_stack_t* s, symbol* sym)
{
if (s->top <= 0) {
printf("The stack is empty.\n");
return FAIL;
} else {
*sym = s->elems[s->top - 1];
return SUCCESS;
}
}
編譯 libstack 靜態庫:
gcc -c stack.c --std c99
ar -crv libstack.a stack.o
- main.c
#include <stdio.h>
#include "stack.h" /* 首先需要生成庫文件,才能被主程序鏈接。 */
int match_symbol(symbol left, symbol right)
{
if (left == '(' && right == ')') {
return SUCCESS;
}
else if (left == '[' && right == ']') {
return SUCCESS;
}
else if (left == '{' && right == '}') {
return SUCCESS;
}
else {
return FAIL;
}
}
int main(void)
{
sq_stack_t s;
symbol sym_str[STACK_MAX_SIZE];
symbol* cp; // Cursor pointer,遊標指針,用於遍歷輸入的字符數組。
symbol left_sym; // 用於臨時存儲棧彈出或返回的數據元素。
stack_initiate(&s);
printf("INPUT >");
gets(sym_str);
cp = sym_str;
/* 當 cp 遊標走到字符串終結符 `/0`,while 循環就會退出。 */
while (*cp) {
switch (*cp) {
case '(':
case '[':
case '{':
stack_push(&s, *cp++); // 1. *cp, 2. cp++
break;
case ')':
case ']':
case '}':
if (SUCCESS == stack_not_empty(&s)) {
printf("There is no left bracket in the stack.\n");
return FAIL;
} else {
stack_top(&s, &left_sym);
if (SUCCESS == match_symbol(left_sym, *cp)) {
stack_pop(&s, &left_sym);
} else {
printf("Not symbol match.\n");
return FAIL;
}
}
default:
cp++;
}
}
if (SUCCESS == stack_not_empty(&s)) {
printf("Match successful.\n");
return SUCCESS;
} else {
printf("Match failed.\n");
return FAIL;
}
}
編譯 main 程序:
gcc main.c -o main -L./ -lstack
運行:
$ ./main
INPUT >{}
Match successful.
$ ./main
INPUT >[]
Match successful.
$ ./main
INPUT >{{}
Match failed.
$ ./main
INPUT >{{}]
Not symbol match.
$ ./main
INPUT >(())
Match successful.