数据结构:链式队和循环队的原理,实现及实际应用

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法

小新的主页:编程版小新-CSDN博客

1.队列的定义

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列严格遵循先进先出FIFO(First In First Out)的原则

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

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

2.队列的分类 

队列的实现其实就和前面学习的栈一样,分为单链表实现实现的链式队,和数组实现的循环队。他们各有好处,也有需要细节需要注意。

1.链式队

我们用两个指针控制这个链表的头和尾,就能够实现队列的相关功能,为了方便计算队列的大小,我们也可以添加一个size来记录队列的大小。

2.循环队 

我们在用数组实现栈的时候,使用的是一般的数组,我们用两个变量记录数组的起始位置和结束位置,但是一般的数组有能解决问题吗?如果不能存在什么问题,以及我们该怎么解决呢?接下来让我们一起去探索吧。

由此可以观察到,当在不断入队和出队后,前面的空间就会被浪费,不能在被使用。

为了解决这个问题我们可以使用循环数组。

 循环数组虽然解决了上面的空间浪费的问题,但由此又产生了一个新的问题,我们该如何计算队列的大小呢?当head和tail指向同一个位置时队列为空还是为满呢

第一种:牺牲一个单元来区分队空和队满,每当入队时tail++,当tail指向的下一个位置是head时即tail+1==head时,此时队列已满。每当出队时head++,当head==tail时,表示队列为空。

第二种:增添一个记录队列大小的变量size 。这样,队空的条件为 size==0;队满的条件为size==MaxSize。

3.队列的功能 

1.队列的初始化

2.判断队列是否为空

3.判断队列是否为满

4.入队

5.出队

6.返回队头和队尾的数据

7.返回队列的大小

8.打印队列

9.销毁队列

4.队列的声明 

1.链式队

//队列的声明
typedef int QDataType;
typedef struct QueueNode
{
	QDataType x;
	struct QueueNode* next;
}QueueNode;

typedef struct QueueNode* PQueueNode;

typedef struct Queue
{
	PQueueNode head;
	PQueueNode tail;
	int size;
}Queue;

2.循环队

//队列的声明
typedef int QDataType;
#define MAXSIZE 50

typedef struct Queue
{
	QDataType* data;
	int head;
	int tail;
}Queue;

5.功能实现

5.1队列的初始化

1.链式队

//队列的初始化
void QueueInit(Queue* pst)
{
	pst->head = NULL;
	pst->tail = NULL;
	pst->size = 0;
}

2.循环队

//队列的初始化
void QueueInit(Queue* pst)
{
	assert(pst);
	pst->data = (QDataType*)malloc(sizeof(QDataType)*MAXSIZE);
	if (pst->data == NULL)
	{
		perror("malloc fail");
		return;
	}
	pst->head = 0;
	pst->tail = 0;
}

5.2判断队列是否为空

1.链式队

//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
	assert(pst);
	return pst->size == 0;
}

2.循环队

进行取模操作是为了防止越界访问,实现数组的回绕,即实现循环的功能。

//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
	assert(pst);
	return (pst->head % MAXSIZE) == (pst->tail % MAXSIZE);
}

5.3判断队列是否为满

1.链式队

链式队是按需申请空间,不存在为满的情况。

2.循环队

按照我们上面分析的为满的情况实现即可,这里及其下面的取模操作都是为了防止越界访问。

//判断队列是否为满
bool QueueFull(Queue* pst)
{
	assert(pst);
	return ((pst->tail + 1) % MAXSIZE) == (pst->head % MAXSIZE);
}

5.4入队

1.链式队

//入队
void QueuePush(Queue* pst, QDataType x)
{
	assert(pst);
	PQueueNode newnode = (PQueueNode)malloc(sizeof(QueueNode));
	newnode->next = NULL;
	newnode->x = x;
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	if (QueueEmpty(pst))
	{
		pst->head = pst->tail = newnode;
	}
	else
	{
		pst->tail->next = newnode;
		pst->tail = newnode;
	}
	pst->size++;
}

2.循环队

插入前要判满。

//入队
void QueuePush(Queue* pst, QDataType x)
{
	assert(pst);
	assert(!QueueFull(pst));
	pst->data[pst->tail] = x;
	pst->tail = (pst->tail + 1) % MAXSIZE;

}

5.5出队

1.链式队

