3.4队列

66 篇文章 37 订阅
65 篇文章 2 订阅

队列(queue)是一种先进先出 (first in first out缩写FIFO)的线性表。它只允许在表的一端进行插入,而在另一端删除。类似食堂排队打饭。允许插入的一端叫做队尾(rear),允许删除的一端叫队头(front)

如图3.8所示:



双端队列是限定插入和删除操作在表的两端进行的线性表。一端叫端点1,一端叫端点2

如下图所示:



用链表表示的队列简称链队列。

一个链队列显然需要两个分别指示队头和队尾指针,才能确定。

下图就很好说明了这个例子:


下面是单链队列的结构体

typedef struct QNode{
	QElemType data;
	struct QNode *next;
}QNode,*QueuePtr;

typedef struct LinkQueue{
	QueuePtr front;	//队头指针
	QueuePtr rear;	//队尾指针
}LinkQueue;

P·S:

                                  在xxx.c文件里面要用typedef来简化机构体,但在.cpp文件里面就不用了。可以直接用


下面是构建一个空队列Q

Status InitQueue(LinkQueue &Q)
{
	Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
	if (!Q.front)
		exit(OVERFLOW);	//存储分配失败
	Q.front->next = NULL;
	return Ok;
}


下面是销毁队列Q

Status DesrtoyQueue(LinkQueue &Q)
{
	while (Q.front)
	{
		Q.rear = Q.front->next;
		free(Q.front);
		Q.front = Q.rear;
	}
	return Ok;
}
这个函数的思路就是用Q.rear保存Q.front->next,然后不停的释放Q.front的空间


下面是插入元素e为Q的新的队尾元素

Status EnQueue(LinkQueue &Q, QElemType e)
{
	QNode *p = (QueuePtr)malloc(sizeof(QNode));
	if (!p)
		exit(OVERFLOW);
	p->data = e;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
	return Ok;
}

此函数思路:把新结点的next赋值为NULL,在把链队列的尾部的next从以前的next赋值为新结点的地址。最后把尾部后移成新节点的地址。


下面是删除Q的队头元素,用e返回其值,并返回OK

Status DeQueue(LinkQueue& Q, QElemType& e)
{
	QNode *p;
	if (Q.front == Q.rear)
		return ERROR;
	p = Q.front->next;
	e = p->data;
	Q.front->next = p->next;
	if (Q.rear == p)
		Q.rear = Q.front;
	free(p);
	return Ok;
}

这里Q.front==Q.rear就是图3.11(a)中的空队列

第二个if里面Q.rear==p是说明如果这里面没有结点了,就说明为空队列时,就Q.rear=Q.front。





循环队列-队列的顺序表示和实现

这个有个经典题目-圆桌问题

循环队列如图所示:



下面是循环队列头尾指针在不同情况下的不同:




下面是队列的顺序存储结构:

#define MAXQSIZE 100
typedef struct{
	QElemType *base;	//初始化的动态分配存储空间
	int front;	//头指针,若队列不空,指向队列头元素
	int rear;	//尾指针,若队列不空,指向下一个元素
}SqQueue;



下面是初始化一个空队列Q
Status InitQueue(SqQueue &Q)
{
	Q.base = (QElemType *)malloc(MAXQSIZE*sizeof(QElemType));
	if (!Q.base)
		exit(OVERFLOW);
	Q.front = Q.rear = 0;
	return Ok;
}



下面是求队列的长度:

int QueueLength(SqQueue Q)
{
	return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}
分析下:

有人会问为什么要(Q.rear-Q.front+MAXQSIZE)%MAXQIZE

举个例子,因为MAXQSIZE的大小为100,当进了110个数,又走了20个数的时候,这时候Q.rear指到10(如果下标从0开始),而Q.front指向20,那么Q.rear-Q.front=-10,这时加上MAXQSIZE后就是90再%MAXQSIZE取余后得90,所以这个(Q.rear-Q.front+MAXQSIZE)%MAXQIZE是计算环形长度的一个好方法。



下面是插入元素e为Q的新的队尾元素:

Status EnQueue(SqQueue& Q, QElemType e)
{
	if ((Q.rear + 1) % MAXQSIZE == Q.front)	//队列满了
		return ERROR;
	Q.base[Q.rear] = e;
	Q.rear = (Q.rear + 1) % MAXQSIZE;
	return Ok;
}

现在分析下算法:

插入:(Q.rear+1)%MAXQSIZE比如队列大小是5,那么他们的标号为01234号,任何一个数对5取余都是这5个数,比如01234队列都满了,你要插入的话插在4的后边(5),可是4后边没有地方了,这时候对5取余,就得到了0,你就可以插到0的位置了。

第一个if:同理当发现要插入的那个位置正好是Q.front的位置,那么就证明队列满了。



下面是删除队头元素:

Status DeQueue(SqQueue& Q, QElemType& e)
{
	//e返回其值
	if (Q.front == Q.rear)
		return ERROR;
	e = Q.base[Q.front];
	Q.front = (Q.front + 1) % MAXQSIZE;
	return Ok;
}

同理上面的(Q.front+1)%MAXQSIZE与上面的(Q.rear+1)%MAXQSIZE一模一样。在此不再解释

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IT1995

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值