ITPub博客

首页 > 应用开发 > C/C++ > 鸡啄米:C++编程入门系列之五(运算符和表达式)

鸡啄米:C++编程入门系列之五(运算符和表达式)

C/C++ 作者:qwer1030274531 时间:2020-11-09 10:44:46 0 删除 编辑

栈和队列的定义和特点

  • 栈和队列是两种常用、重要的线性结构,从数据结构角度上来看也是线性表。
  • 栈和队列是限定插入和删除只能在表的“端点”进行操作的线性表。

栈:是限定仅为表尾进行插入或者删除的线性表。因此,对栈来说,表尾端有其特殊含义,成为 栈顶(top),相应的,表头端称为 栈底(bottom)。不含元素的空表称为空栈。栈是一种 后进先出的线性表;

与栈相反, 队列(queue)是一种 先进先出(first in first out,缩写为FIFO)的线性表,它只允许在表的一端进行插入,而在另一端删除元素。在队列中,允许插入的一端叫做 队尾(rear),允许删除的一端则称为 队头(front)。

栈的表示和实现 http://dxb.myzx.cn

栈的插入操作,叫做进栈,也称压栈、入栈。
栈的删除操作,叫作出栈,也有的叫弹栈。
在这里插入图片描述

栈的顺序存储结构

我们定义一个top变量来指示栈顶元素在数组中的位置,类似于小时候学过的油游标卡尺,它的来回移动就意味着栈顶top可以变大变小。当栈存在一个元素是,top等于0,因此通常把空栈的判断条件定为top等于-1.

栈的结构定义

typedef int SElemType;typedef struct{
	SElemTye data[MAXSIZE];
	int top;      /* 用于栈顶指针 */}SqStack;123456
栈的顺序存储结构——进栈操作 http://zzdxb.baikezh.com/

对于栈的插入,即进栈操作(如下图所示):

在这里插入图片描述
代码如下:

/* 插入元素e为新的栈顶元素 */Status Push(SqStack *s, SElemType e){
	if(s->top == MAXSIZE -1)    // 栈满
	{
		return ERROR;
	}
	s->top++;               /* 栈顶指针加一 */
	s->data[s->top] = e;    /* 将新插入元素赋值给栈顶空间 */}12345678910
栈的顺序存储结构——出栈操作

出栈操作pop

在这里插入图片描述
代码如下:

/* 若栈不空,则删除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;      }123456789
  • 两者没有涉及到任何循环语句,因此时间复杂度均为O(1)。 http://dxb.myzx.cn/henan/
栈的链式存储结构

对于链栈来说,是不需要头结点的。基本不存在满栈的情况,除非已经没有可以使用的空间,如果真的发生那么此时计算机操作系统已经面临死机崩溃的情况,而不是链栈溢出的问题。
对于空栈来说,链表原定义是头指针指向空,那么链栈的空其实就是top=NULL。

链栈的结构代码如下:

typedef struct StackNode{
	SElemType data;
	struct StackNode *next;}StackNoda,*LinkStackPtr;typedef struct LinkStack{
	LinkStackPtr top;
	int count;}LinkStack;1234567891011
栈的链式存储结构——进栈操作

对于链栈的进栈push操作,假设元素值为e的新结点是s,top为栈顶指针。
代码如下:

Status Push(LinkStack *S, SElemType e){
	LinkStackPtr s =(LinkStackPtr)malloc(sizeof(StackNode));
	s->data=e;
	s->next=S->top;    /* 把当前的栈顶元素赋值给新结点的直接后继 */
	S->top = s;       /*  将新的结点s赋值黑栈顶指针 */
	S->count++;
	return OK;}123456789
栈的链式存储结构——出栈操作

对于链栈的pop操作,也是很简单的三句操作。假设用变量 p 用来存储要删除的栈顶结点,将栈顶指针下移一位,最后释放p即可。
代码表示如下: http://zzdxb.baikezh.com/jiyuan/

