数据结构 —— 队列

本文主要讨论使用c语言实现队列。

什么是队列?

上篇文章我们学习了栈以及实现;我们知道栈的元素进出规则是先进后出,而队列与栈正好相反,队列的规则是先进先出

就比如现在有一个排队的队伍,从末尾进去的元素会逐渐向另一端靠近,当达到头部的时候离开队伍。这就是先进先出。

所以,队列是一种先进先出的数据结构:

  • 入队:在队尾插入元素
  • 出队:在队头删除元素

实现

头文件

同样先来看头文件。我们使用链表来实现队列。如果是使用数组,则不方便实现先进先出的功能,需要使用循环数组来完成。我们先来看代码:

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int QType;

typedef struct Node
{
	QType data;
	struct Node* next;
}Node;

typedef struct Queue
{
	Node* front;
	Node* rear;
}Queue;

void QueueInit(Queue* q);
void QueueDestory(Queue* q);

void QueuePush(Queue* q, QType x);
void QueuePop(Queue* q);
int QueueSize(Queue* q);
int QueueEmpty(Queue* q);
QType QueueFront(Queue* q);
QType QueueBack(Queue* q);

这里我们创建了front和rear两个指针。链式队列必须同时维护 frontrear,否则就无法在 O(1) 时间内完成入队和出队。front表示头指针,rear表示尾指针。如果没有尾指针,那么插入操作每次都需要遍历链表找到尾部,这样的效率并不高。所以我们使用头尾指针来实现效率更高。

源文件

1、初始化

初始化很简单,只需要将头尾指针置空即可。

cpp 复制代码
void QueueInit(Queue* q)
{
	assert(q);
	q->front = q->rear = NULL;
}

2、销毁

销毁时需要将每个节点都销毁,因此这时必须进行遍历,并且,最后不要忘记将头尾指针置空防止造成野指针。

cpp 复制代码
void QueueDestroy(Queue* q)
{
	assert(q);

	Node* cur = q->front;
	while (cur)
	{
		Node* next = cur->next;
		free(cur);
		cur = next;
	}
	//释放完后front和rear仍然指向原来的旧值,会造成野指针
	//所以需要置空
	q->front = q->rear = NULL;
}

3、创建新节点

该部分我们已经写过很多遍了。这里就不再说明,直接上代码:

cpp 复制代码
Node* BuyNewNode(QType x)
{
	Node* newnode = (Node*)malloc(sizeof(Node));
	if (newnode == NULL)
	{
		perror("malloc failed!");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

4、插入新元素

队列中的元素总是从尾部插入。要注意的是,如果是空队列,还需要对front进行处理。代码如下:

cpp 复制代码
void QueuePush(Queue* q, QType x)
{
	assert(q);
	Node* newnode = BuyNewNode(x);
	if (newnode == NULL)
	{
		printf("创建节点失败!\n");
		return;
	}
	//如果是空队列
	if (q->rear == NULL)
	{
		q->front = q->rear = newnode;
		return;
	}
	q->rear->next = newnode;
	q->rear = newnode;
}

5、删除元素

删除元素时除了考虑空队列外海需要考虑当队列只有一个元素时,不能只处理front,rear也要一并处理。代码如下:

cpp 复制代码
void QueuePop(Queue* q)
{
	assert(q);
	if (q->front == NULL)
	{
		printf("队列为空!\n");
		return;
	}
	if (q->front == q->rear)
	{
		free(q->front);
		q->front = q->rear = NULL;
		return;
	}
	Node* next = q->front->next;
	free(q->front);
	q->front = next;
}

6、计算队列元素个数

如果我们想要得知队列的元素个数,在当前的情况下就需要遍历一遍队列来让标记递增。实现代码如下:

cpp 复制代码
int QueueSize(Queue* q)
{
	assert(q);
	if (q->front == NULL)
	{
		//空队列元素为0
		return 0;
	}
	Node* cur = q->front;
	int tmp = 0;
	while (cur)
	{
		tmp++;
		cur = cur->next;
	}
	return tmp;
}

不过,如果我们的队列对元素个数的获取使用十分频繁的话,为了保证O(1),我们直接在结构的时候多创建一个size变量用于记录即可:

cpp 复制代码
typedef struct Node
{
	QType data;
	struct Node* next;
    int size;//记录队列元素个数
}Node;

7、返回队首/队尾元素

返回元素十分简单,注意判空就行。直接上代码:

cpp 复制代码
QType QueueFront(Queue* q)
{
	assert(q);
	assert(q->front);
	return q->front->data;
}

QType QueueRear(Queue* q)
{
	assert(q);
	assert(q->rear);
	return q->rear->data;
}

8、判空函数

我们从上面的功能中发现需要判断队列为空的部分有很多,因此我们也可以单独写一个判空的函数,调用时使用assert断言即可判断。代码如下:

cpp 复制代码
int QueueEmpty(Queue* q)
{
	assert(q);

	return q->front == NULL;
}

测试

最后我们对代码进行测试。测试函数如下:

cpp 复制代码
void Test()
{
    Queue q;
    QueueInit(&q); // 初始化队列

    printf("队列是否为空?%s\n", QueueEmpty(&q) ? "是" : "否");

    // 入队
    printf("入队元素:1 2 3 4 5\n");
    for (int i = 1; i <= 5; i++)
    {
        QueuePush(&q, i);
        printf("队头: %d, 队尾: %d, 队列大小: %d\n",
            QueueFront(&q), QueueRear(&q), QueueSize(&q));
    }

    printf("\n出队\n");
    while (!QueueEmpty(&q))
    {
        printf("出队元素: %d\n", QueueFront(&q));
        QueuePop(&q);
        if (!QueueEmpty(&q))
            printf("队头: %d, 队尾: %d, 队列大小: %d\n",
                QueueFront(&q), QueueRear(&q), QueueSize(&q));
        else
            printf("队列为空\n");
    }

    // 再次判空
    printf("\n出队完成后,队列是否为空?%s\n", QueueEmpty(&q) ? "是" : "否");

    QueueDestroy(&q); // 销毁队列
}

运行结果如下:

相关推荐
曾几何时`2 小时前
滑动窗口(十五)2962. 统计最大元素出现至少 K 次的子数组(越长越合法型)
数据结构·算法
蜂蜜黄油呀土豆2 小时前
Redis 数据结构详解:从底层实现到应用场景
数据结构·redis·跳表·zset
于樱花森上飞舞2 小时前
【多线程】CAS和哈希表
java·数据结构·java-ee
编程之路,妙趣横生2 小时前
数据结构(十二) 位图 & 布隆过滤器
数据结构
无言(* ̄(エ) ̄)2 小时前
C语言--运算符/函数/结构体/指针
c语言·开发语言·数据结构·数据库·算法·mongodb
im_AMBER2 小时前
Leetcode 91 子序列首尾元素的最大乘积
数据结构·笔记·学习·算法·leetcode
CoderCodingNo2 小时前
【CSP】CSP-XL 2025辽宁复赛真题-第三题, 小L打比赛(match)
数据结构·算法
TheSumSt11 小时前
Python丨课程笔记Part3:语法进阶部分(控制结构与基础数据结构)
数据结构·笔记·python
长安er14 小时前
LeetCode 20/155/394/739/84/42/单调栈核心原理与经典题型全解析
数据结构·算法·leetcode·动态规划·