一、基本概念
栈是只允许在一段进行插入和删除的线性表。
假如有一组数{1,2,3,4},存入栈里,1先到达栈底,然后依次进入,此时从栈底到栈顶分别为1,2,3,4
当要把栈中的数取出去,由于我们只能在栈顶操作,所以先取出4,依次为3,2,1
可以明显看到,有着先进后出的特点。
栈顶:线性表允许进行插入和删除的一端。
栈底:固定的,不允许插入和删除的另一端。
空栈:不含任何元素的空表。
先来分析一波。即然是个栈,它可能是空的,也可能非空。
我们往进存东西,先要看看它,是否为空,若空着,直接存,否则我们就不能存。
当我们要取东西的是否,先看看这个栈空不空,若为空,啥也没有就不能取出东西,有的话就取。
基本操作:
- InitStack(&S):初始化一个空栈
- StackEmpty(S),判断一个栈是否为空,若栈空则返回true,否则返回false
- Push(&S,x):进栈,若栈S未满,则将x加入使之成为新栈顶
- Pop&S,x):出栈,若栈S非空,则用x返回栈顶元素
- ClearStack(&S):销毁栈,并释放栈S占用的存储空间,
此外还有个重要的公式,当n个元素以某种顺序进栈,并在任意时刻出栈(满足先进后出的前提),
所获得的元素排列数目N恰好满足Catalan()函数的计算,
即然栈它是操作受限的线性表,按存储结构分为:顺序栈和链式栈
二、顺序栈⭐⭐⭐⭐
采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶的位置
1.顺序栈的实现
#define MaxSize 100 //定义栈中元素的最大个数
typedef struct{
Elemtype data[MaxSize];
int top;
}SqStack;
2.基本状态和操作
(1)栈空状态
S.top==-1
为啥栈空不是用0表示呢?在有些书上是s.top===0,因为这样会浪费一个元素大小的空间。所以不少书都用-1
数组下标从0开始,栈空应当s.top<0,因此栈空时栈顶指针s.top==-1
(2)栈满状态
S.top==maxSize-1
maxSize是栈中最大元素个数,则maxSize-1 为栈满时栈顶元素在数组中的位置,因为数组下标从0开始。这里规定指针top为-1时栈空,即top==0的数组位置也可以存有数据元素
(3)进栈操作
元素x进栈,S.data[++S.top]=x分解为两步就是 ++(S.top) 和S.data[S.top]=x
即然规定top为-1时栈空,则元素进栈猜哦在必须是先移动指针,再进入元素,因为数组下标不存在-1
(4)出栈操作
x=S.data[S.top--]分解为两步就是 x=S.data[S.top] 和 --(S.top)
进栈次序决定了出栈操作次序,因为进栈操作时先变动栈顶指针,再存入元素,
因此出栈操作必须为先取出元素,再变动指针。
如果反过来,会丢失栈顶元素
基本运算实现
(1)初始化
void initStack(SqStack &st)
{ s.top==-1;} ///初始化栈顶指针
(2)判栈空
bool StackEmpty(Stack S){
if(S.top==-1) return true;
else return false;
}
(3)进栈
bool Push(SqStack &S,Elemtype x){
if(S.top==MaxSize-1) //栈满报错
return false;
S.data[++S.top]=x; //指针先加1,再入栈
return true;
}
(4)出栈
bool Pop(SqStack &S,Elemtype x){
if(S.top==-1) //栈空,报错
return false;
x=S.data[S.top--]; //先出栈,再减1
return true;
}
(5)读栈顶元素
bool GetTop(SqStack S,Elemtype &x){
if(S.top==-1) return false; //栈空报错
x=S.data[S.top]; //记录栈顶元素
return true;
}
3.共享栈
利用栈底位置相对不变的特性,可让两个顺序栈共享一个一维数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
连个栈的栈顶指针都指向栈顶元素,top0=-1时,0号占位空,top1=MaxSize-1时1号栈为空;
仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满。当0号栈进栈时,top0先加1,在赋值,1号栈进栈时top1先减去1在赋值;出栈时刚刚相反。
共享栈时为了有效利用存储空间,两个栈的空间相互调节,只在整个存储空间被占满菜发生上溢。
三、链栈
采用链式存储的栈称为链栈,它的优点时便于多个栈共享存储空间和提高效率,且不存在栈满上溢。通常采用单链表实现,并规定所有操作都是在表头进行
typedef struct LinkStack{
Elemtype data; //数据域
struct LinkStack *next; //指针域
} *LStack; //栈类型定义
下面的了解即可
//初始化
void initQueue(LiQueue *&lqu){
lqu=(LiQueue*)malloc(sizeof(LiQueue));
lqu->front=lqu->rear=NULL;
}
判空
int isQuenueEmpty(LiQueue *lqu){
if(lqu->rear==NULL || lqu->front==NULL)
return 1;
else
return 0;
}
void enQueue(LiQueue * lqu,int x){ //入队
QNode *p;
p=(QNode*)malloc(sizeof(QNode));
p->data=x;
p->next=NULL;
if(lqu->rear==NULL)
lqu->front=lqu->rear=p;
else
{
lqu->rear->next=p;
lqu->reaer=p;
}
}
//来自https://bbs.csdn.net/topics/392467555
//大佬代码收藏下
#include<stdio.h>
#define Size 50
typedef char stackelemtype;
typedef struct
{
stackelemtype elem[Size];
int top;
}seqstack;
void Initstack(seqstack *s)
{
s->top = -1;
}
int push(seqstack *s,stackelemtype x)
{
if(s->top == Size-1)
return 0;
s->top++;
s->elem[s->top] = x;
return 1;
}
int pop(seqstack *s,stackelemtype *x)
{
if(s->top == -1)
return 0;
//else
//{
*x = s->elem[s->top];
s->top--;
return 1;
//}
}
int gettop(seqstack *s,stackelemtype *x)
{
if(s->top==-1)
return 0;
printf("弹出栈顶元素\n");
*x = s->elem[s->top];
printf("%c\n", *x);
return 1;
}
void OutStack(seqstack*s)
{ //输出这个栈
if(s->top==-1) {
printf("这是一个空栈");
return;
}
//else{
while(s->top > -1)
{
printf("%c ", s->elem[s->top]);
s->top--;
}
//}
}
int main()
{
seqstack p;
int i,k;
//char b[Size], x=NULL;
char b[Size], x = 0;
Initstack(&p);//初使化顺序栈
printf("加入元素个数\n");
scanf("%d",&k);
printf("输入栈的元素\n");
for(i=0;i<k+1;i++)
scanf("%c", &b[i]);
for(i=0;i<k+1;i++)
push(&p,b[i]);
gettop(&p, &x);
printf("top elem = %c\n", x);
pop(&p,&x);
printf("剩余栈的元素\n");
OutStack(&p);
printf("\n");
return 0;
}