作业要求
1.本次作业要求将程序写成.cpp和.h分离的形式
2.根据设计的类图进行编码,搭建主体框架
3.本次作业不要求实现核心的算法功能
4.发表一篇博客,博客内容为:提供本次作业的github链接,对栈的知识学习探索
这学期在上C语言的时候,老师在最后一两次课,有向我们讲了一下关于堆栈的概念,还很贴心的举了一些例子,希望给下个学期的数据结构过渡。并没有教我们如何编码,那个时候的堆栈题目可惜的是没有去做,所以这次是一个机会,一个学习堆栈的机会。刚开始的时候是在网上检索,结果并没有发现特别好的相关知识,最终去了图书馆。在图书馆里没有专门介绍堆栈的书籍,可能是因为堆栈的知识太少不足以出书,在在数据结构的第三、四章有介绍堆栈。
堆栈的学习
堆栈的概念
在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。堆栈都是一种按序列排序的数据结构,只能在一端(称为栈顶(top)对数据进行插入和删除。在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。要点:队列,先进先出;栈,先进后出
栈
1.栈的概念
栈是一种特殊的线性表,其特殊性体现在元素插入和删除运算上,它的插入和运算仅限定在表的某一端进行,不能在表中间和另一端进行。允许进行插入和删除的一端称为栈顶,另一端称为栈底。处于栈顶位置的数据元素称为栈顶元素。不含任何数据元素的栈称为空栈。举一个例子说明,假设有一个很窄的死胡同,胡同里能容纳若干个人,但是每次只允许一个人进出。这里人进出死胡同的原则是先进去的后出来。
2.栈的基本运算
- 初始化栈:InitStack(st),建立一个空栈st.
- 销毁栈:destroyStack(st),释放栈st占用的内存空间。
- 进栈:Push(st,x),将元素x插入栈st中,使x成为栈st的栈顶元素。
- 出栈:Pop(st,x),当栈st不空时,将栈顶元素赋给x,并从栈中删除当前栈顶。
- 取栈顶元素:GetTop(st),若栈不空时,返回栈顶元素;当栈st为空时,结果为一特殊标识(ERROR)。
- 判断栈空:StackEmpty(st),判断栈st是否为空栈。
3.栈的顺序存储结构
栈的顺序存储结构称为顺序表。顺序栈通常由一个一维数组data和一个记录顶元素位置的top组成。习惯上将栈低放在数组下标小的那端,栈顶元素由栈顶指针top所指向。
- 3.1顺序栈的定义如下:
#define MaxSize 100 //顺序栈的初始分配空间大小
typedef struct
{
ElemType data[MaxSize]; //保存栈中的元素,这里假设ElemType为char 类型
int top; //栈顶指针
}Sqstack;
在上述顺序栈定义中,ElemType为栈元素的数据类型,那么在这里表示的是一个字符型的栈;MaxSize为一个常量,表示data数组中最多可放的元素个数,因为数组的下标是从0开始的,所以data元素的下标范围为0~MaxSize-1;当top=-1时表示栈空,当top=MaxSize-1时表示栈满。
归纳起来,对于顺序栈st,其初始位置st.top=-1,它的四个要素:(1)栈空条件:st.top==-1;(2)栈满条件:st.top==MaxSize-1;(3)元素进栈操作:st.top++;将元素x放在st.data[st.top]中;(4)出栈元素x的操作:取出栈元素x=st.data[st.top];top--。
- 3.2初始化栈运算算法:其主要操作是设定栈顶指针top为-1。算法如下:
void InitStack(SqStack &st) //st为引用型参数
{
st.top=-1;
}
- 3.3销毁栈运算算法:这里顺序栈的内存空间是系统自动分配的,在不再需要时系统自动释放其空间。算法如下:
void DestroyStack(SqStack st)
{ }
- 3.4进栈运算算法:其主要操作是:进栈指针加一,将进栈元素放在栈顶处。算法如下:
int Push(SqStack &st,ElemType x)
{
if(st.top==MaxSize-1)return 0; //栈满上溢返回0
else
{
st.top++;
st.data[st.top]=x;
return 1; //成功进栈返回1
}
}
- 3.5出栈运算算法:其主要操作是:先将栈顶元素取出,然后将栈顶指针减一。算法如下:
int Pop(SqStack &st,ElemType &x) //x为引用型参数
{
if(st.top==-1)return 0; //栈空返回0
else
{
x=st.data[st.top];
st,top--;
return 1; //成功出栈返回1
}
}
- 3.6取栈顶元素运算算法:其主要操作是:将栈指针top处的元素取出赋给变量x。算法如下:
int GetTop(SqStack &st,ElemType &x) //x为引用型参数
{
if(st.top==-1)return 0; //栈空返回0
else
{
x=st.data[st.top];
return 1; //成功取栈顶元素返回1
}
}
- 3.7判断栈空运算算法:其主要操作是:若栈为空(top==-1)则返回值0,,否则返回值1。算法如下:
int StackEmpty(SqStack st)
{
if(st.top==-1)return 0;
else return -1;
}
- 3.8举例:回文指的是一个字符串从前面和从后面读都一样,如“123454321"、"abcba"都是回文。设计一个算法利用顺序栈的运算判断一个字符串是否为回文。
思路:因为回文是从前到后以及从后到前都是一样的,所以只要将待判断的字符串颠倒,然后与原字符串相比较,就可以决定是否为回文了。算法如下:
intishw(char str[]) //判断给定字符串str是否回文,是返回1,否则返回0
{
SqStack st; //定义一个顺序栈st
InitStack(st); //栈初始化
int i=0;char ch;
while((ch=str[i++])!='\0')Push(st,ch); //所有字符依次进栈
i=0; //从头开始遍历str
while(!StackEmpty(st)) //栈不空循环
{
Pop(st,ch);
if(ch!=str[i++]) //两字符不相同时返回0
{
DestroyStack(st);
return 0;
}
}
DestoryStack(st);
return 1; //所有相应字符都相同的时候返回1
}
4.栈的链式存储结构
栈的链式存储结构是采用某种链表结构,栈的链表存储结构简称为链栈。和链表的知识有一定的相关联,这里采用单链表作为链栈。
- 4.1链栈的定义如下:
typedef struct node
{
ElemType data; //存储结点数据,这里假设ElemType为char类型
struct node *next; //指针域
}LinkStack;
单链表的第一个结点就是链栈的栈顶结点,通常有一个指向栈顶的指针head,这里采取栈顶指针为ls.类似地,栈由栈顶指针ls唯一确定,栈中的其他结点通过它们的next域链接起来,栈底结点的next域为NULL。因为链栈本身没有容量限制,所以不考虑栈满的情况。
归纳起来,链栈ls初始时ls=NULL,其4个要素如下:(1)栈空条件:ls==NULL;(2)栈满条件:不考虑;(3)元素x进栈操作:创建存放元素x的结点*p,将其插入到栈顶位置上;(4)出栈元素x操作:置x为栈顶结点的data域,并删除该节点。
- 4.2初始化栈运算算法:其主要操作是:创建一个栈头结点*ls,用ls=NULL标识栈为空栈。算法如下:
void InitStack(LinkStack *&ls) //ls为引用型参数
{
ls=NULL;
}
- 4.3销毁栈运算算法:链栈的所有结点空间都是通过malloc函数分配的,在不需要时需通过free函数释放所有结点的空间。算法如下:
void DestroyStack(LinkStack *&ls)
{
LinkStack *pre=ls,*p;
if(pre==NULL)return ; //考虑空栈的情况
p=pre->next;
while(p!=NULL)
{
free(pre); //释放*pre结点
pre=p; //pre、p同步后移
p=p->next;
}
free(pre); //释放尾结点
}
- 4.4进栈运算算法:其主要操作是:先创建一个新的结点,其data域值为x;然后将该结点插入到*ls结点之后作为栈顶结点。算法如下:
void Push(LinkStack *&ls,ElemType x) //ls为引用型参数
{
LinkStack *p;
p=(LinkStack *)malloc(sizeof(LinkStack));
p->data=x; //创建结点*p用于存放x
p->next=ls; //插入*p结点作为栈顶结点
ls=p;
}
- 4.5出栈运算算法:其主要操作是:将栈顶结点(即ls所指结点)的data域值赋给x;然后删除该栈顶结点。算法如下:
int Pop(linkStack *&ls,Elemtype &x) //ls为引用型参数
{
Linkstack *p;
if(ls==NULL)return 0; //栈空,下溢出返回0
else
{ //栈不空时出栈元素x并返回1
p=ls; //p指向栈顶结点
x=p->data; //取栈顶元素x
ls=p->next; //删除结点*p
free(p); //释放*p结点
return 1;
}
}
- 4.6取栈顶元素运算算法:其主要操作是:将栈顶结点(即ls所指结点)的data域值赋给x。算法如下:
int GetTop(LinkStack *ls,ElemType &x)
{
if(ls==NULL)return 0; //栈空,下溢出时返回0
else
{
x=ls->data; //栈不空,取栈顶元素x并返回1
return 1;
}
}
- 4.7判断栈空运算算法:1其主要操作是:若栈为空(即ls==NULL)则返回值1,否则返回值0。算法如下:
int LinkStack(LinkStack *ls)
{
if(ls==NULL)return 1;
else return 0;
}
- 4.8举例设计一个算法,判断一个可能含有小括号(“(”和“)”)、中括号("["和"]")和大括号("{"和"}")的表达式中各类括号是否匹配。若匹配,则返回1;否则返回0。例如:[()],匹配;{(}),不匹配。
思路:设置一个栈st(这里用字符数组存放栈中的元素,另用一个整型变量top作为栈顶指针),用i扫描表达式exps,不考虑非括号字符。算法如下:
int mach(char *exps) //exps存放表达式
{
char st[MaxSize];
int nomatch=1,top=-1,i=0;
while(exps[i]!='\0'&&nomatch==1) //遍历表达式exps
{
switch(exps[i])
{
case'(':case'[':case'{': //左括号进栈
top++;st[top]=exps[i];break;
case'(': //遇到“)”
if(st[top]==')')top--; //判断栈顶是否为“(”
else nomatch=0;
break;
case'[': //遇到“]”
if(st[top]==']')top--;
//判断栈顶是否为“[”
else nomatch=0;
break;
case'{': //遇到“}”
if(st[top]=='}')top--;
//判断栈顶是否为“{”
else nomatch=0;
break;
default:
//跳过其他字符
break;
}
i++;
}
if(nomatch==1&&top==-1)return 1;
//栈空且符号匹配则返回1
else return 0;
//否则返回0
}
感想
这次自学栈的知识,举的两个例子都是上C语言课程的时候,老师有提到的典例,通过这次学习终于知道怎么回事了。栈的链式结构与链表有一点相似,但是栈只允许在栈顶进行相关操作,相比之下,栈显得更加简单。
代码问题
这次没有编写代码,所以就没有相关链接......