數據結構 — 棧

目錄

首先需要說明本文討論的棧(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.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章