数据结构栈和队列(队列的详解)

队列

队列只允许在一端进行插入数据,一端进行删除数据的特殊线性表
队尾:插入数据的一端(入队列)
队头:删除数据的一端(出队列)
队列只能先进先出

入栈(一种):1 2 3 4 出栈(多种):可以边进边出
入队列(一种):1 2 3 4 出队列(一种):1 2 3 4
因为它只能队尾进,队头出,边进边出也是这一种

队列的实际应用

1.队列用于保持公平性
比如:抽号码,先抽的人先排队(使用),后抽的人后排队(使用)
在这里插入图片描述
假设我不知道新的号,要取队尾数据加一
取队头数据,再取队中或队尾数据,相减,得到前面有多少个人,比如取3和取8,(8 - 3 = 5人)

2.生产者消费者模型:
生产者:取号的人,比如扫一下码就多插入一个号
消费者:机器,有人使用了,把号删除
比如:医院排号
生产号:排号(插入号)
医院:消费号(删除号)

3.如果医院两个窗口同时叫到一个人
那这个人怎么办?

这时候就有的概念了(锁在后面的操作系统锁的竞争和并发也会学习),访问队列先获取锁,获取锁的人先使用,锁占用会阻塞,等到前面解锁了再进去
有多线程的概念,会同时取号
(比如3号,8号窗口同时取3号患者)

后面会详细学习,不用担心

4.广度优先遍历
二叉数,图和队列都会涉及到广度优先遍历
以点的形式一圈一圈向外遍历,例如水纹

举个例子,qq好友推荐
这些连线叫做边
要给小徐推荐好友就要找到它好友的好友
以小徐为点,要找到小徐好友的好友
1.小徐进队列,之后小徐出队列,小明和小王就进队列
2.小明和小王出队列,小张和小花就进队列,就找到了小徐的好友的好友
小徐的好友出队列,那么小徐的好友的好友进队列

怎么判断好友的好友出来了?
计数,这个我也不太懂,等后面详细学习广度优先遍历再来补充
在这里插入图片描述

队列的实现

队列选择数组还是链表来实现呢?
数组:数组出队列不好实现,每次出队列都要挪动数据
所以我们选择链表实现队列
在这里插入图片描述

链表链表头删和尾插都很好,有一个头指针和一个尾指针
在这里插入图片描述

队列的结构

//队列的节点的结构

typedef int QDataType;
typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

//队头删除
//void QueuePop(QNode** pphead, QNode** pptail, QDataType x);
//队尾插入
//void QueuePush(QNode** pphead, QNode** pptail);


typedef struct Queue
{
	QNode* phead;//指向节点的指针
	QNode* ptail;
	int size;//标队列的大小
}Queue;

队列有两个指针,头指针和尾指针,为了头删和尾插
把两个指针封装成一个结构体,就不用使用多个参数(头尾指针)和二级指针了
结构体指针就可以改变结构体中的值
也可以改变结构体中指针的值(指向)

为什么是使用二级指针呢?
假设开始队列为空,phead和ptail都指向NULL
要改变一级指针的指向(值)要使用二级指针,解引用改变一级指针

初始化和销毁

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	//没有pq这个结构体就不用初始化了
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
    //没有pq这个结构体就不用销毁了
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	//把节点一个一个地释放掉
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

队头删除和队尾插入

//队头删除
void QueuePop(Queue* pq);
//队尾插入
void QueuePush(Queue* pq,QDataType x);

//队头删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);
	//判断删空了的情况

	if (pq->phead->next == NULL)
	{
		//一个节点
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		//多个节点
		QNode* cur = pq->phead->next;
		free(pq->phead);
		pq->phead = cur;
	}
	//忘记处理一个节点和多个的情况了,剩一个节点不处理的话会出现,pq->ptail会成为野指针

	pq->size--;
}

//队尾插入
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("QueuePush:malloc");
		return;
	}
	//空间开辟成功

	newnode->next = NULL;
	newnode->val = x;
	if (pq->ptail == NULL)
	{
		//没有节点
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		//有空间(节点)
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}


队头删除必须区分是不是一个节点和多个节点的情况
在这里插入图片描述

取队头数据和队尾数据

//取队头数据
QDataType QueueTop(Queue* pq);
//取队尾数据
QDataType QueueTail(Queue* pq);

//取队头数据
QDataType QueueTop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);
	//assert(pq->phead);或者头指针是空,没有数据
	//队列大小不为空,才能取队头的数据

	return pq->phead->val;
}
//取队尾数据
QDataType QueueTail(Queue* pq)
{
	assert(pq);
	assert(pq->size  != 0);
	//assert(pq->ptail);或者尾指针是空,没有数据
	//队列大小不为空,才能取队尾的数据

	return pq->ptail->val;
}

取队列的大小

//队列的大小
int QueueSize(Queue* pq);

//队列的大小
int QueueSize(Queue* pq)
{
	assert(pq);
	
	return pq->size;
	//size就是队列的大小
	//如果不设计size,每次都要遍历链表算出size的大小(增减数据size会变化)
}

队列的判空

//队列的判空
bool QueueEmpty(Queue* pq);

//队列的判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
	//size为0返回true,真为空
	//size为非0返回false,假为有数据,非空
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值