栈的基本概念
线性表是具有相同数据类型的n个数据元素的有限序列,其中n为表长。
栈是只允许在栈顶进行插入或删除操作的线性表
如洗的盘子和 插肉串
栈顶:允许插入和删除的一端
栈底:不允许插入和删除的一端
特点:后进先出 Last In First Out(LIFO)
栈的基本操作
如果出栈操作和进展操作交叉进行,则会有多种出栈顺序。
n个不同的元素进栈,出栈元素不同排列的个数为
栈的顺序存储实现
顺序存储,用静态数组实现,并需要记录栈顶指针
顺序栈:用顺序存储方式实现的栈
基本操作:
- 创(初始化)
- 增(进栈)
- 删(出栈)
- 查(获取栈顶元素)
- 判空、判满
顺序栈的定义
#define MaxSize 10 //栈中元素的最大个数
typedef struct{
int data[MaxSize]; //静态数组存放栈中元素
int top; //栈顶指针
}SqStack; //sq:sequence
void InitStack(SqStack &S){
S.top=-1;
}
bool StackEmpty(SqStack S){
return (S.top==-1);
}
int main(){
SqStack S;
InitStack(S);
}
top指向栈顶元素,如果此时存入了五个元素,则top=4.(数组下标)
top指针不能指向0,因为此时data[0]还没有储存元素,因此可以把top的值设为负一。
要判断栈是否为空,只要看top的值是否为负一即可。
进栈操作
首先要判断栈是否已满,然后将指针加1,令新元素入栈
#define MaxSize 10 //栈中元素的最大个数
typedef struct{
int data[MaxSize]; //静态数组存放栈中元素
int top; //栈顶指针
}SqStack; //sq:sequence
void InitStack(SqStack &S){
S.top=-1;
}
bool StackEmpty(SqStack S){
return (S.top==-1);
}
bool Push(SqStack &S,int x){ //新元素入栈
if(S.top==MaxSize-1) //栈满
return false;
S.top=S.top+1; //指针先加1
S.data[S.top=x]; //新元素入栈
//更简洁的写法:
//S.data[++S.top]=x;
return true;
}
int main(){
SqStack S;
InitStack(S);
Push(S,1);
}
出栈操作
Pop(SqStack &S,int &x)中x有&,
出栈函数的调用者首先会在自己得函数里面定义一个变量,存放在内存中的某一个位置,加了引用符号后,在出栈操作里面的变量x和函数调用者定义的x对应的是同一份数据而不是复制品。
#define MaxSize 10 //栈中元素的最大个数
typedef struct{
int data[MaxSize]; //静态数组存放栈中元素
int top; //栈顶指针
}SqStack; //sq:sequence
void InitStack(SqStack &S){
S.top=-1;
}
bool Pop(SqStack &S,int &x){
if (S.top==-1) //栈空,报错
return false;
x=S.data[S.top]; //栈顶元素先出栈
S.top=S.top-1; //指针再减1
//等价于
//x=S.data[S.top--];
return true;
}
删除后数据还残留在内存中,只是逻辑上被删除了。
x=S.data[–S.top];等价于 S.top=S.top-1; x=S.data[S.top];
读栈顶操作
与出栈操作几乎一样,只是top不用- -
#define MaxSize 10 //栈中元素的最大个数
typedef struct{
int data[MaxSize]; //静态数组存放栈中元素
int top; //栈顶指针
}SqStack; //sq:sequence
bool GetTop(SqStack S,int &x){
if(S.top==-1)
return false;
x=S.data[S.top] ; //x记录栈顶元素
return true;
}
另一种方式 top=0而不是-1
top指针初始指向0,此时指向了下一个可以插入元素的位置
#define MaxSize 10 //栈中元素的最大个数
typedef struct{
int data[MaxSize]; //静态数组存放栈中元素
int top; //栈顶指针
}SqStack; //sq:sequence
void InitStack(SqStack &S){
S.top=0; //初始化栈顶指针指向0
}
bool StackEmpty(SqStack S){
return (S.top==0);
}
bool Push(SqStack &S,int x){ //新元素入栈
if(S.top==MaxSize) //栈满
return false;
S.data[S.top++]=x;
return true;
}
bool Pop(SqStack &S,int &x){
if (S.top==0) //栈空,报错
return false;
x=S.data[--S.top];
return true;
}
bool GetTop(SqStack S,int &x){
if(S.top==0)
return false;
x=S.data[S.top] ; //x记录栈顶元素
return true;
}
int main(){
SqStack S;
InitStack(S);
Push(S,1);
}
共享栈
顺序栈的缺点:栈的大小不可变
共享栈:两个栈共享同一片存储空间
栈满的条件:top0+1==top1
往0号栈放入数据元素,栈顶是从下往上依次递增
往1号栈放入数据元素,栈顶是从上往下依次递增
#define MaxSize 10 //栈中元素的最大个数
typedef struct{
int data[MaxSize]; //静态数组存放栈中元素
int top0; //0号栈栈顶指针
int top1; //1号栈栈顶指针
}ShStack;
void InitStack(SqStack &S){
S.top0=0; //初始化栈顶指针
S.top1=MaxSize;
}
销毁顺序栈
1.逻辑上清空一个栈 令top=-1 指向初始化的位置
2.回收这个栈所占用的内存资源 使用的是变量声明分配内存空间,没有使用malloc函数,故函数运行结束后系统自动回收内存。
栈的链式存储实现
出栈/进栈都只能在栈顶一端进行(链头作为栈顶)
typedef struct LinkNode{
int data; //数据域
struct LinkNode *next; //指针域
}*LiStack; //栈类型定义
栈的基本操作:
- 初始化
- 进栈
- 出栈
- 获取栈顶元素
- 判空、判满
不带头结点:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct LinkNode{
int data; //数据域
struct LinkNode *next; //指针域
}Linode,*LiStack; //栈类型定义
//初始化
bool InitStack(LiStack &L){
L=NULL; //防止脏数据
return true;
}
//判空
bool isEmpty(LiStack L){
return (L==NULL);
}
//进栈
void PushStack(LiStack &L){
int x;
scanf("%d",&x);
while(x!=9999){
Linode *p=(Linode *)malloc(sizeof(Linode));
assert(p);
p->data=x;
p->next=L; //不带头结点
L=p;
scanf("%d",&x);
}
}
//出栈
bool PopStack(LiStack &L,int &x){
Linode *p;
if(L){
p=L;
x=p->data;
L=L->next;
free(p);
return true;
}
return false;
}
//获取栈顶元素
bool GetStack(LiStack &L,int &x){
if(L){
x=L->data;
return true;
}
return false;
}
//求表长
int StackLength(LiStack L)
{
int count = 0;
Linode *p=L;
while (p)
{
count++;
p = p->next;
}
return count;
}
//打印链栈
void PrintStack(LiStack L){
if(L){
while(L){
printf("%d ",L->data);
L=L->next;
}
printf("\n");
}
}
int main(){
LiStack L;
InitStack(L);
printf("%d\n",isEmpty(L));
PushStack(L);
int x;
int a;
printf("%d\n",PopStack(L,x));
printf("%d\n",x);
printf("%d\n",GetStack(L,a));
printf("%d\n",a);
printf("%d\n",StackLength(L));
PrintStack(L);
}
带头结点:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct LinkNode{
int data; //数据域
struct LinkNode *next; //指针域
}Linode,*LiStack; //栈类型定义
//初始化
bool InitStack(LiStack &L){
L=(Linode *)malloc(sizeof(Linode));
if(L==NULL){
return false;
}
L->next=NULL; //防止脏数据
return true;
}
//判空
bool isEmpty(LiStack L){
return (L->next==NULL);
}
//进栈
void PushStack(LiStack &L){
int x;
scanf("%d",&x);
while(x!=9999){
Linode *p=(Linode *)malloc(sizeof(Linode));
assert(p);
p->data=x;
p->next=L->next; //带头结点
L->next=p;
scanf("%d",&x);
}
}
//出栈
bool PopStack(LiStack &L,int &x){
Linode *p;
if(L){
p=L->next;
x=p->data;
L->next=L->next->next;
free(p);
return true;
}
return false;
}
//获取栈顶元素
bool GetStack(LiStack &L,int &x){
if(L){
x=L->next->data;
return true;
}
return false;
}
//求表长
int StackLength(LiStack L)
{
int count = 0;
Linode *p=L->next;
while (p)
{
count++;
p = p->next;
}
return count;
}
//打印链栈
void PrintStack(LiStack L){
if(L){
while(L){
printf("%d ",L->next->data);
L->next=L->next->next;
}
printf("\n");
}
}
int main(){
LiStack L;
InitStack(L);
printf("%d\n",isEmpty(L));
PushStack(L);
int x;
int a;
printf("%d\n",PopStack(L,x));
printf("%d\n",x);
printf("%d\n",GetStack(L,a));
printf("%d\n",a);
printf("%d\n",StackLength(L));
PrintStack(L);
}