队列详解包含测试过的完整代码

零.前言

栈和队列都是顺序表或者链表的特殊结构,栈由于是尾插尾删,所以我们使用顺序表来实现;而队列使用尾插头删,如果我们使用顺序表的话则正中其缺点,即插入删除数据的时候需要进行数据的挪动,所以我们使用链表来进行队列的实现。

1.队列的结构

1.逻辑结构

在这里插入图片描述
即先进先出,头删尾插。

物理结构

物理结构与链表是相同的,这里就不画了。

2.实现队列的基本操作

1.队列的建立

typedef struct QueueNode {
	struct QueueNode* next;
	DataType x;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

这里使用了两个结构体来进行队列的实现,第一个结构体表示队列的一个节点,第二个结构体表示一个队列的头指针和尾指针。第二个结构体用头指针和尾指针抽象表示一个队列。

2.队列的初始化

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;//将pq队列的head和tail都置为空
}

建立一个队列pq,将它的头指针和尾指针都指向空。

3.队列的销毁

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;//遍历队列,由于释放cur之后无法再找到cur->next了,所以定义一个next先存起来
	}
	pq->head = pq->tail = NULL;
}

和链表一样,在进行队列的销毁时,也需要遍历整个序列,然后依次销毁空间,最后指针别忘记指空。

4.判断队列是否为空

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;//当head为空则为空返回TRUE
}

我们用bool类型来接收结果,如果为空返回True,不为空返回False。

5.计算队列的大小

int QueueSize(Queue* pq)
{
	assert(pq);
	int n = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		n++;
		cur = cur->next;//利用cur进行遍历
	}
	return n;
}

遍历队列,只到找到空指针,每一次对n加一。

3.队列的插入与删除

1.队列的插入

void QueuePush(Queue* pq, DataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->x = x;
	newnode->next = NULL;//初始化一个节点
	if (pq->head == NULL)
	{
		pq->head =pq->tail= newnode;//当只有一个节点的时候将head和tail赋值为newnode
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;//将newnode作为新的尾指针
	}
}

队列采用的是尾插,即在tail后插入,如果队列为空,则插入的元素就为头元素,若不为空则在尾部插入,然后将节点作为新的尾。

2.队列的删除

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	Queue* next = pq->head->next;
	free(pq->head);
	pq->head = next;//首先定义一个next接收head->next
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}//当只有一个元素时删除后要将pq的尾指针赋值为空
}

对队列进行删除,需要先断言判断队列是否为空,不为空则从头部开始删除,然后将第二个元素作为头部。

4.得到队列的头尾

1.得到队列的头

int QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->x;
}

2.得到队列的尾

int QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->x;
}

5.全部文件

1.queue.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#define DataType int
typedef struct QueueNode {
	struct QueueNode* next;
	DataType x;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, DataType x);
void QueuePop(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
int QueueFront(Queue* pq);
int QueueBack(Queue* pq);
void QueuePrint(Queue* pq);

2.queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"queue.h"
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;//将pq队列的head和tail都置为空
}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;//遍历队列,由于释放cur之后无法再找到cur->next了,所以定义一个next先存起来
	}
	pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, DataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->x = x;
	newnode->next = NULL;//初始化一个节点
	if (pq->head == NULL)
	{
		pq->head =pq->tail= newnode;//当只有一个节点的时候将head和tail赋值为newnode
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;//将newnode作为新的尾指针
	}
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	Queue* next = pq->head->next;
	free(pq->head);
	pq->head = next;//首先定义一个next接收head->next
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}//当只有一个元素时删除后要将pq的尾指针赋值为空
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;//当head为空则为空返回TRUE
}
int QueueSize(Queue* pq)
{
	assert(pq);
	int n = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		n++;
		cur = cur->next;//利用cur进行遍历
	}
	return n;
}
int QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->x;
}
int QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->x;
}
void QueuePrint(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		printf("%d ", cur->x);
		cur = cur->next;
	}
	printf("\n");
}

3.test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"queue.h"
void menu()
{
	printf("****1.入队*******2.出队****\n");
	printf("****3.队首*******4.队尾****\n");
	printf("****5.是否为空***6.大小****\n");
	printf("****0.删除队列*************\n");
}
int main()
{
	Queue pq;
	QueueInit(&pq);
	QueuePush(&pq, 1);
	QueuePush(&pq, 2);
	QueuePush(&pq, 3);
	QueuePush(&pq, 4);
	QueuePush(&pq, 5);
	QueuePrint(&pq);
	//printf("write by lonely little boy\n");
	int input = 0;
	do{
		menu();
		int x;
		int y;
		scanf("%d", &input);
		switch (input)
		{
		case 1:scanf("%d", &x);
			QueuePush(&pq, x);
			QueuePrint(&pq);
			break;
		case 2:QueuePop(&pq);
			QueuePrint(&pq);
			break;
		case 3:y = QueueFront(&pq);
			printf("%d\n", y);
			break;
		case 4:y = QueueBack(&pq);
			printf("%d\n", y);
			break;
		case 5:y = QueueEmpty(&pq);
			if (y == 1)
			{
				printf("empty!\n");
			}
			else
			{
				printf("not empty\n");
			}
			break;
		case 6:y = QueueSize(&pq);
			printf("%d\n", y);
			break;
		case 0:QueueDestroy(&pq);
			break;
		default:printf("wrong type!\n");
			break;
		}
	} while (input);
	return 0;
}

6.测试

插入:成功
删除至空报错:成功
判断为空:成功
计算大小为空为0:成功
取头取尾空时报错:成功
退出:成功
综上代码测试成功

7.总结

队列是建立起来和别的不一样的存在,需要定义两个结构体,定义了头尾指针方便了我们进行对队列的操作,最近也做了一些栈和队列的题目,发现其实很多用C++编写会相对简单一些,C语言不是那么太适合刷题,但是要了解一些最最基本的结构实现的话,C语言是最好的选择,我认为原理还是最重要的内容,如果不了解原理是没有办法进行创新的。

posted @   卖寂寞的小男孩  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示