【数据结构】顺序栈,链栈,双栈基本操作

一.栈的基本概念

定义:

限定只能在表的一端(栈顶)进行插入和删除运算的线性表。表尾(an端)称为栈顶,表头(a1端)称为栈底

表达式S=(a1,a2,...,an)

入栈:插入元素到栈顶

出栈:从栈顶删除最后一个元素

运算规则:先进后出 LIFO

存储结构:顺序栈,链栈

逻辑结构:一对一

已知入栈序列,入栈过程中允许出栈,判断可能的出栈序列

入栈序列为...,aj,...,ak,...ai,...,那么一定不存在这样的出栈序列:...ai,...aj,...ak,...。

比如有输入序列1,2 ,3 。那么不可能出现出栈序列为3,1,2

c,a,b,d     出栈序列不会有c,d,a,b或者b,c,d,a;

二.顺序栈的表示与实现

顺序栈存储结构: 与顺序表十分相似

利用一组地址连续的存储单元依次存放从栈底到栈顶(地址从小到大)的数据元素,同时设置指针top指向栈顶元素的位置。栈底指针base始终指向栈底元素,top初值指向栈底,元素入栈时始终指向栈顶元素的下一个位置(我的理解:top=NULL,所以下面写到的出栈操作要先减再弹)。

【注】

1.栈空标志:base==top;栈满标志:top-base==MaxSize;栈不存在:base=NULL;

2.入栈口诀:top先压后加 S[top++]=an  赋值完成后指针移动

   出栈口诀:先减后弹 e=S[--top]  (一定要预设top)

代码实现

/*顺序栈*/

#include "stdio.h"
#include "stdlib.h"
#include "malloc.h"

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10

typedef char SElemtype;//以char为例

typedef struct
{
	SElemtype *base;//栈底指针。构造之前和销毁之后其值为NULL
	SElemtype *top;//栈顶指针
	int stacksize;//当前分配的内存空间
}SqStack;

/*初始化*/
void InitStack(SqStack &S)
{
	S.base=(SElemtype*)malloc(STACK_INIT_SIZE*sizeof(SElemtype));
	if(!S.base) printf("Error");
	S.top=S.base;//栈空
	S.stacksize=STACK_INIT_SIZE;
}

/*判断是否为空*/
bool StackEmpty(SqStack S)
{
	if(S.top==S.base) return 1;
	else return 0;
}

/*求长度*/
int StackLength(SqStack S)
{
	return S.top-S.base;
}

/*清空栈*/
void ClearStack(SqStack &S)
{
	if(S.base) S.top=S.base;//栈存在,则让栈空。实际上是让栈的长度为0,栈中不存在有效数据,而让栈底内存保留,是一种逻辑上的清空,而非物理上的释放内存。需要与销毁区分
}

/*销毁栈*/
void DestoryStack(SqStack &S)
{
	if(S.base)
	{
		free(S.base);//释放才是销毁。S.base动态分配的内存块的首地址,那么整个内存块会被释放
		S.stacksize=0;//内存空间为0
		S.base=S.top=NULL;
	}
}

/*进栈*/
void Push(SqStack &S,SElemtype e)
{
	if(S.top-S.base>=S.stacksize)//栈满
	{
		S.base=(SElemtype*)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemtype));
		if(!S.base) printf("Error");//分配失败
		S.top=S.base+S.stacksize;//注意:新增的空间并不是放在栈的某个位置,栈的内存块被重新分配和扩展(这是一个新的,更大的内存块)。
		//新内存块的起始地址更新为S.base,所以S.top相应也要更改。此时应该是刚好栈满,标志就是S.top-S.base=S.stacksize,所以top的值就有了
		S.stacksize+=STACKINCREMENT;	
	}
	*S.top++=e;//先压后加
}

/*出栈*/
void Pop(SqStack &S,SElemtype &e)
{
	if(S.top==S.base)//栈空
	printf("Error");
	e=*--S.top;//返回到e.先减后出
}

/*取栈顶元素*/
SElemtype GetTop(SqStack S,SElemtype &e)
{
	if(S.top==S.base) printf("Error");//栈空
	e=*(S.top-1);//!!!!减1。top指向栈顶元素上方一个
	return e; 
}

int main()
{
	SqStack S;
	SElemtype e;
	InitStack(S);
	printf("栈是否为空:%d",StackEmpty(S));
	Push(S,'B');
	Push(S,'a');
	Push(S,'n');
	Push(S,'a');
	Push(S,'n');
	Push(S,'a');
	printf("\n");
	while(!StackEmpty(S))
	{
		printf("%c",GetTop(S,e));
		Pop(S,e);
	}
}

结果:

最终逆序输出,符合先进后出

【习题】:.在一个具有n个单元的顺序栈中,假设以地址高端作为栈底,以top作为栈顶指针,则当作进栈处理时,top 的变化为 top--。分析top移动的方向:向下。(从上到下 地址从高到低)

三.链栈的表示与实现

与单链表也很相似

代码实现

