栈与队列的学习笔记

程序来源:《大话数据结构》
栈(stack)定义:
限定仅在表尾进行插入和删除的线性表,允许插入和删除的一端为栈顶,另一端为栈底。
栈注意点:先进后出,后进先出。
栈元素具有线性关系(前驱后继关系),插入叫做入栈,删除叫做出栈。

栈的结构:顺序结构和链式结构。
顺序结构
栈的结构定义
typedef int SElemType;/*SElemType类型根据实际情况而定,这里假设为int */
typedef struct
{
	SElemType data[MAXSIZE];
	int top;   /*用作栈顶指针*/
}SqStack;
栈顶位置top小于存储栈的长度,判断空栈的条件:top = -1;栈满时top为MAXSIZE-1。
根据判断条件,进行入栈操作
/* 插入元素e为新的栈顶元素*/
Status Push (SqStack *s, SElemType e)
{
	if (s->top == MAXSIZE - 1) /*栈满*/
	{
		return ERROR;
	}
	s->top++;   /*栈顶指针加一*/
	s->data[s->top] = e;  /*将新插入元素赋值给栈顶空间*/
	return OK;
}
出栈操作
/*若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR*/
Status Pop (SqStack *S, SElemType *e)
{
	if (S->top == -1)
	{
		return ERROR;
	}
	*e = S->data[S->top];   /*将要删除的栈顶元素赋值给e*/
	S->top--;               /*栈顶指针减一*/
	return OK;
}
两栈共享空间
两个相同类型的栈,分别把数组(空间,这里用数组来举例)的两端作为栈底,从两端向中间延伸。定义两者的栈顶分别为top1,top2,当top1+1 = top2时栈满。
注意,两栈的入栈出栈时需要注意是谁入栈出栈,top1向后延伸是++,top2是--。
两栈共享空间这样的数据结构通常出现在两个栈的空间需求有相反关系时,即一个栈增长时另一个栈在缩短。

链式结构
栈顶放在链表头部(不需要头结点),空栈时就是栈顶指向为空(top == NULL)
链栈结构
typedef struct StackNode
{
	SElemType data;
	struct StackNode *next;
}StackNode, *LinkStackPtr;

typedef struct LinkStack
{
	LinkStackPtr top;
	int count;
}LinkStack;
链栈入栈:在栈顶结点前新建立一个结点作为新的结点作为新的栈顶,之前栈顶作为新栈顶的直接后继。
链栈出栈:将栈顶结点释放,原来栈顶结点的直接后继作为新栈顶

顺序栈和链栈的操作时间复杂度均为O(1),顺序栈可能存在空间浪费问题,长度受到限制,但存取定位方便;链栈开销较大,但长度无限制。
在使用栈时,若过程中元素变化不可知,最好用链栈,若元素变化可控,建议使用顺序栈。

栈的作用
栈的引用简化了程序设计的问题,划分了不同关注层次,缩小思考范围,使我们更多关注问题核心。

栈的应用:在程序语言中使用递归(直接调用自身(递归函数)或通过一系列调用语句间接调用自身),四则运算表达式(后缀表示法和中缀表示法转化为后缀表示法)。
递归
递归定义时必须至少有一个条件使其在不满足条件时返回返回值退出,结束递归。
栈在递归中的具体应用
在前行阶段,对每一层递归,函数的局部变量、参数值以及返回地址都被压入栈中,在退回阶段,位于栈顶的存储被弹出,恢复调用的状态。
四则运算表达式
后缀表达式是程序执行四则运算时的依据,后缀的原因是所有的符号在运算数字后面出现
后缀表达式的规则:从左到右遍历表达式,遇到数字进栈,遇到符号就将处在栈顶的两个数字出栈进行运算,结果进栈,直到表达式结束。
平时所用的标准四则运算表达式是中缀表达式,将中缀表达式转换为后缀表达式的规则:
从左到右遍历表达式,若是数字就输出,成为后缀表达式的一部分,若是符号就判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号的,则栈顶符号依次出栈输出,并将当前符号入栈,循环操作直到表达式结束。
2018/01/29 18:59

