链式队列的实现

1.链队列的简介

1.1链队列的结构

  队列是一种我们比较常见的数据结构,它只允许在一端进行插入数据操作,在另一端进行删除数据的特殊线性表。队列可以用数组和链表的结构实现,使用单链表的结构更优一些,因为如果使用数组的结构,出队列在数组头出数据,效率会比较低。

1.2链队列的特征

  链队列的底层实现逻辑便是对单链表的访问,只不过需要头尾两个指针来提高储存和访问数据的效率,其次队列最有特征的数据储存访问方式便是先进先出FIFO(First In First Out),这可以与栈的先进后出做对比,入队列则在队尾进行插入操作,出队列则在对头进行删除操作。

2.链队列的实现

  上面说到链队列是用单列表实现的,所以用个结构体来定义节点,这里是否带头可根据自己喜好选择,这里为不带头单列表,数据类型最好重命名为了以后可以适应不同的数据类型,提高代码可读性。另需一个结构体来储放队列的头尾指针,再用一个整形来记录队列药效数据个数,这个结构体的作用是为了封装两个指针,之后向函数传结构体的地址即可,避免了二级指针的使用,如果不用这个结构体封装,则必须向函数传两个指针的地址,则需要二级指针,增加了理解难度,和运行效率。

2.1Queue.h

头文件

#pragma once

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

 定义节点和头尾指针 

typedef int QDataType;

typedef struct QueueNode
{
    struct QueueNode* next;
    int val;
}QNode;


typedef struct Queue
{
    // 队列的头指针
    QNode* phead;
    // 队列的尾指针
    QNode* ptail;
    // 队列的大小
    int size;
}Queue;

 函数接口 

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

// 向队列中添加元素
void QueuePush(Queue* pq, QDataType x);
// 从队列中删除元素
void QueuePop(Queue* pq);
// 获取队列头部元素
QDataType QueueFront(Queue* pq);
// 获取队列尾部元素
QDataType QueueBack(Queue* pq);
// 获取队列大小
int QueueSize(Queue* pq);
// 判断队列是否为空
bool QueueEmpty(Queue* pq);

 2.2Queue.c

初始化队列

 

// 初始化队列
void QueueInit(Queue* pq)
{
	// 断言pq不为空
	assert(pq);
	// 初始化头指针和尾指针为NULL
	pq->phead = pq->ptail = NULL;
	// 初始化队列大小为0
	pq->size = 0;
}

销毁队列

 

// 销毁队列
void QueueDestroy(Queue* pq)
{
	// 断言pq不为空
	assert(pq);
	// 获取头节点
	QNode* cur = pq->phead;
	// 遍历链表
	while (cur)
	{
		// 获取下一个节点
		QNode* next = cur->next;
		// 释放当前节点内存
		free(cur);
		// 移动cur到下一个节点
		cur = next;
	}
	// 头节点和尾节点置为空
	pq->phead = pq->ptail = NULL;
	// 队列大小置为0
	pq->size = 0;
}

入队列

// 向队列中添加元素
void QueuePush(Queue* pq, QDataType x)
{
	// 参数检查
	assert(pq);
	// 分配内存
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	// 初始化节点
	newnode->val = x;
	newnode->next = NULL;
	// 如果队列不为空,将新节点添加到队列尾部
	if (pq->ptail)
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	// 如果队列为空,将新节点作为头节点和尾节点
	else
	{
		pq->phead = pq->ptail = newnode;
	}
	// 队列大小加1
	pq->size++;
}

 出队列

// 弹出队列元素
void QueuePop(Queue* pq)
{
	// 断言pq不为空
	assert(pq);
	// 断言头节点不为空
	assert(pq->phead != NULL);
	// 如果头节点的下一个节点为空,说明只有一个节点,直接释放头节点,并将头节点和尾节点置为空
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	// 如果头节点的下一个节点不为空,说明队列中至少有两个节点,将头节点指向下一个节点,释放当前头节点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	// 队列大小减一
	pq->size--;
}

 获取队列首元素

 

// 获取队列首元素
QDataType QueueFront(Queue* pq)
{
	// 断言pq不为空
	assert(pq);
	// 断言pq的队头指针不为空
	assert(pq->phead != NULL);
	// 返回队列首元素的值
	return pq->phead->val;
}

获取队列尾部数据

// 函数:返回队列尾部的数据
QDataType QueueBack(Queue* pq)
{
	// 断言:pq不为空
	assert(pq);
	// 断言:pq的尾部指针不为空
	assert(pq->ptail != NULL);
	// 返回:pq的尾部数据
	return pq->ptail->val;
}

获取队列大小

// 获取队列的大小
int QueueSize(Queue* pq)
{
	// 断言pq不为空
	assert(pq);
	// 返回队列的大小
	return pq->size;
}

 检查队列是否为空

 

// 检查队列是否为空
bool QueueEmpty(Queue* pq)
{
	// 断言pq不为空
	assert(pq);
	// 如果队列大小为0,则队列空
	return pq->size == 0;
}

2.3Test.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);
    // 打印队列头部元素
    printf("%d ", QueueFront(&q));
    // 弹出队列头部元素
    QueuePop(&q);
    // 当队列不为空时,循环执行
    while (!QueueEmpty(&q))
    {
        // 打印队列头部元素
        printf("%d ", QueueFront(&q));
        // 弹出队列头部元素
        QueuePop(&q);
    }
    // 销毁队列
    QueueDestroy(&q);
    // 返回0
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值