数据结构 —— 队列

本文主要讨论使用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); // 销毁队列
}

运行结果如下:

相关推荐
IT陈图图6 分钟前
Flutter × OpenHarmony 文件管家:数据结构设计与实现
数据结构·flutter
budingxiaomoli18 分钟前
优选算法-哈希表
数据结构·算法·散列表
平哥努力学习ing35 分钟前
线性表与链表(part 1)
数据结构·链表
小龙报1 小时前
【C语言进阶数据结构与算法】LeetCode27 && LeetCode88顺序表练习:1.移除元素 2.合并两个有序数组
c语言·开发语言·数据结构·c++·算法·链表·visual studio
Script kid1 小时前
Redis(Remote Dictionary Server远程字典服务器)五种常见数据结构及常见用法和指令
服务器·数据结构·redis
范纹杉想快点毕业1 小时前
C语言查找算法对比分析
数据结构·算法
好奇龙猫2 小时前
【大学院-筆記試験練習:线性代数和数据结构(14)】
数据结构
Remember_9932 小时前
【数据结构】深入理解Map和Set:从搜索树到哈希表的完整解析
java·开发语言·数据结构·算法·leetcode·哈希算法·散列表
TTGGGFF2 小时前
控制系统建模仿真(二):掌握控制系统设计的 MAD 流程与 MATLAB 基础运算
开发语言·数据结构·matlab
金融小师妹2 小时前
基于AI多模态分析的日本黄金储备60%跃升研究:外汇结构重构与资产价格联动机制解构
大数据·数据结构·人工智能·深度学习