在出队的时候分为两种情况,只有一个节点时和多个节点时,要进行分开处理。

//出队
void QueuePop(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	//1.只有一个节点
	if (pst->head == pst->tail)
	{
		free(pst->head);
		pst->head = pst->tail = NULL;
	}
	else
	{
		//2.有多个节点
		PQueueNode del = pst->head;
		pst->head = pst->head->next;
		free(del);
		del = NULL;
	}
	pst->size--;
}

2.循环队

//出队
void QueuePop(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	pst->head = (pst->head + 1) % MAXSIZE;
	
}

5.6返回队头和队尾的数据

1、链式队

//返回对头的数据
QDataType QueueFront(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->head->x;
}

//返回队尾数据
QDataType QueueBack(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->tail->x;
}

2.循环队

在返回队尾数据时,因为我们是没入一次队pst->tail++,所以在返回队尾数据时,pst->tail要先减去1,但是这里有可能在不断入队出队后,pst->tail=0,减1的话就会越界,而(pst->tail-1+MAXSIZE) % MAXSIZE就避免了这个问题。

//返回对头的数据
QDataType QueueFront(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->data[pst->head%MAXSIZE];
}

//返回队尾数据
QDataType QueueBack(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->data[(pst->tail-1+MAXSIZE) % MAXSIZE];
}

5.7返回队列的大小

1.链式队

//返回队列的大小
int QueueSize(Queue* pst)
{
	assert(pst);
	return pst->size;
}

2.循环队

//返回队列的大小
int QueueSize(Queue* pst)
{
	return (pst->tail + MAXSIZE- pst->head) % MAXSIZE;
}

5.8打印队列

1.链式队

//打印队列的元素
void QueuePrint(Queue* pst) 
{
	assert(pst);
	assert(pst->size > 0);
	PQueueNode cur = pst->head;
	printf("队头->");
	while (cur != pst->tail->next)
	{
		printf("%d->", cur->x);
		cur = cur->next;
	}
	printf("队尾\n");
}

2.循环队

//打印队列的元素
void QueuePrint(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	int cur = pst->head;
	printf("对头->");
	while (cur!=pst->tail)
	{
		printf("%d->", pst->data[cur]);
		cur = (cur + 1) % MAXSIZE;
	}
	printf("队尾\n");
}

5.9销毁队列

1.链式队

因为链式队时一个个节点来实现的,在销毁队列时就要一个一个去释放。

//销毁队列
void QueueDestroy(Queue* pst)
{
	assert(pst);
	PQueueNode cur = pst->head;
	while (cur != NULL)
	{
		PQueueNode del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
	}
	pst->head = pst->tail = NULL;
	pst->size = 0;
}

2.循环队

只要将整体开辟的一个空间释放即可。

//销毁队列
void QueueDestroy(Queue* pst)
{
	assert(!QueueEmpty(pst));
	free(pst->data);
	pst->data = NULL;
	pst->head = pst->tail = 0;
}

6.链式队和循环队的对比 

7.队列的实际应用

1.吃饭排队取号

2.好友推荐也有队列的身影

8.完整代码

1.链式队

Queue.h

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

//链式队

//队列的声明
typedef int QDataType;
typedef struct QueueNode
{
	QDataType x;
	struct QueueNode* next;
}QueueNode;

typedef struct QueueNode* PQueueNode;

typedef struct Queue
{
	PQueueNode head;
	PQueueNode tail;
	int size;
}Queue;

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

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

//判断队列是否为满
//bool QueueFull(Queue* pst);

//入队
void QueuePush(Queue* pst, QDataType x);

//出队
void QueuePop(Queue* pst);

//返回对头的数据
QDataType QueueFront(Queue* pst);

//返回队尾数据
QDataType QueueBack(Queue* pst);

//返回队列的大小
int QueueSize(Queue* pst);

//打印队列的元素
void QueuePrint(Queue* pst);

//销毁队列
void QueueDestroy(Queue* pst);

Queue.c

#include"Queue.h"

//队列的初始化
void QueueInit(Queue* pst)
{
	pst->head = NULL;
	pst->tail = NULL;
	pst->size = 0;
}

//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
	assert(pst);
	return pst->size == 0;
}

//判断队列是否为满
//链式队不存在判满的情况

