数据结构与算法(C语言版)P6---队列

1、队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除操作的特殊线性表,队列具有__先进先出__FIFO(First In First Out)

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

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

在这里插入图片描述
数据存放:现在有数据:A、B、C、D要存放,那存放数据顺序就是一次存入,并且存放的位置如下:
在这里插入图片描述

1.1、队列的实现概念

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果数组的结构,出队列在数组头上出数据,效率会比较低。

因为数组队列在处一个数据后们需要整体挪动数据向前,所以麻烦,这里不如链表。而链表在出数据是,把节点释放,然后将phead在指向要出数据的后一个数据即可,并且我们也记录个尾节点ptail,用来链接放在队列中新数据即可。

在这里插入图片描述
所以队列的实现主要以链表队列为主要。

2、链队列的实现

队列主要有以下接口函数

  • 初始化(QueueInit)
  • 销毁(QueueDestroy)
  • 扩容(BuyQueueNode)
  • 队尾插入数据(QueuePush)
  • 对头删除数据(QueuePop)
  • 取队尾数据(QueueBack)
  • 取对头数据(QueueFront)
  • 统计队列中数据个数(QueueSize)
  • 判断队列是否为空(QueueEmpty)

2.1、定义结构体

这里需要重点说明这个结构体的定义。
1、首先我们定义一个QueueNode结构体,这个结构体就是动态扩容的结点。里面有一个Next指针域,还有一个data数据域。如下:

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

2、然后是重点来了:这里我们需要创建两个QueueNode*类型的指针:head,tail。分别用来队列的头结点和尾节点。如下:

typedef struct Queue
{
	struct QueueNode* head;
	struct QueueNode* tail;
}Queue;

然后整体结构体定义就如下:

#pragma once

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

typedef int QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* Next;
}QNode;

typedef struct Queue
{
	struct QueueNode* head;
	struct QueueNode* tail;
}Queue;

2.2、队列初始化

将头结点和尾节点置为NULL即可。

//队列初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

2.3、销毁

依次遍历结点进行销毁

//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->Next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

2.4、扩容

//扩容
QNode* BuyQueueNode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->Next = NULL;
	return newnode;
}

2.5、队尾插入数据

队列在队尾入数据,因为我们提前设置了尾结点(tail),那操作就容易了,先将tail的Next域指向新节点的地址,然后再将新节点的地址赋值给tail,便于下次再尾插。

但是由于我们没创建哨兵位,所以在进行结点的插入和删除时,需要考虑边界问题。
那这里,特殊情况就是,如果是第一次插入数据,也就是当pq->head == NULL时,我们可以直接将新节点的地址赋值给头结点和尾节点。

//对尾插入数据
void QueuePush(Queue* pq, QDataType x)
{
	QNode* newnode = BuyQueueNode(x);

	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->Next = newnode;
		pq->tail = newnode;
	}
}

2.6、对头删除数据

核心思想:先保存头结点的后驱结点的地址,然后释放头结点,然后将先前保存头结点的后驱结点的地址赋值给头结点。
特殊情况:当pq->head == NULL时,也就意味着整个队列为空了,所以我们还需要将pq->tail = NULL。

//队头删除数据
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* next = pq->head->Next;
	free(pq->head);
	pq->head = next;
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

2.7、取队尾数据

//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

2.8、取对头数据

//取对头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

2.8、统计队列元素个数

使用个计数器,依次遍历队列中的结点,来统计队列元素个数。

//统计队列元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	int count = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		count++;
		cur = cur->Next;
	}
	return count;
}

2.10、判断队列是否为空

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

2.11、main函数测试程序

下面测试程序功能:遍历输出队列中的数据。

#include "queue.h"

int main()
{
	Queue q;
	QueueInit(&q);

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

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

	return 0;
}

输出:
在这里插入图片描述

3、全代码展示

这里使用三个文件:

  • queue.h:用于结构体、各种函数接口的声明
  • queue.c:用于各种函数接口的定义。
  • test.c:用于创建链表,实现链表。

3.1、queue.h

#pragma once

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

typedef int QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* Next;
}QNode;

typedef struct Queue
{
	struct QueueNode* head;
	struct QueueNode* tail;
}Queue;

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

//销毁
void QueueDestroy(Queue* pq);

//扩容
QNode* BuyQueueNode(QDataType x);

//对尾插入数据
void QueuePush(Queue* pq, QDataType x);

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

//取队尾数据
QDataType QueueBack(Queue* pq);

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

//统计队列元素个数
int QueueSize(Queue* pq);

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

3.2、queue.c

#include "queue.h"

//队列初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->Next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

//扩容
QNode* BuyQueueNode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->Next = NULL;
	return newnode;
}

//对尾插入数据
void QueuePush(Queue* pq, QDataType x)
{
	QNode* newnode = BuyQueueNode(x);

	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->Next = newnode;
		pq->tail = newnode;
	}
}

//队头删除数据
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* next = pq->head->Next;
	free(pq->head);
	pq->head = next;
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

//取对头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

//统计队列元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	int count = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		count++;
		cur = cur->Next;
	}
	return count;
}

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

3.3、test.c

#include "queue.h"

int main()
{
	Queue q;
	QueueInit(&q);

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

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

	QueueDestroy(&q);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

让我三行代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值