栈
栈的概述
栈是一种只能从一端插入的线性表
只能在同一个端进行插入和删除栈的几个概念
允许插入和删除的一端称为栈顶
另一端称为栈底
栈中没有元素时称为空栈
栈的插入操作通常称为进栈或入栈
栈的删除操作通常称为出栈或退栈
栈的特点:
栈的主要特点是后进先出即后进栈的元素先出栈。栈也被称为后进先出表
设一个栈的输入序列为a,b,c,d,则借助一个栈所得到的输出序列不可能是_D__
A,c,d,b,a B,d,c,b,a C,a,c,d,b D,d,a,b,c
抽象数据类型=逻辑结构+基本运算(运算描述)
栈的几种基本运算如下:
InitStack(&s)初始化栈。构造一个空栈s
DestroyStack(&s)销毁栈。释放栈s所占的存储空间
StackEmpty(s)判断栈是否为空:若栈s为空,则返回真,否则返回假
Push(&s,e)进栈,将元素e插入栈s中作为栈顶元素,栈顶指针加1
Pop(&s,&e)出栈,从栈s中退出栈顶元素,并且将其赋值给e
GetTop(s,&e)取栈顶元素。返回当前栈顶元素,将其赋值给e
栈的顺序存储结构
顺序栈
假设栈中的存储元素个数最大不超过正整数MaxSize个,所有元素类型都具有相同一数据类型ElemType,则可以用以下方式来定义顺序栈类型SqStacktypedef struct
{
ElemType daat[MaxSize];
int top; //栈顶指针
}SqStack;
小结
- 约定top总是指向栈顶元素,初始值为-1
- 当top=MaxSize-1时不能再进栈-栈满
- 进栈时top增1,出栈时top减1
顺序栈的四要素
栈空条件栈顶指针top=-1
栈满条件栈顶指针top=MaxSize-1
元素e进栈操作top++;将元素e放入top处
元素e出栈操作从top处取出元素e,top--
在顺序栈中实现栈基本运算的算法
InitStack(&s)初始化栈
建立一个新的栈s,实际上是将栈顶指针指向-1即可//初始化栈
void InitStack(SqStack* &s) {
//s为栈指针,top为s所指的栈的指针
s = (SqStack*)malloc(sizeof(SqStack)); //分配栈空间
s->top = -1; //初始化栈顶指针
}
注意:s为栈指针,top为s所指的栈的栈顶指针
DestroyStack(&s)销毁栈
释放栈s的存储空间//销毁栈
void DestroyStack(SqStack *&s) {
free(s)
}
StackEmpty(s)判断是否为空
栈s为空的条件是s->top==-1,即单链表的中没有数据元素//判断栈是否为空
bool StackEmpty(SqStack* s) {
return s->top == -1;
}
Push(&s,e)进栈
在栈不满的情况下,先将top指针+1,然后将元素e插入在top处//进栈
bool PushStack(SqStack* &s, Elemtype e) {
if (s->top == MaxSize - 1) //栈满
return false;
s->top++; //栈顶指针加1
s->data[s->top] = e; //将元素e赋值给栈顶指针处
return true;
}
Pop(&s,&e)出栈
在栈不为空的情况下,将栈顶元素赋值给e,然后再将栈顶指针-1//出栈
bool PopStack(SqStack*&s,Elemtype &e) {
if (s->top == -1)return false; //栈空
e=s->data[s->top]; //将栈顶指针元素值赋值给元素e
s->top--;
return true;
}
GetTop(s,&e)获取栈顶元素
在栈不为空的情况下,将栈顶元素赋值给e//取栈顶元素
bool GetTop(SqStack* s, Elemtype* e) {
if (s->top == -1)return false;
e = s->data[s->top];
return true;
}
顺序栈练习
设计一个算法利用顺序zhan判断一个字符串是否是对称串。所谓对称串是指从左往右读和从右往左读的序列是相同的。算法设计思路
字符串str的所有元素依次进栈,产生出栈序列正好与str的顺序相同=》str是对称串bool symmtry(Elemtype str[]) {
int i = 0;SqStack* s;Elemtype e;
InitStack(s); //初始化栈s
for ( i = 0; str[i]!='/0'; i++) //将所有元素进栈
{
Push(s, str[i]); //元素进栈
}
for ( i = 0; str[i] != '/0'; i++)
{
Pop(s, e); //退栈元素e
if (str[i] != e) { //若当前元素和元素e不同则不是对称串
DestroyStack(s); //销毁栈
return false; //返回假
}
}
DestroyStack(s);
return true;
}
栈的链式存储结构
栈的链式存储结构及其基本运算的实现
采用链式存储的栈称为链栈,这里采用带头结点的单链表实现链栈的四要素
栈空条件s->next==NULL;
栈满条件不考虑
元素e进栈操作将包含元素e的结点插入到头结点之后
元素e出栈操作去除头结点之后的元素并删除
链栈中数据结点的类型LiStack定义如下:
typedef struct LinkNode
{
Elemtype data; //数据域
struct LinkNode * next; //指针域
} LiStack;
在链栈中。栈的基本运算算法如下:
InitStack(&s)初始化栈
建立一个空栈,并且创建头结点,将头结点置为NULL//初始化栈
void InitStack(LiStack* &s) {
//s为栈指针,top为s所指的栈的指针
s = (LiStack*)malloc(sizeof(LiStack)); //分配栈空间
s->next = NULL; //初始化栈顶指针
}
DestroyStack(&s)销毁栈
释放栈s的全部存储空间//销毁栈
void DestroyStack(LiStack *&s) {
LiStack *p=s,*q=s-next;
while(q!=NULL)
{
free(p);
p=q;
q=p->next;
}
free(p); //此时p指向尾结点,释放其空间
}
StackEmpty(s)判断是否为空
栈s为空的条件是s->next==NULL,即单链表的中没有数据结点//判断栈是否为空
bool StackEmpty(LiStack* s) {
return s->next==NULL;
}
Push(&s,e)进栈
将数据结点插入到头结点之后//进栈
void PushStack(LiStack* &s, Elemtype e) {
LiStack *p;
p=(LiStack *)malloc(sizeof(LiStack));
p->data=e; //新建元素e对应的结点*p
p->next=s->nxet; //插入p结点作为s的开始结点
s->next=p;
}
Pop(&s,&e)出栈
在栈不为空的情况下,将头结点后继数据结点的数据域赋给e,然后将其删除//出栈
bool PopStack(LiStack*&s,Elemtype &e) {
LiStack *p;
if(s->next==NULL)return false;
p=s->next;
e=p->data;
s->next=p->next;
free(p);
retuen true;
}
GetTop(s,&e)获取栈顶元素
在栈不为空的情况下,将头结点后继数据元素结点的数据域赋值给e//取栈顶元素
bool GetTop(LiStack* s, Elemtype* e) {
if(s->next==NULL)return false;
e=s-next->data;
return true;
}
链栈和顺序栈两种存储结构有什么不同?
编写一个算法判断输入的表达式中括号是否配对(假设只含有左、右圆括号)
算法设计思路
一个表达式中左右括号是按最近位置配对的。所以利用一个栈来进行求解。这里采用链栈 遇到左括号进栈,遇到右括号出栈,最后查看栈内是否为空栈,如果是空栈那么成功配对,否则失败bool Match(char exp[],int n)
{
LiStack * s;
int i=0;
char e;
bool match=true;
InitStack(s); //初始化链栈
while(i<n&&match) //扫描exp中所有字符
{
if(exp[i]=='(') //遇到任何左括号都进栈
{
Push(s,exp[i])
}else if(exp[i]==')') //当前字符为右括号
{
if(GetTop(s,e)==true)
{
if(e!='('){match=false;} //栈顶元素不为左括号时不匹配
else{Pop(s,e)} //出栈
}
else
{
match=false; //无法取出栈顶元素时不匹配
}
}
}
if(!StackEmpty(s)) //运算完栈不空表示不匹配
return false;
DestroyStack(s);
return match;
}