//入队
void QueuePush(Queue* pst, QDataType x)
{
	assert(pst);
	PQueueNode newnode = (PQueueNode)malloc(sizeof(QueueNode));
	newnode->next = NULL;
	newnode->x = x;
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	if (QueueEmpty(pst))
	{
		pst->head = pst->tail = newnode;
	}
	else
	{
		pst->tail->next = newnode;
		pst->tail = newnode;
	}
	pst->size++;
}

//出队
void QueuePop(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	//1.只有一个节点
	if (pst->head == pst->tail)
	{
		free(pst->head);
		pst->head = pst->tail = NULL;
	}
	else
	{
		//2.有多个节点
		PQueueNode del = pst->head;
		pst->head = pst->head->next;
		free(del);
		del = NULL;
	}
	pst->size--;
}

//返回对头的数据
QDataType QueueFront(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->head->x;
}

//返回队尾数据
QDataType QueueBack(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->tail->x;
}

//返回队列的大小
int QueueSize(Queue* pst)
{
	assert(pst);
	return pst->size;
}

//打印队列的元素
void QueuePrint(Queue* pst) 
{
	assert(pst);
	assert(pst->size > 0);
	PQueueNode cur = pst->head;
	printf("队头->");
	while (cur != pst->tail->next)
	{
		printf("%d->", cur->x);
		cur = cur->next;
	}
	printf("队尾\n");
}

//销毁队列
void QueueDestroy(Queue* pst)
{
	assert(pst);
	PQueueNode cur = pst->head;
	while (cur != NULL)
	{
		PQueueNode del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
	}
	pst->head = pst->tail = NULL;
	pst->size = 0;
}

2.循环队

Queue.h

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

//队列的声明
typedef int QDataType;
#define MAXSIZE 50

typedef struct Queue
{
	QDataType* data;
	int head;
	int tail;
}Queue;

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

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

//判断队列是否为满
bool QueueFull(Queue* pst);

//入队
void QueuePush(Queue* pst, QDataType x);

//出队
void QueuePop(Queue* pst);

//返回对头的数据
QDataType QueueFront(Queue* pst);

//返回队尾数据
QDataType QueueBack(Queue* pst);

//返回队列的大小
int QueueSize(Queue* pst);

//打印队列的元素
void QueuePrint(Queue* pst);

//销毁队列
void QueueDestroy(Queue* pst);

Queue.c

#include"Queue.h"

//循环队(基于数组实现)

//队列的初始化
void QueueInit(Queue* pst)
{
	assert(pst);
	pst->data = (QDataType*)malloc(sizeof(QDataType)*MAXSIZE);
	if (pst->data == NULL)
	{
		perror("malloc fail");
		return;
	}
	pst->head = 0;
	pst->tail = 0;
}

//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
	assert(pst);
	return (pst->head % MAXSIZE) == (pst->tail % MAXSIZE);
}

//判断队列是否为满
bool QueueFull(Queue* pst)
{
	assert(pst);
	return ((pst->tail + 1) % MAXSIZE) == (pst->head % MAXSIZE);
}

//入队
void QueuePush(Queue* pst, QDataType x)
{
	assert(pst);
	assert(!QueueFull(pst));
	pst->data[pst->tail] = x;
	pst->tail = (pst->tail + 1) % MAXSIZE;

}
//出队
void QueuePop(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	pst->head = (pst->head + 1) % MAXSIZE;
	
}

//返回对头的数据
QDataType QueueFront(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->data[pst->head%MAXSIZE];
}

//返回队尾数据
QDataType QueueBack(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	return pst->data[(pst->tail-1+MAXSIZE) % MAXSIZE];
}

//返回队列的大小
int QueueSize(Queue* pst)
{
	return (pst->tail + MAXSIZE- pst->head) % MAXSIZE;
}

//打印队列的元素
void QueuePrint(Queue* pst)
{
	assert(pst);
	assert(!QueueEmpty(pst));
	int cur = pst->head;
	printf("对头->");
	while (cur!=pst->tail)
	{
		printf("%d->", pst->data[cur]);
		cur = (cur + 1) % MAXSIZE;
	}
	printf("队尾\n");
}

//销毁队列
void QueueDestroy(Queue* pst)
{
	assert(!QueueEmpty(pst));
	free(pst->data);
	pst->data = NULL;
	pst->head = pst->tail = 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值