数据结构 — 栈

目录

文章目录

  • 目录
  • 栈的特性与结构
  • 栈的操作集
  • 应用示例:括号匹配问题

首先需要说明本文讨论的栈(Stack)是一种数据结构,而非用户态虚拟存储器中的空间结构。作为数据结构的栈是一种特殊的线性表,其数据成员也与线性表一致。区别在于栈是后进先出的,而线性表允许在任意位置插入和删除数据元素。所以,栈也被称作后进先出的线性表,或简称后进先出表。

栈的一种应用场景就是改变数据元素序列的顺序,其思路就是:顺序的将数据元素压栈,但却根据需要让数据元素按照预期的时机出栈,从而改变它们的序列。

在软件设计中,利用栈来进行数据元素序列转换的例子很多。例如:在编译软件系统中,就需要频繁地把中缀表达式形式的算术表达式,转换成后缀表达式形式的算术表达式。又例如:任何支持递归算法的程序设计语言,都是借助栈来实现递归算法需要的后调用的过程先执行的要求的。

栈的特性与结构

栈的核心特性之一显然就是 后进先出。而栈的结构具有栈底(含元素)、栈顶(不含元素)的概念,入栈和出栈都只能在栈顶完成。

数据结构 — 栈_第1张图片

栈的操作集

  • 初始化栈(StackInitiate):设定栈长,并将栈顶置零。
  • 非空否判断(StackNotEmpty):若堆栈非空,则返回 1,否则返回 0。
  • 入栈(StackPush):在栈顶插入数据元素。
  • 出栈(StackPop):把栈顶数据元素弹出(删除并返回)。
  • 取栈顶数据元素(StackTop):取当前栈顶数据元素(返回,但不删除)。

应用示例:括号匹配问题

假设一个算术表达式中包含圆括号、方括号和花括号三种类型的括号,编写一个函数,用来判别表达式中括号是否正确配对,并设计一个测试主函数。

算法思路: 检验括号是否配对可以设置一个栈,每读入一个括号,如果是左括号,则直接进栈,如果读入的是右括号,并且与当前栈顶的左括号是同类型的,则说明括号是配对的,将栈顶的左括号出栈,否则不配对。如果输入序列已经读完,而栈中仍然有等待配对的左括号,则该括号不配对。

  • stack.h
#ifndef __STACK		// 避免头文件被多次导入。
#define __STACK

#include 

#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 

#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 

#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.

你可能感兴趣的:(C,语言)