【数据结构】队列
目录
- 一、队列
- 1、概念与结构
- 2、队列的实现
- 3、队列的初始化
- 4、打印队列数据
- 5、入队
- 6、销毁队列
- 7、队列判空
- 8、出队
- 9、取队头、队尾数据
- 10、队列中有效元素个数
- 二、源码
个人主页,点击这里~
数据结构专栏,点击这里~

一、队列
1、概念与结构
概念:只允许在⼀端进行插入数据操作,在另⼀端进⾏删除数据操作的特殊线性表,队列具有先进先出FIFO
(First In First Out)的性质。
入队列
:进行插入操作的⼀端称为队尾。
出队列
:进行删除操作的⼀端称为队头。
队列底层结构选择:队列可以用数组和链表的结构实现,使用链表的结构实现更优⼀些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。所以我们将用链表实现队列这一数据结构。
2、队列的实现
我们要用链表实现队列,首先就要考虑先进先出
这种模式下,链表该如何实现?
我们可以定义一个生成队列结点的结构体,生成一个结点看守队头,再生成一个结点看守队尾,这样我们访问队头和队尾的时候都会比较简单且节省时间,然后将这两个结点包装在一个队列结构体里面。
typedef int QDataType;
//队列结点的结构
typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QN;
//队列的结构
typedef struct Queue
{QN* head;QN* tail;
}Queue;
3、队列的初始化
队列的初始化的时候,我们只需要将队列中的两个结点head
和tail
置为空即可。
void QueueInit(Queue* pq)
{assert(pq);pq->head = NULL;pq->tail = NULL;
}
4、打印队列数据
//队列打印
void QueuePrint(Queue* pq)
{assert(pq);QN* pcur = pq->head;while (pcur){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
5、入队
队列入队的时候是从队尾入的。在入队之前,我们要定义一个新节点newNode
储存新数据,再执行入队操作。
//入队---队尾
void QueuePush(Queue* pq,QDataType x)
{//定义新节点QN* newNode = (QN*)malloc(sizeof(QN));if (newNode == NULL){perror("malloc fail!");return 1;}newNode->data = x;newNode->next = NULL;//入队if (pq->tail == NULL){pq->head = pq->tail = newNode;}else{pq->tail->next = newNode;pq->tail = newNode;}
}
测试代码:
#include "Queue.h"void test()
{Queue plist;QueueInit(&plist);QueuePush(&plist, 1);QueuePush(&plist, 2);QueuePush(&plist, 3);QueuePrint(&plist);
}int main()
{test();return 0;
}
测试结果:
6、销毁队列
我们在单链表博客中实现销毁链表的时候,是一个节点一个节点进行销毁的,所以我们在队列的销毁过程也一样,另外在销毁队列的时候,我们要保存要销毁节点的下一个节点,以免找不到下一个节点。
//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QN* pcur = pq->head;//指向队列的队头while (pcur){QN* next = pcur->next;free(pcur);pcur = next;}pq->head = pq->tail = NULL;
}
注意
:这里不用把pq也释放掉,因为pq是队列的结构体,它不是动态内存开辟出来的,只有节点才是动态内存开辟出来的,它们才需要释放。
销毁测试结果:
从右面可以看出所有的节点都被置为了空,队列已经完全被销毁了。
7、队列判空
我们接下来就要实现出队操作,在出队操作之前我们还需要判断队列是不是空,如果为空就不能进行出队列操作。
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->head == NULL;
}
这里要用到bool
类型,所以要包含头文件#include <stdbool.h>
8、出队
出队时我们要把第一个节点销毁掉。
//出队---队头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));//队头和队尾相等,即只有一个节点if (pq->head == pq->tail){free(pq->head);pq->head = pq->tail = NULL;}else{QN* next = pq->head->next;free(pq->head);pq->head = next;}
}
9、取队头、队尾数据
取队头数据:
//取队头数据
QDataType QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->head->data;
}
取队尾数据:
//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->tail->data;
}
10、队列中有效元素个数
//队列中有效元素个数
int QueueSize(Queue* pq)
{assert(pq);//方法一:遍历队列(适用于不频繁使用数据个数的情况)QN* pcur = pq->head;int size = 0;while (pcur){size++;pcur = pcur->next;}return size;//方法二:在队列的结构体中添加变量size// 并在初始化时初始化为0,每当添加节点时就让它计数//(适用于频繁使用数据个数的情况)//return pq -> size;
}
测试代码:
#include "Queue.h"void test()
{Queue plist;QueueInit(&plist);QueuePush(&plist, 1);QueuePush(&plist, 2);QueuePush(&plist, 3);QueuePrint(&plist);printf("%d",QueueSize(&plist));QueueDestroy(&plist);
}int main()
{test();return 0;
}
测试结果:
二、源码
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;
}QN;
//队列的结构
typedef struct Queue
{QN* head;QN* tail;
}Queue;
//队列的初始化
void QueueInit(Queue* pq);
//队列打印
void QueuePrint(Queue* pq);
//入队---队尾
void QueuePush(Queue* pq, QDataType x);
//销毁队列
void QueueDestroy(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
//出队---队头
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//队列中有效元素个数
int QueueSize(Queue* pq);
Queue.c
#include "Queue.h"//队列的初始化
void QueueInit(Queue* pq)
{assert(pq);pq->head = NULL;pq->tail = NULL;
}
//队列打印
void QueuePrint(Queue* pq)
{assert(pq);QN* pcur = pq->head;while (pcur){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
//入队---队尾
void QueuePush(Queue* pq,QDataType x)
{//定义新节点QN* newNode = (QN*)malloc(sizeof(QN));if (newNode == NULL){perror("malloc fail!");return 1;}newNode->data = x;newNode->next = NULL;//入队if (pq->tail == NULL){pq->head = pq->tail = newNode;}else{pq->tail->next = newNode;pq->tail = newNode;}
}
//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QN* pcur = pq->head;//指向队列的队头while (pcur){QN* next = pcur->next;free(pcur);pcur = next;}pq->head = pq->tail = NULL;
}
//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->head == NULL;
}
//出队---队头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));//队头和队尾相等,即只有一个节点if (pq->head == pq->tail){free(pq->head);pq->head = pq->tail = NULL;}else{QN* next = pq->head->next;free(pq->head);pq->head = next;}
}
//取队头数据
QDataType QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->head->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->tail->data;
}
//队列中有效元素个数
int QueueSize(Queue* pq)
{assert(pq);//方法一:遍历队列(适用于不频繁使用数据个数的情况)QN* pcur = pq->head;int size = 0;while (pcur){size++;pcur = pcur->next;}return size;//方法二:在队列的结构体中添加变量size// 并在初始化时初始化为0,每当添加节点时就让它计数//(适用于频繁使用数据个数的情况)//return pq -> size;
}
test.c
#include "Queue.h"void test()
{Queue plist;QueueInit(&plist);QueuePush(&plist, 1);QueuePush(&plist, 2);QueuePush(&plist, 3);QueuePrint(&plist);printf("%d",QueueSize(&plist));QueueDestroy(&plist);
}int main()
{test();return 0;
}
总结:
以上就是本期博客分享的全部内容啦!如果觉得文章还不错的话可以三连支持一下,你的支持就是我前进最大的动力!
技术的探索永无止境! 道阻且长,行则将至!后续我会给大家带来更多优质博客内容,欢迎关注我的CSDN账号,我们一同成长!
(~ ̄▽ ̄)~