队列
队列的定义
队列(queue)是只允许在一端进行插入操作,另一端进行删除操作的线性表。
先进先出,允许插入的一端为队尾,允许删除的一端为队头.
队列应用:操作系统,客服系统,键盘输入系统。

队列结构分为顺序存储结构和链式存储结构。
顺序存储结构有两个指针front和rear,front指针指向队头元素,rear指针指向队尾元素的下一个位置,当front == rear 时,是空队列。
单纯的顺序存储结构有很大的不足,队列插入时间复杂度O(1),输出时间复杂度O(n);容易造成假溢出。
为解决这些不足,我们使用队列时通常使用循环队列(队列的头尾相接)。
为了表明front == rear 时是队列满还是队列为空,有两种方法
1. 设置一个标准变量flag,当 front == rear 且 flag = 0 时队列为空, front == rear 且 flag = 1 时队列为满。
2. 当队列空时,front == rear, 当队列满时, front和rear的下一个相等,即保留一个元素空间。
通用计算队列长度公式: (rear - front + QueueSize) % QueueSize
循环队列的结构
typedef int QElemType;     /*QElemType类型根据实际情况而定,这里假设为int */
/*循环队列的顺序存储结构*/
typedef struct
{
	QElemType data[MAXSIZE];
	int front;             /*头指针*/
	int rear;              /*尾指针,若队列不空,指向队列尾元素的下一个位置*/
}SqQueue;
循环队列的初始化代码
/*循环链表的初始化代码*/
Status InitQueue (SqQueue *Q)
{
	Q->front = 0;
	Q->rear = 0;
	return OK;
}
循环队列求队列长度

/*返回Q的元素个数,也就是队列的当前长度*/
int QueueLength(SqQueue Q)
{
	return (Q.rear - Q.front + MAXSIZE) %MAXSIZE;
}
循环队列的入队操作
/*若队列未满,则插入元素e为Q新的队尾元素*/
Status EnQueue (SqQueue *Q, QElemType *e)
{
	if ((Q->rear + 1) % MAXSIZE == Q->front)  /*队列满的判断*/
	{
		return ERROR;
	}
	Q->data[Q->rear] = e;    /*将元素e赋值给队尾*/
	Q->rear = (Q->rear + 1) % MAXSIZE;  /*rear指针向后移一位*/
										/*若到最后则转到数组头部*/
	
	return OK;
}
循环队列的出队操作
/*若队列不空,则删除Q中队头元素,用e返回其值*/
Status DeQueue (SqQueue *Q, QElemType *e)
{
	if (Q->front == Q->rear)    /*队列空的判断*/
	{
		return ERROR;
	}
	*e = Q->data[Q->front];     /*将队头元素赋值给e*/
	Q->front = (Q->front + 1) % MAXSIZE;  /*front指针向后移一位置*/
								/*若到最后则转到数组头部*/
	return OK;
}
循环队列插入和删除时间复杂度都为O(1)

队列的链式存储结构
就是线性表的单链表,只能尾进头出,队头指针指向链表队列的头结点,队尾指针指向终端结点,空队列时,队头指针和队尾指针都指向头结点。
链队列的入队操作就是在链表尾部插入结点,出队操作就是将头结点的后继结点出队,将头结点的后继改为原后继的后继,若链表除头结点外只有一个元素时,需要将rear指向头结点。

循环队列与链队列比较
同:
它们基本操作的时间复杂度都为O(1)
异:
循环链表是事先申请好空间,使用期间不释放,且循环队列必须有一个固定的长度。
链队列每次申请和释放结点消耗的时间较长,但它没有固定的长度,在空间上更加灵活。

在确定队列最大长度时使用循环队列,无法估计队列长度时使用链队列。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值