数据结构之队列

队列的定义:

只允许在一端进行插入(入队),在另一端删除(出队)的线性表。

举例:

在队末进行入队,在对头进行出队。
在这里插入图片描述

队列的特点:

先进入队列的元素先出队(先进先出)

空队列:

在这里插入图片描述对头和队尾元素:
在这里插入图片描述

队列的基本操作:

定义队列:

#define Maxsize 10
typedef struct
{
	ElemType data[Maxsize];//用静态数组存放队列元素
	int front, rear;//队头和队尾指针
}SqQueue;

在这里插入图片描述

InitQueue(&Q):初始化队列,构造一个空队列Q。

void InitQueue(SqQueue&Q)
{
	Q.rear = Q.front = 0;//初始化时,队头,队尾指针指向0
}

DestoryQueue(&Q):销毁队列,销毁并释放队列Q所占用的内存空间。

EnQuquq(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾。

bool EnQueue(SqQueue& Q, ElemType x)
{
	if (队列已满)//判断队列是否存满
		return false;
	Q.data[Q.rear] = x;//将x插入队尾
	Q.rear = (Q.rear + 1)%Maxsize;//队尾指针加1取模
	return true;
}

在这里插入图片描述那么队列已满的条件是不是rear==Maxsize?

那当然不是!

举例:

在这里插入图片描述
如果是上述这种情况,虽然此时rear==Maxsize,但是队列中的前几个元素已经出队列了,此时的队列并没有存满。

正确的判断条件是:

方案一:

(Q.rear + 1)%Maxsize//(9+1)%10=0使rear指向0

在这里插入图片描述相当于将线性存储在逻辑结构上变成了环状:

在这里插入图片描述队列已满的条件:

队尾指针的再下一个位置是对头,即(Q.rear+1)%Maxsize==Q.front

注意:这里无法将所有的空间都使用,因为如果当rear和front指向同一个位置,那么这不就恰好成为了空队列的条件吗?因此我们必须牺牲一个存储单元。

方案二:

在这里插入图片描述

方案三:

在这里插入图片描述

当rear指向的是队尾元素时的基本操作:

在这里插入图片描述

在这里插入图片描述

队列元素个数:

(rear+Maxsize-front)%Maxsize

DeQueue(&Q,&x):出队,若队列Q非空,删除对头元素,并用x返回。

bool DeQueue(SqQueue& Q, ElemType& x)
{
	if (Q.rear == Q.front)//空栈则报错
	{
		return false;
	}
	x = Q > data[Q.front];
	Q.front = (Q > front + 1) % Maxsize;//从队头元素开始,使元素依次出队
	return true;
}

GetHead(Q,&x):读对头元素,若队列Q非空,则将对头元素赋值给x。

bool GetHead(SqQueue Q, ElemType& x)
{
	if (Q.rear == Q.front)//队空则报错
		return false;
	x = Q.data[Q.front];
	return true;
}

QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回false.

bool QueueEmpty(SqQueue Q)
{
	if (Q.rear == Q.front)
		return true;
	else
		return false;
}

队列的链式实现:

typedef struct LinkNode//链式队列结点
{
	ElemType data;
	struct LinkNode* next;
}LinkNode;
typedef struct//链式队列
{
	LinkNode* front, * rear;//队列的对头和队尾指针
}LinkNode;

链队列:链式存储实现的队列
在这里插入图片描述

链式队列的初始化:

//带头结点
void InitQueue(LinkQueue& Q)
{
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));//使指针都指向头结点
	Q.front->next = NULL;
}
//不带头结点
void InitQueue(LinkQueue& Q)
{
	Q.front = NULL;
	Q.rear = NULL;
}

链式队列的判空操作:

//带头结点
bool IsEmpty(LinkQueue Q)
{
	if (Q.front == Q > rear)
		return true;
	else
		return false;
}
//不带头结点
bool IsEmpty(LinkQueue Q)
{
	if (Q.front == NULL)
		return true;
	else
		return false;
}

链式队列的入队:

//带头结点
void EnQueue(LinkQueue& Q, ElemType x)
{
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	Q.rear->next=s;//新节点插入rear之后
	Q.rear = s;//修改表尾指针
}
//不带头结点
void EnQueue(LinkQueue& Q, ElemType x)
{
	LInkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	if (Q.front == NULL)//在空队列中插入第一个元素
	{
		//修改队头队尾指针
		Q.front = s;
		Q.rear = s;
	}
	else
	{
		Q.rear->next = s;//新节点插入到rear节点之后
		Q.rear = s;//修改rear指针
	}
}

链式队列的出队操作:

//带头结点
bool DeQueue(LinkQueue& Q, ELemType& x)
{
	if (Q.front == Q.rear)//空队
		return false;
	LinkNode* p = Q.front->next;
	x = p->data;//用变量x返回队头元素
	Q.front->next = p->next;//修改头结点的next指针
	if (Q > rear == p)//最后一个节点出队
	{
		Q.rear = Q.front;//修改rear指针
	}
	free(p);
	return true;
}
//不带头结点
bool DeQueue(LinkQueue& Q, ElemType& x)
{
	if (Q.front == NULL)//空队列
		return false;
	LinkNode* p = Q.front;//p指向此次出队列的节点
	x = p->data;//用变量x返回队头元素
	Q.front = p->next;//修改front指针
	if (Q.rear == p)//最后一个节点出队
	{
		Q.front = NULL;
		Q.rear = NULL;
	}
	free(p);
	return true;
}

上面我们提到如果是顺序存储,那么当预分配的空间耗尽时,就代表此时队列已满,而对于链式存储来说,一般情况下,队列是不会满的,除非内存不足。

双端队列:

前面我们学习过,栈是只允许从一端插入和删除的线性表,队列是只允许从一端插入,另一端删除的线性表,而双端队列是只允许从两端插入,两端删除的线性表。
在这里插入图片描述

判断输出序列的合法性:

若数据元素输入序列为1,2,3,4,则那些输出序列是合法的?那些是非法的?

在这里插入图片描述对数据元素1,2,3,4的输出共有24种:

在栈中:在这里插入图片描述
红色字体代表非法输出,绿色字体代表合法输出!

举例分析:

当为1,2,3,4的顺序时:

数据1,输入再输出,数据2输入再输出,直到数据四完成输出。

当为2,4,1,3的顺序时:

数据2作为第一个元素输出,那么,输入1,2,输出2,接下来要输出数据4,那么,输入3,4,输出4,此时栈中剩余1,3,1是在3之前输入的,要想输出1,就必须先输出3,因此无法完成这组的输出。

其他结果也可按此进行分析。

注:在栈中合法的输出序列,在双端序列中一定合法。

对于计算合法输出序列,我们可以使用卡特兰数:

在这里插入图片描述在输入受限的双端队列中:
在这里插入图片描述红色字体代表非法输出,绿色字体代表合法输出!

在输出受限的双端队列中:
在这里插入图片描述
红色字体代表非法输出,绿色字体代表合法输出!

输出/输入受限的双端队列中判断序列的输出是合法还是非法,和在栈中判断方法基本相同,只需要注意栈只能在一端,而输出/输入受限的双端队列虽然可以在两端,但会有不同的限制!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从未止步..

谢谢你的打赏,我会继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值