/*链栈*/
#include "stdio.h"
#include "stdlib.h"
#include "malloc.h"
typedef int SElemtype;
typedef struct StackNode
{
	SElemtype data;
	struct StackNode *next;	
}StackNode,*LinkStack;//结点类型

/*初始化*/
void InitStack(LinkStack &S)
{
	S=NULL;
}
/*判断是否为空*/
int StackEmpty(LinkStack S)
{
	if(S==NULL) return 1;//注意S是栈顶结点,只有它不存在,链栈才为空。而s->next为空,只能说明栈顶结点没有后续节点
	else return 0;
}
/*进栈*/
void Push(LinkStack &S,SElemtype e)
{
	LinkStack p=(LinkStack)malloc(sizeof(StackNode));//生成新结点p
	if(!p) printf("Error");//失败
	p->data=e;//数据域
	p->next=S;//p的next指向S
	S=p;//p成为栈顶结点
}
/*出栈*/
void Pop(LinkStack &S,SElemtype &e)
{
	if(S==NULL) printf("Error");
	e=S->data;
	LinkStack p=S;
	S=S->next;
	free(p);
}
/*取栈顶元素*/
SElemtype GetTop(LinkStack S)
{
	if(S==NULL) return 0;
	else return S->data;
}

int main()
{
	LinkStack S;
	InitStack(S);
	SElemtype e;
	Push(S,1);Push(S,2);Push(S,3);Push(S,6);Push(S,7);
	Pop(S,e);
	printf("popped:%d ",e);
	while(StackEmpty(S)==0)
	{
		printf("top:%d ",GetTop(S));
		Pop(S,e);
	}
}

四.双栈共享一个栈空间

编号为0和1的两个栈存放于一个数组空间V[m]中,栈底分别处于数组的两端。当第0号栈的栈顶指针top[0]等于-1时该栈为空,当第1号栈的栈顶指针top[1]等于m时该栈为空。两个栈均从两端向中间增长

优点:互相调剂,灵活性强,减少溢出机会

【注】1.栈空:top[i]==bot[i] i 表示栈编号;栈满:top[0]+1==top[1] 两个栈顶指针相遇

关于双栈中的指针:

top[i],bot[i]是整型变量,记录的是数组的位置索引比如top[0]=2表示栈0的栈顶元素在V[2]。将他们称为指针是因为功能类似与指针----标记位置并动态移动。

为什么用下标而非真实指针?
数组的特性:数组通过下标直接访问元素,无需像链表那样存储地址。  
操作简化:  `top[0]++` 等价于“指针右移”。  `top[1]--` 等价于“指针左移”。 

代码实现

#include "stdio.h"
#include "stdlib.h"
typedef int Status;
typedef int SElemtype;
//双栈共享空间
typedef struct 
{
	int top[2],bot[2];//栈顶栈底指针
	SElemtype *V;//栈数组:同时存储“两个栈”的元素
	int maxsize;//栈最大可容纳元素数
}DblStack;
/*初始化一个大小为m的双向栈S*/
Status Init_Stack(DblStack &S,int m)
{
	S.V=(SElemtype*)malloc(m*sizeof(SElemtype));//!!!
	if(!S.V) return 0;
	S.maxsize=m;//最大能容纳的元素个数
	S.top[0]=S.bot[0]=-1;//栈0.此时栈空
	S.top[1]=S.bot[1]=m;//栈1
	return 1;
}
/*把x插入栈i*/
Status Dblpush(DblStack &S,int i,SElemtype x)
{
	if(S.top[1]-S.top[0]==1)
	printf("栈满,无法入栈\n");
	switch (i)
	{
		case 0:S.V[++S.top[0]]=x;break;//左栈(0)右栈(1)
		case 1:S.V[--S.top[1]]=x;break;//1.给栈数组赋值S.V[]=x;2.确定要赋值的位置:改变S.top[i];3.将位置带入栈数组
		default:break;
	}
	return 1;
}
/*退掉位于栈i栈顶的元素*/
Status Dblpop(DblStack &S,int i,SElemtype &x)
{
	if(S.top[i]==S.bot[i])
	{
		printf("栈空,无数据元素出栈");
	}
	switch (i)
	{
		case 0:x=S.V[S.top[0]--];break;
		case 1:x=S.V[S.top[1]--];break;
		default:printf("无效编号");break;
	}
	return 1;
}
/*判断栈i空否,空返回1,否则返回0*/
int IsEmpty(DblStack &S,int i)
{
	return (S.top[i]==S.bot[i]);
}
/*栈满*/
int IsFull(DblStack S)
{
	if(S.top[0]+1==S.top[1])
	return 1;
	else return 0;
}
int main()
{
	DblStack DS;
	SElemtype x;
	Init_Stack(DS,6);
	Dblpush(DS,0,1);
	Dblpush(DS,0,7);
	Dblpush(DS,1,8);
	Dblpush(DS,0,6);
	Dblpush(DS,1,3);		
	while(!IsEmpty(DS,0))
	{
		Dblpop(DS,0,x);
		printf("栈0弹出%d ",x);
	}
}

欢迎指正评价:)))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值