通常称,栈和队列是限定插入和删除只能在表的“端点”进行的线性表。
线性表 栈 队列
Insert(L, i, x) Insert(S, n+1, x) Insert(Q, n+1, x)
1≤i≤n+1
Delete(L, i) Delete(S, n) Delete(Q, 1)
1≤i≤n
栈是限定仅在表尾进行插入或删除操作的线性表。因此,对栈来说,表尾端称为栈顶,相应地,表头端称为栈底。不含元素的空表称为空栈。
假设栈S=(a1,a2, …,an),则an 端为栈顶,a1 端为栈底。
根据栈的存储结构的不同分为顺序栈和链栈,
以下函数的实现均以顺序栈为例
算法1:栈的数据结构
1 #define STACK_INIT_SIZE 100;
2 #define STACKINCREMENT 10;
3 typedef struct {
4 SElemType *base;
5 SElemType *top;
6 int stacksize;
7 } SqStack;
算法2:InitStack(&S)操作结果:构造一个空栈S。
1 Status InitStack (SqStack &S)
2 {S.base=(ElemType*)malloc(STACK_INIT_SIZE* sizeof(ElemType));
3 if (!S.base) exit (OVERFLOW);
4 S.top = S.base; //栈空的条件
5 S.stacksize = STACK_INIT_SIZE;
6 return OK;}
7 栈空的条件为: S.top = =S.base
算法3:得到栈顶元素
1 Status GetTop (SqStack S, SElemType &e)
2 { // 若栈不空,用e返回其值,并返回OK
3 // 否则返回ERROR
4 if (S.top == S.base) return ERROR;
5 e = *(S.top-1);
6 return OK;
算法4:入栈
1 Status Push (SqStack &S, SElemType e)
2 {if (S.top - S.base >= S.stacksize) {//栈满,追加存储空间
3 S.base = (ElemType *) realloc ( S.base, (S.stacksize + STACKINCREMENT) * sizeof (ElemType));
4 if (!S.base) exit (OVERFLOW); //存储分配失败
5 S.top = S.base + S.stacksize;
6 S.stacksize += STACKINCREMENT;
7 }
8 *S.top++ = e; return OK;
9 }
算法5:出栈
1 Status Pop (SqStack &S, SElemType &e) // 若栈不空,则删除S的栈顶元素,
2 // 用e返回其值,并返回OK;
3 // 否则返回ERROR
4 if (S.top == S.base) return ERROR;
5 e = *--S.top;
6 return OK;
栈的应用问题:
- 数制转换
1 void conversion () {
2 InitStack(S);
3 scanf ("%d",N);
4 while (N) {
5 Push(S, N % 8);
6 N = N/8;
7 }
8 while (!StackEmpty(S)) {
9 Pop(S,e);
10 printf ( "%d", e );
11 }
12 } // conversion
例二:括号匹配的检验
算法的设计思想:
1)凡出现左括弧,则进栈;
2)凡出现右括弧,首先检查栈是否空
若栈空,则表明该“右括弧”多余,
否则和栈顶元素比较,
若相匹配,则“左括弧出栈” ,
否则表明不匹配
3)表达式检验结束时,
若栈空,则表明表达式中匹配正确,
否则表明“左括弧”有余。
1 Status matching(string& exp) {
2 int state = 1; i=0;
3 while (i<=Length(exp) && state) {
4 switch of exp[i] {
5 case 左括弧:{Push(S,exp[i]); i++; break;}
6 case 右括弧: {
7 if(NOT StackEmpty(S)&&GetTop(S)=左括弧)
8 {Pop(S,e); i++;}
9 else {state = 0;}
10 break; } }
11 if (StackEmpty(S)&&state) return OK; }
例三:行编辑程序问题
每接受一个字符即存入存储器并不恰当!
在用户输入一行的过程中,允许用户输入出差错,并在发现有误时可以及时更正。
合理的作法是:
设立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区,并假设“#”为退格符,“@”为退行符。
1 while (ch != EOF) { //EOF为全文结束符
2 while (ch != EOF && ch != '\n') {
3 switch (ch) {
4 case '#' : Pop(S, c); break;
5 case '@': ClearStack(S); break;// 重置S为空栈
6 default : Push(S, ch); break;
7 }
8 ch = getchar(); // 从终端接收下一个字符 }
9 将从栈底到栈顶的字符传送至调用过程的数据区;
10 ClearStack(S); // 重置S为空栈
11 if (ch != EOF) ch = getchar();}
例四:表达式求值
实现算符优先算法,可以使用两个工作栈。
一个称做OPTR,用来存放运算符;
另一个称做OPND,用来存放操作数或运算结果。
算法的基本思想如下:
1)首先置操作数栈为空栈,表达式起始符“#”为运算符栈的栈底元素
2)依次读入表达式中每个字符,若是操作数则进OPND栈,若是运算符,则和OPTR栈的栈顶运算符比较优先权以后作相应操作,直至整个表达式求值完毕。
如何从后缀式求值
先找运算符,再找操作数
例如: Exp = a ´ b + (c - d / e) ´ f
前缀式: + ´ a b ´ - c / d e f
中缀式: a ´ b + c - d / e ´ f
后缀式: a b ´ c d e / - f ´ +