概念
栈是一种只能在一端进行插入或删除操作的线性表。表中允许进行插入、删除操作的一端称为栈顶。(后进先出)
顺序栈
定义
栈的顺序存储结构(容易产生溢出)
typedef struct{
SElemType* base;
SElemType* top;
int stacksize;//栈可用最大容量
}SqStack;
另外附设top、base指针,指示栈顶元素和栈底元素在顺序栈中的位置,通常top指示真正的栈顶元素之上的下标地址,stacksize表示栈可用的最大容量,指向同一数组的指针相减得到元素个数差。
空栈:base==top
满栈:top-base==stacksize;
栈满时的处理:
1、报错,返回操作系统
2、分配更大的空间(见入栈的算法)
基本操作
1、顺序栈的初始化
Status InitStack(SqStack &S) {
S.base = (SElemType*)malloc(MAXSIZE * sizeof(SElemType));
if (!S. base) exit(OVERFLOW);
S.top = S.base;
S.stacksize = MAXSIZE;
return OK;
}
2、顺序栈判断是否为空
Status StackEmpty(SqStack S) {
if (S.top == S.base)
return TRUE;
else
return FALSE;
}
3、求顺序栈的长度
int StackLength(SqStack S) {
return S.top - S.base;
}
4、清空顺序栈(清空元素)
Status ClearStack(SqStack& S) {
if(S.base)
S.top = S.base;
return OK;
}
5、销毁顺序栈(清空元素并释放空间)
Status DestroyStack(SqStack& S) {
if (S.base) {
free(S.base);
S.stacksize = 0;
S.base = S.top = NULL;
}
return OK;
}
6、顺序栈入栈
Status push(SqStack& S,SElemType e) {
if (S.top - S.base == S.stacksize) //栈满
return ERROR;//处理方法一
/*处理方法二
if (S->top - S->base == S->stacksize) {//如果栈蛮,修改原先已经分配的内存的大小
S->base = (ElemType*)realloc(S->base,(INCREASE+MAXSIZE) * sizeof(ElemType));
S->top = S->base + S->stacksize;
S->stacksize += INCREASE;
}
*/
*S.top = e;
S.top++;//可用*S.top++=e;代替这两句
return OK;
}
7、顺序栈出栈
Status Pop(SqStack& S, SElemType e) {
if (S.top == S.base)
return ERROR;
S.top--;
e = *S.top;//栈顶在最上面的元素的上个位置,故要先--再取值
return OK;
}
链栈
规定栈的所有操作都是在单链表的表头进行的,用带头节点的单链表表示链栈,第一个数据节点是栈顶节点,最后一个节点是栈底节点。
注意链栈中的指针指向的是前驱元素,而链表中指向的是后继元素。头指针就是栈顶,不需要头结点。空栈想到与头指针指向空。
定义
typedef struct StackNode {
SElemType data;
struct StackNode* next;
}StackNode,*LinkStack;
基本操作
1、链栈的初始化
void InitStack(LinkStack& S) {
S = NULL;
}
2、判断是否为空
Status StackEmpty(LinkStack S) {
if (S == NULL)
return TRUE;
else
return FALSE;
}
3、入栈
Status Push(LinkStack& S, SElemType e) {
StackNode* p;
p =(StackNode*)malloc(sizeof(SElemType)) ;
p->data = e;
p->next = S;
S = p;
return OK;
}
4、出栈
Status Pop(LinkStack& S, SElemType& e) {
if (S == NULL) return ERROR;
StackNode* p;
p = (StackNode*)malloc(sizeof(SElemType));
e = S->data;
p = S;
S = S->next;
free(p);
return OK;
}
5、取栈顶元素
SElemType GetTop(LinkStack S) {
if (S != NULL)
return S->data;
}
栈的应用
1、括号匹配
算法思想:
1、凡出现左括弧,则进栈;
2、凡出现右括弧,首先检查栈是否空。若栈空,则表明该“右括弧”多余
3、否则和栈顶元素比较,若相匹配,则“左括弧(栈顶元素)出栈”,否则表明不匹配
4、表达式检验结束时,若栈空,则表明表达式中匹配正确,否则表明“左括弧”有余。
具体代码见NOJ07
2、中缀表达式计算器
算法思想:
1、首先置数据栈为空栈,表达式起始符“#”为算符栈的栈底元素
2、自左向右扫描表达式,读到操作数进OPND栈,读到运算符,则和OPTR栈顶元素比较(栈顶元素为c1,读到的算符为c2)
若c1<c2,则c2进栈继续扫描后面表达式;
若c1=c2,则(“=”),即括号内运算结束,将c1出栈,并且c2放弃,继续扫描后面表达式;
若c1>c2,则将c1出栈,并在操作数栈取出两个元素a和b按c1做运算,运算结果进OPND。
重复直到表达式求值完毕。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2eb35649e6cc4b7785622d36d968f17f.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzU2MDQ2MDky,size_16,color_FFFFFF,t_70
3、表达式转换
算法思想:
1、设计一个操作符存储栈,用来存取当前读取到的操作符,运算数不存入栈,直接输出
2、比较栈顶元素从c1和将要入栈的操作符c2的优先级:
c1<c2:将c2入栈
c1>c2:弹出栈顶元素并输出
c1=c2:读取下一个操作符,并弹出栈顶元素
操作符优先级如下:
具体代码见NOJ08