/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */Status Pop(LinkStack *S, SElemType *e){
	LinkStackPtr p;
	if(StackEmpty(*S))
		return ERROR;
	*e = S->top->data;
	p=S->top;                /* 将栈顶结点赋值给p */
	S->top = S->top->next;   /* 使得栈顶指针下移一位,指向后一结点 */
	free(p);
	S->count--;
	return OK;}12345678910111213

如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些 http://dxb.myzx.cn/question/


队列的表示和实现

队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

与栈不同的是,队列元素的出列是在队头,即下标为0的位置,也就是意味着,队列中的所有元素都得想前移动,以确保队列的队头也就是小标为0的位置不为空,此时时间复杂度为O(n),如下图所示:

(手绘多包涵)
在这里插入图片描述
为了避免当只有一个元素时,队头和队尾重合使处理编的麻烦,所有引入两个指针, front 指针指向队头元素, rear 指针指向队尾元素的下一个位置,这样当front等于rear时,此队列不是还剩一个元素,而是空队列。

假如这个队列的总个数不超过5个,但是目前如果接着入队的话,因数组末尾元素已经占用,再向后加,就会产生数组越界的错误,可实际上,我们的队列在下标为0和1的地方还是空闲的。我们把这种现象称为“假溢出”。
因此引入循环队列概念。 http://ask.baikezh.com/

另外,当rear > front 时,此时队列的长度为 rear - front 。但当 rear < front 时,队列长度分为两段,一段是QueueSize - front,另一段是0 + rear ,加在一起,队列长度为 rear - front + QueueSize。因此通用的计算队列长度公式为:
(rear - front + QueueSize)% QueueSize

队列的链式存储结构及实现

队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而以,我们通常简称它为链队列。
为了方便操作,我们将头指针指向链队列的头结点,而队尾指针指向终端节点。

在这里插入图片描述
空队列,front 和 rear 都指向头结点。

链队列的结构为: http://ask.baikezh.com/anhui/

typedef int QElemType; /* QElemType 类型根据实际情况而定 */typedef struct QNode  /* 姐弟啊结构 */{
	QElemType data;
	struct QNode *next;}QNode,*Queueptr;typedef struct              /* 队列的链表结构 */{
	QueuePtr front,rear;   /* 队头、队尾指针 */}LinkQueue;123456789101112
队列的链式存储结构——入队操作

入队操作时,其实就是在链表尾部插入结点。
其代码如下: http://zzdxb.baikezh.com/zhumadian/

/* 插入元素e为Q的新的队尾元素 */Status EnQueue(LinkQueue *Q, QElemType e){
	QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
	if(!s)     exit(OVERFLOW);   /* 储存内存分配失败 */
	s->data = e;
	s->next = NULL;
	Q->rear->next=s;           /* 把拥有元素 e 新结点 s 赋值给原队列结点的后继 */
	Q->rear=s;                 /* 把当前的 s 设置为队尾结点, rear 指向 s */
	return OK;}1234567891011
队列的链式存储结构——出队操作

出队操作时,就是头结点的后继结点出队,将头结点的后继改为它后面的结点,若链表除头结点外只剩下一个元素时,则需要将 rear 指向头结点。

代码如下: http://ask.baikezh.com/heilongjiang/

/* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK */Status DeQueue(LinkQueue *Q, QElemType *e){
	QueuePtr p;
	if(Q->front == Q->rear)
		return ERROR;
	P = Q->front->next; /* 将欲删除的队头结点暂存给p */
	*e = p->data;       /* 将欲删除的队头结点的值赋值给e */
	Q->front->next = p->next;  /* 将原队头结点后继p->next 赋值给头结点后继 */
	if(Q->rear = p)          /* 若队头是队尾,则删除后将rear指向头结点 */
		Q->rear = Q->front;
	free(p);
	return OK;}1234567891011121314

总的来说,在确定队列长度最大值的情况下,建议用循环队列,如果你无法预估队列的长度时,则用链队列。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/30239065/viewspace-2732922/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-05-08

  • 博文量
    190
  • 访问量
    164195