1、栈(stack)是限定仅在表尾进行插入和删除操作的线性表。(表尾指栈顶)

---- 把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。

栈又称为先进后出/后进先出(Last In First Out)的线性表,简称LIFO结构。

理解栈的定义需要注意:

---- 首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。

它的特殊之处就在于限制了这个线性表的插入和删除位置,它始终只在栈顶进行。

这也就使得:栈底是固定的,最先进栈的只能在栈底。

---- 栈的插入操作,叫做进栈,也称压栈、入栈(Push)。栈的删除操作,叫做出栈,有的叫做弹栈(Pop)

2、栈的顺序存储结构

---- 栈是线性表的特例,那么栈的顺序存储其实也是线性表顺序存储的简化,简称为顺序栈。顺序表是用数组来实现的。

定义一个top变量来指示栈顶元素在数组中的位置,可以变化,但不能超过栈的长度StackSize。

当栈存在一个元素时,top=0,因此通常把空栈的判定条件定为 top=-1.

栈的结构定义:

#include <iostream>
using namespace std;
#define OK 1
#define ERROR 0
typedef int Status;
#define MaxSize 50
typedef int SElemtype;
typedef struct
{
	SElemtype stack[MaxSize];
	int top;
} SqStack;

---- 进栈操作push和出栈操作pop
//进栈操作push,插入元素e为新的栈顶元素
Status Push(SqStack *S,SElemtype e)
{
	if(S->top==MaxSize-1) //栈满
		return ERROR;
	S->top++;
	S->stack[S->top]=e;
	return OK;
}
//出栈操作pop,若栈不为空,则删除S的栈顶元素,用e返回其值
Status pop(SqStack *S,SElemtype *e)
{
	if(S->top==-1)  //空栈
		return ERROR;
	*e=S->stack[S->top];	//将要删除的栈顶元素赋值给e
	S->top--;		//栈顶指针减1
	return OK;
}

3、两栈共享空间

---- 用一个数组来存储两个栈,数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为

数组的末端,即下标为数组长度n-1处。这样,两个栈如果增加元素,就是两端点向中间延伸。

思路:两个栈在数组的两端,向中间靠拢。top1和top2是栈1和栈2的栈顶指针,可以想象,只要它们俩不见面,两个栈就可以一直使用。

栈1为空时,top1=-1,当栈2为空时,top2=n,当 top1 + 1 = top2 时,即两个栈顶元素相邻时,栈满。

两栈共享空间的结构的代码如下:

#define MaxSize 100 
typedef struct
{
	SElemtype stack[MaxSize];
	int top1;	//栈1栈顶指针
	int top2;	//栈2栈顶指针
} SqDoubleStack;
对于两栈共享空间的push和pop方法,除了我们要插入和删除元素值参数外,还需要有一个判断是栈1还是栈2的栈号参数stacknumber。

插入和删除元素的代码如下:

Status Push(SqDoubleStack *S,SElemtype e,int stacknumber)
{
	if(S->top1+1 == S->top2) //栈已满,不能再push新元素了
		return ERROR;
	if(stacknumber == 1)	//栈1有元素进栈
		S->stack[++S->top1]=e;	//先top1+1后给数组元素赋值
	else if(stacknumber == 2)
		S->stack[--S->top2]=e;	//先top2-1后给数组元素赋值
	return OK;
}
Status Pop(SqDoubleStack *S,SElemtype *e,int stacknumber)
{
	if(stacknumber==1)
	{
		if(S->top1=-1)		//栈1是空栈
			return ERROR;
		*e = S->stack[S->top1--];	//将栈1的栈顶元素出栈
	}
	else if(stacknumber==2)
	{
		if(S->top2==MaxSize)	//栈2是空栈
			return ERROR;
		*e = S->stack[S->top2++];
	}
	return OK;
}

事实上,使用这样的数据结构,通常都是当两个栈的空间需求有相反关系时,即一个栈在增长时另一个栈在缩短的情况。

并且这两个栈具有相同的数据类型。

4、栈的链式存储结构--链栈

---- 栈顶放在单链表的头部(插入和删除操作)。对于链栈来说,是不需要头结点的。对于链栈来说,基本不存在栈满的情况,除非内存已经

没有可以使用的空间。对于空栈来说,链表原定义是头指针指向空,那么链栈的空其实就是top=NULL。

链栈的结构代码如下:

typedef int SElemtype;
//结点
typedef struct StackNode
{
	SElemtype sdata;
	struct StackNode *next;
}StackNode,*LinkStackPtr;
//栈
typedef struct LinkStack
{
	LinkStackPtr top;	//栈顶指针
	int count;		//栈内元素个数
}LinkStack;

进栈操作和出栈操作

Status Push(LinkStack *S,SElemtype e)
{
	LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));//创建新结点
	s->sdata = e;
	s->next = S->top;//把当前的栈顶元素赋值给新结点的直接后继
	S->top = s;
	S->count++;
	return OK;
}
Status Pop(LinkStack *S,SElemtype *e)
{
	LinkStackPtr p;
	if(S->top==NULL)	//链栈为空
		return ERROR;
	*e = S->top->sdata;
	p = S->top;
	S->top = S->top->next;
	free(p);
	S->count--;
	return OK;
}
链栈的进栈Push和出栈Pop操作都很简单,没有任何循环操作,时间复杂度均为O(1)。

对比一下顺序栈与链栈,它们在时间复杂度上是一样的,均为O(1)。对于空间复杂度,顺序栈需要事先确定一个固定的长度,可能会

存在内存空间浪费的问题,但它的优势是存取时定位很方便,而链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但

对于栈的长度无限制。

---- 如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈。

反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值