顺序栈
- 数据结构(c语言)
- 基本概念
- 常见操作
基本概念:
栈stack是限定只在表尾进行插入和删除操作的线性表
栈stack是一种LIFO(后进先出)或者说FILO(先进后出)的结构,可以想象成一叠摆放好的餐盘,最上面的餐盘是最后放上去的但是要最先拿出来。
顺序栈有两种定义方式,分别是
typedef struct
{Selemtype data[MAXSIZE];
int top;
}SqStack;
和
typedef struct
{Selemtype *bottom;
Selemtype*top;
int stack_size;
}SqStack;
提问:最先入栈的一定最后出栈吗?
让1 2 3三个数字按照大小顺序入栈,则出栈的顺序可能会
①1 2 3(入1出1,入2出2,入3出3);
②1 3 2 (入1 出1,入2、3出3 、2)
③2 3 1(入1、2出2,入3出3)
④2 1 3(入1、2出2、1)
⑤3 2 1
所以最先入栈的不一定是最后出栈的,因为不一定一次就把所有元素入栈,有可能出现入栈出栈交错进行的这种状况
常见操作:
栈本身是一种线性表,所以它的实现分为顺序栈(基于顺序表),和链栈(基于链表)。常见的操作有
1判空、判满
第一种
empty_sqstack(SqStack*p)
{
if(p->top==-1)
printf("\n此栈为空\n");
else
printf("\n此栈不空\n");
}
full_sqstack(SqStack*p)
{
if(p->top==MAXSIZE-1)
printf("栈满");
}
思考题
怎么理解判空条件
一般认为p->top= =n,就是p[n]存在,p的数组里面最小下标是p->top==0,所以没有元素就初始化为-1,判空条件也因此是-1;
第二种
empty_sqstack(SqStack*p)
{
if(p->bottom==p->top)
printf("\n此栈为空\n");
else
printf("\n此栈不空\n");
}
full_sqstack(SqStack*p)
{
if(p->top-(p->bottom)==p->stack_size)
printf("栈满");
}
思考题
为什么判满条件不是为什么这里不是p->top-(p->bottom)==p->stack_size-1?(明明->stack_size-1才是它们之间的元素间隔个数呀)
栈的最后一个元素是*(p->top-1),也就是说最后一位元素的下一位才是top指针
2压栈(push)
线性表的表尾称为栈顶,压栈(插入元素)和出栈(删除元素)都是发生在栈顶。
第一种
push(SqStack*p)
{ Selemtype e;
printf("请输入压栈的元素:姓名 学号");
scanf("%s %d",e.name,&(e.stu_num));
p->data[++p->top]=e;
}
第二种
push(SqStack*p)
{ Selemtype e;
printf("请输入压栈的元素:姓名 学号");
scanf("%s %d",e.name,&(e.stu_num));
*(++p->top)=e;
}
3出栈(pop)
第一种
pop(SqStack*p,Selemtype*e )
{
*e=p->data[p->top--];
}
第二种
Selemtype pop(SqStack*p)
{Selemtype e;
e=*((p->top)--);
return e;
}
思考题(总代码在最后)
为什么代码块换成
pop(SqStack*p,Selemtype*e )
{
e=(p->top)--;
}
···
int main()
{···
pop(&st,&b);
···}
输出的弹栈结果是
但真实结果应该是
答案:(暂时没想到答案)
2019.10.19:两个指针之间只能够进行值的赋值,因为指针本身表示储存空间的位置,如果对指针进行赋值可能会出现两个储存空间不同,但是在系统中表示地址的值一样的错误,所以不能够对指针本身赋值(猜想)。
4总代码
第一种建栈方式代码
整形数组的栈使用方法
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
typedef int Selemtype;
typedef struct/*注释1:栈的顺序表结构这里是利用静态数组,所以也就不需要分配内存空间,但是,如果这里不是一个int数组呢,是否需要分配内存空间?*/
{Selemtype data[MAXSIZE];/*注释2:在链表,动态数组里面都有一个指针,我猜这里的数组也可以换成指针*/
int top;/*注释3:用于data->top表示栈顶,也就是所谓的栈顶指针*/
}SqStack;
init_sqstack(SqStack*p)
{
p->top=-1;/*注释4:一般认为p->top==n,就是p[n]存在,p的数组里面最小下标是p->top==0,所以没有元素就初始化为-1;*/
}
empty_sqstack(SqStack*p)
{
if(p->top==-1)
printf("\n此栈为空\n");
else
printf("\n此栈不空\n");
}
full_sqstack(SqStack*p)
{
if(p->top==MAXSIZE-1)
printf("栈满");
}
pop(SqStack*p,int*e )
{
*e=p->data[p->top--];
}
push(SqStack*p,int *e)//注释5:这里第二个形参可以是int e,但是必须有返回值。但是也可以直接传入e的地址
{p->data[++p->top]=*e;//注释6:i++返回i的值,++i返回i+1之后的值;
}
print(SqStack*p)
{
while(p->top!=-1)
printf("\n%d",p->data[p->top--]);
}
int main()
{
int a=100,b=99;/*注释7:a用来压栈,b用来弹栈*/
SqStack st={{1,2,3,4},3};/*注释8:top其实是数组的下角标,所以这里的值是3;*/
//print(&st);注释9
//init_sqstack(&st);注释10
/*注释11:以上两个注释让我明白了①见思考题1②st类型的数据在定义的时候就已经给赋值,所以这里的init_sqstack并没有实际的作用*/
empty_sqstack(&st);
push(&st,&a);
printf("压栈后栈顶元素为:%d",st.data[st.top]);
pop(&st,&b);
printf("\n打印依次出栈的元素");
printf("\n");
print(&st);
printf("\nb的值是:%d\n",b);//注释12:栈的缺点,他只能够全部直接输出栈内的元素只一次,如果要输出多次的话就只能事先存储好st->top值的大小
}
结果图:
思考题
1
如果注释9去掉//和"注释9"字样,栈会正常输出吗?
不会,因为输出之后st->top==-1,然而后续的弹栈,压栈操作又会对st->top进行增减,所以实际的结果就输出无限循环(请实操)
2
结合注释12,思考能不能将print函数变成这样,以便实现注释9中不能够多次输出的问题?
print(SqStack*p)
{ int i;
i=p->top;
while(i!=-1)
printf("\n%d",p->data[i--]);
}
最开始以为:这样能够避免栈只能够输出一次的这个缺点((▽)伦家想要输出多少次都可以实操啦)
然而:这是一个栈不是一个数组,栈里面的元素当然只能够出一次。而且p->top表示的栈顶指针,如果像上面的代码块一样实施,p->top的值始终都是3,出栈这个操作也就名不副实了。
为了解决注释1中能否分配空间问题,写了如下代码:
结构体数组的栈使用方法
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
typedef struct
{char name[3];
int stu_num;
}Selemtype;
typedef struct
{Selemtype *data;/*注释1:在链表,动态数组里面都有一个指针,这里的数组也可以换成指针*/
int top;
}SqStack;
init_sqstack(SqStack*p)
{
p->top=-1;
p->data=(Selemtype*)malloc(MAXSIZE*sizeof(Selemtype));/*注释2:初始化的时候进行分配空间和栈顶指针赋值*/
}
empty_sqstack(SqStack*p)
{
if(p->top==-1)
printf("\n此栈为空\n");
else
printf("\n此栈不空\n");
}
full_sqstack(SqStack*p)
{
if(p->top==MAXSIZE-1)
printf("栈满");
}
pop(SqStack*p,Selemtype*e )
{
*e=p->data[p->top--];
}
push(SqStack*p)
{ Selemtype e;
printf("请输入压栈的元素:姓名 学号");
scanf("%s %d",e.name,&(e.stu_num));
p->data[++p->top]=e;
}
print(SqStack*p)
{
for(;p->top>=0;p->top--)
printf("\n%s %d\n",p->data[p->top].name,p->data[p->top].stu_num);
}
int main()
{
Selemtype b;
SqStack st;//={{1,2,3,4},3}
//print(&st);
init_sqstack(&st);
printf("%d\n",st.top);
while(st.top<3)
{
push(&st);
}/*注释3:想压栈3个既定元素*/
empty_sqstack(&st);
push(&st);/*注释4:再输入一个元素进行压栈,这一步很无聊,也可以直接忽略*/
pop(&st,&b);
printf("输出");
print(&st);
printf("被弹栈的元素是");
printf("\n%s %d\n",b.name,b.stu_num);
}
结果图
思考题:
如果将注释1前的代码Selemtype *data;
变成Selemtype data[5];
即将指针变成一个结构体数组那么
scanf("%s %d",e.name,&(e.stu_num));
还能够正常编译出吗?
不行
不能够直接在指针没有指向的基础上进行赋值,或者malloc一类的运算。
2019.10.19
否定原因:如果是指针没有指向的问题的话换成Selemtype *data后传入pop函数的还是st的地址,指针依旧没有确切的指向?
依旧没有答案
第二种建栈方式代码
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
typedef struct
{char name[3];
int stu_num;
}Selemtype;
typedef struct
{Selemtype *bottom;
Selemtype*top;
int stack_size;
}SqStack;
init_sqstack(SqStack*p)
{
p->bottom=(Selemtype*)malloc(MAXSIZE*sizeof(Selemtype));/*注释2:初始化的时候进行分配空间和栈顶指针赋值*/
p->top=p->bottom;
p->stack_size=MAXSIZE;
}
empty_sqstack(SqStack*p)
{
if(p->bottom==p->top)
printf("\n此栈为空\n");
else
printf("\n此栈不空\n");
}
full_sqstack(SqStack*p)
{
if(p->top-(p->bottom)==p->stack_size)/*注释1:为什么这里不是=p->stack_size+1,猜想:链表的最后一个元素是*(p->top-1),也就是说最后一位元素的下一位才是top指针,这也就解释了为什么没有给top开辟空间*/
printf("栈满");
}
Selemtype pop(SqStack*p )
{
Selemtype e;
e=*(p->top--);
return e;
}
push(SqStack*p)
{ Selemtype e;
printf("请输入压栈的元素:姓名 学号");
scanf("%s %d",e.name,&(e.stu_num));
*(++p->top)=e;
}
print(SqStack*p)
{
for(;p->top>p->bottom;p->top--)
printf("\n%s %d\n",p->top->name,p->top->stu_num);
}
int main()
{
Selemtype b;
SqStack st;//={{1,2,3,4},3}
//print(&st);
init_sqstack(&st);
while(st.top-st.bottom<3)
{
push(&st);
}/*注释3:想压栈3个既定元素*/
empty_sqstack(&st);
push(&st);/*注释4:再输入一个元素进行压栈*/
b=pop(&st);
printf("输出");
print(&st);
printf("被弹栈的元素是");
printf("\n%s %d\n",b.name,b.stu_num);
}
结果图