栈
1. 逻辑结构
栈(Stack)是一种线性的逻辑结构,但是
- 它的插入运算和删除运算均在同一端进行。
- 进行插入和删除的那一端称为栈顶,另一端称为栈底。栈的插入操作和删除操作也分别简称进栈和出栈。
栈具有“先进后出”(FILO:Fisrt In Last Out)、“后进先出”的特性。
实例
- 一叠饭碗
图示
2. 操作
- init(st) 初始化
- is_empty(st)判断栈是否为空
- get_top(st) 取得栈顶结点值
- push(st, x) 栈的插入操作
- pop(st) 栈的删除操作
3. 顺序实现(顺序栈)
- 栈的顺序存储方式实现又称作顺序栈 栈的顺序存储方式就是在顺序表的基础上对插入和删除操作限制它们在顺序表的同一端进行,所以同顺序表一样也可用一维数组表示。
3.1. 存储结构定义
用数组定义的顺序栈
3.2. 算法实现
顺序栈初始化
判断栈是否为空
获取顺序栈栈顶元素
入栈
出栈
打印栈
判断顺序栈是否已满
4. 链式实现
- 栈的链式存储称为链式栈。链式栈就是一个特殊的单链表,对于这特殊的单链表,它的插入和删除只在单链表的头部进行。 这里采用带头结点的单链表实现。
4.1. 存储结构结构定义
数据结构定义同带头结点的单链表。
4.2. 算法实现
初始化
- 同带头结点的单链表
取栈顶元素
入栈
出栈
5. 应用
5.1. 括号匹配
5.1.1. 问题描述
设一个表达式中可以包含三种括号:小括号、中括号和大括号,各种括号之间允许任意嵌套,如小括号内可以嵌套中括号、大括号,但是不能交叉。举例如下:
([]{}) 正确的 ([()]) 正确的 {([])} 正确的 {[(])} 不正确的 {(}[]) 不正确的
5.1.2. 算法思路
- 自左向右扫描一个表达式,当遇到的左括号,都期待后面有一个和它对应的右括号与之匹配。我们将所遇到的开括号存放入一个栈中,等待匹配。
- 当遇到一个右括号时,就查看这个栈的栈顶结点,如果它们匹配,则删除栈顶结点,如果不匹配,则说明表达式中括号是不匹配的。
- 如果扫描它整个表达式后,这个栈是空的,则说明表达式中的括号是匹配的,否则表达式的括号是不匹配的。
5.1.3. 算法实现
1 int match_kuohao (char *s) {
2 int i;
3 seq_stack st;
4 init_stack(&st);
5 for(i = 0; s[i]!='\0'; i++) {
6 switch (s[i]) {
7 case '{': case '[': case '(':
8 push(&st, s[i]); break;
9 case '}':
10 if(!is_empty(&st)&&get_top(&st)=='{')
11 { pop(&st);break; } else return 0;
12 case ']':
13 if(!is_empty(&st)&&get_top(&st)=='[')
14 { pop(&st);break; } else return 0;
15 case ')':
16 if(!is_empty(&st)&&get_top(&st)=='(')
17 { pop(&st);break; } else return 0;
18 }
19 }
20 return is_empty(&st);
21 }
5.2. 算术表达式求值
对简单的中缀表达式进行求值
从左到右扫描表达式,进行如下处理: 1. 遇到运算数进operand栈 1. 遇到运算符,与operator栈顶运算符比较优先级
- 如果比栈顶运算优先级高,则入栈。
- 否则operator出栈,operand出栈,进行运算,结果入栈。重做2