别样的数据结构---队列

本文介绍了队列的概念,作为线性表的一种,遵循FIFO(先进先出)原则。队列可以用数组或链表实现,链表实现更优。文中提供了使用链表实现队列的C语言代码,包括队列初始化、销毁、插入、删除、获取队头和队尾元素以及判断队列是否为空等操作。
摘要由CSDN通过智能技术生成

队列的认识

和栈一样,队列同样是一种线性表,特点是只允许在一端进行插入操作(队尾),在另一端进行删除操作(队头)

符合 FIFO(first in first out),先进的先出(队列的核心特点)

入队列:进行插入数据操作的一端为队尾

出队列:进行删除数据操作的一端为队头

队列可以用数组来实现,也可以用链表来实现,显然用链表来实现更优,因为数组插入数据或者删除数据可能需要大量挪动数据

队列的实现

Queue.h

#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>

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


typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;


//队列初始化
void QueueInit(Queue* pq);

//队列销毁
void QueueDestory(Queue* pq);

//队列插入
void QueuePush(Queue* pq, QDataType x);

//出队头数据
void QueuePop(Queue* pq);

//取队头数据
QDataType QueueFront(Queue* pq);

//取队列的尾
QDataType QueueBack(Queue* pq);

//判断队列是否为空
bool QueueEmpty(Queue* pq);

这里面用了两个结构体,第一个结构体是每个节点的结构,而第二个结构体有指向队列的头尾指针以及当前队列元素个数

Queue.c

1.队列初始化

 开始时队列为空,因此我们把头尾指针都复制成NULL,size赋值成0

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

2.队列销毁

①本质就是要释放掉队列所占据的内存空间,而队列是由单链表来实现的,里面是一个个节点,因此我们就用之前讲过的释放单链表的方法来释放队列---即遍历链表,释放一个个节点

②由于队列销毁了,因此也要把phead和ptail都置空,size归0

void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

3.队尾入数据

①本质就是单链表的尾插,之前经常把新建节点封装成函数,是因为单链表有头插尾插以及任意位置插入删除都要用到创建节点,而队列的特点是只能从队尾入数据,所以就这一个功能需要用到创建节点,就没必要封装成函数了~

②尾插的时候要分类讨论(已经很常见了~,避免空指针解引用~)

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->phead == NULL)
	{
		assert(pq->ptail == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

 解释一下上述代码中的这一步,其实这句断言可以没有,因为理论上来说当phead为NULL时,ptail也一定为空,加上这句话就是保险起见,万一代码或者操作哪里出了问题,导致phead为空,ptail不为空,这里就会报错提示

4.队头出数据

①队列为空,当然不能出数据了,因此记得断言~

②思考一下队列只剩一个节点和还有多个节点时出数据代码一样吗~

我们先考虑队列有多个节点时出数据~,这就是单链表中的头删操作

     QNode* next = pq->phead->next;
     free(pq->phead);
     pq->phead = next;

这三句代码就可以实现头删

再考虑队列只剩下一个节点时出数据~

 可以看到,当队列只剩下一个节点时候,如果还是上述代码,会出现一个问题:ptail成为了野指针,因为最后一个节点已经被释放了,但是ptail仍然保存了最后一个节点的地址,经典的野指针

所以只有一个节点时队头出数据,直接释放掉最后一个节点,然后让ptail = phead = NULL即可

考虑到这一点,队头出数据应该分类讨论

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	//一个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	//多个节点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

5.取队头数据

要assert断言队列是否为空

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

6.取队尾数据

 要assert断言队列是否为空

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return  pq->ptail->data;
}

7.求队列长度

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

8.判断队列是否为空

队列为空的特点是size为0 或者 phead 和 ptail都为空

采取一种办法判断即可~

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
	//return pq->phead == NULL && pq->ptail == NULL;
}

test.c

#include"Queue.h"

void TestQueue()
{
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
	QueueDestory(&q);
}


int main()
{
	TestQueue();
}

队列的基本内容就介绍到这了,欢迎大家交流指正~

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值