栈的存储结构、算法与应用
一、相关结构体定义
1.顺序栈定义
typedef struct{
int data[maxSize];
int top;
}SqStack;
2.链栈结点定义
typedef struct LNode{
int data;
struct LNode *next;
}LNode;
二、顺序栈
1.顺序栈的要素
对于顺序栈st, 一共有4个要素,包括两个特殊状态和两个操作。
(1)几个状态
1)栈空状态。
st.top==-1
2)栈满状态。
st.top=maxSize-1. maxSize 为栈中最大元素的个数,则maxSize- 1为栈满时栈顶元素在数组中的位置,因为数组下标从0号开始。
3)非法状态(上溢和下溢)。
栈满就是一种继续入栈就会上溢的状态,对应的栈下溢就是栈空的时候继续出栈所造成的结果。
(2)两个操作
1)元素x进栈操作: ++(st.top);st.data[st.top]=x;。 既然规定了top为-1时栈为空,则元素进栈操作
必须是先移动指针,再进入元素,因为数组下标不存在-1。
2)元素x出栈操作: x=st.data[st.top];–(st.top);。 进栈操作次序决定了出栈操作次序,由于进栈操作是先变动栈顶指针,再存入元素,因此出栈操作必须为先取出元素,再变动指针。如果在上述进栈操作不变的情况下先变动指针,再取出元素,则栈顶元素丢失,取出的是栈顶下边的元素。
2.初始化栈的代码
//只需将栈顶指针置为-1即可
void initStack(SqStack &st){
st.top=-1;
}
3.判断栈空代码
//栈st为空的时候返回1,否则返回0
int isEmpty(SqStack st){
if(st.top==-1)
return 1;
else return 0;
}
4.进栈代码
int push(SqStack &st,int x){
if(st.top==maxSize-1) return 0;
++(st.top);
st.data[st.top]=x;
return 1;
}
5.出栈代码
int pop(SqStack &st,int &x){
if(st.top==-1)
return 0;
x=st.data[st.top];
--(st.top);
return 1;
}
ps:大多时候为了方便采用如下写法
(1)定义一个栈并初始化
int stack[maxSize];int top=-1;
(2)元素x进栈
stack[++top]=x;
(3)元素x出栈
x=stack[top--];
三、链栈
1.链栈的要素
和顺序栈对应,链栈也有4个要素,包括两个特殊状态和两个操作。
(1)两个状态
1)栈空状态。
lst->next==NULL
2).栈满状态。
不存在栈满的情况(假设内存无限大的情况下不存在。一般题目要求不太严格, 可以这么认为)。
02)两个操作
1)元素(由指针p所指)进栈操作。
p->next=lst->next; lst->next=p;//其实就是头插法建立链表中的插入操作
2)出栈操作(出栈元素保存在x中)。
p=lst->next;x=p->data;lst->next=p->next;free (p);//其实就是单链表的删除操作
2.链栈的初始化代码
void initStack(LNode *&lst){
lst=(LNode *)malloc(sizeof(LNode));
lst->next=NULL;
}
3.判断栈空代码
void push(LNode *lst,int x){
LNode *p;
p=(LNode *)malloc(sizeof(LNode));
p->next=NULL;
p->data=x;
p->next=lst->next;
lst->next=p;
}
4.出栈代码
int pop(LNode *lst,int &x){
LNode *p;
if(lst->next==NULL) return 0;
p=lst->next;
x=p->data;
lst->next=p->next;
free(p);
return 1;
}
四、栈的应用
1.顺序栈的应用
例1.编写算法,判断一个表达式中的括号是否正确配对,表达式已经存入字符数组exp[ ]中,表达式中的字符个数为n
代码如下:
int match(char exp[],int n){
char stack[maxSize];int top=-1;
int i;
for(i=0;i<n;++i){
if(exp[i]=='(')
stack[++top]='(';
if(exp[i]==')'){
if(top==-1) return 0;
else --top;
}
}
if(top==-1) return 1;
else return 0;
}
例2.编写一个函数,求后缀式的数值,期中后缀式存于一个字符数组exp中,exp中最后一个字符为‘\0’,作为结束符,并且假设后缀式中的数字都只有以为。本题中出现的除法运算皆为整除运算。
**执行过程:**当遇到数值的时候入栈,当遇到运算符的时候,连续两次出栈,将两个出栈元素结合运算符进行运算,将结果当成新遇到的数值入栈。如此往复,直到扫描到终止符“\0"。此时栈底元素即为表达式的值。
int op(int a,char Op,int b){ //运算函数
if(Op=='+') return a+b;
if(Op=='-')return a-b;
if(Op=='*')return a*b;
if(Op=='/'){
if(b==0){
cout<<"ERROR"<<endl;
return 0;
}
else return a/b;
}
}
int com(char exp[]){
int i,a,b,c;
int stack[maxSize];int top=-1;
char Op;
for(i=0;exp[i]!='\0';++i){
if(exp>='0'&&exp[i]<='9')
stack[++top]=exp[i]-'0';//字符型和整型的转换
else{
Op=exp[i];
b=stack[top--];
a=stack[top--];
c=op(a,Op,b);
stack[++top]=c;
}
}
return stack[top];
}
2.链栈的应用
例3:用不带头结点的单链表存储链栈,设计初始化栈,判断栈是否为空、进栈和出栈等相应的算法
分析:不带头结点的单链表lst为空的条件是lst==NULL,进栈和出栈操作都是在表头进行,算法入下:
void insertStackl(LNode *&lst){
lst=NULL;
}
int isEmptyl(LNode *lst){
if(lst==NULL)return 1;
else return 0;
}
void pushl(LNode *&lst,int x){
LNode *p;
p=(LNode *)malloc(sizeof(LNode));
p->next=NULL;
p->data=x;
p->next=lst;
lst=p;
}
int popl(LNode *&lst,int &x){
LNode *p;
if(lst==NULL)
return 0;
p=lst;
x=p->data;
lst=p->next;
free(p);
return 1;
}