在今天的学习中,将会介绍栈和队列的概念,以及其功能的实现,还有其应用场景
前言
队列是只允许再一端进行插入数据操作,另一端进行删除数据操作的特殊线性表,和栈不同的是,队列是先进先出的,进行插入的一端叫做队尾,出队列的一段叫做队头。
就像排队上公交车一样先排的肯定先进公交车,后来的就等着。
更简洁一点来说就是:
队列的实现
队列也是一种特殊的线性表,所以也可以用顺序表或者链表来实现,那么该选谁呢?这时候我们就要思考效率的问题,如果用顺序表的话,数组头就是队头,我要出队的话肯定是从数组头开始出,那么我后面的数据全要挪移,这复杂度就变成O(N)了,如果使用链表呢?用链表的话无论是入队还是出队,都只需要挪动前一个节点或者后一个节点的指向而已,所以我们选用链表来实现队列。
实现功能如下:
1.初始化队列
2.购买节点
3.入队
4.出队
5.检查队列是否为空
6.销毁队列
首先如果是链表的话,我们现要有一个节点的结构体:
typedef struct QListNode
{
struct QListNode* _next;
QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* _front;
QNode* _rear;
}Queue;
这里面有两个结构体,其中QNode是队列中每一个节点,Queue代表整个队列,其中存储队头和队尾。(有点头节点那意思了)
1.初始化队列
void QueueInit(Queue* q)
{
q->_front = NULL;
q->_rear = NULL;
}
2.购买节点
QNode * BuyQueueNode(QDataType x)
{
QNode * cur = (QNode*)malloc(sizeof(QNode));
cur->_data = x;
cur->_next = NULL;
return cur;
}
这是链表的节点,注意是队列的结构体存的结构体指针的类型,不是队列的类型
3.入队
void QueuePush(Queue* q, QDataType data)
{
QNode * cur = BuyQueueNode(data);
if(q->_front==NULL)
{
q->_front = q->_rear = cur;
}
else//有节点就在队列最后尾插,让rear后移一位
{
q->_rear->_next = cur;
q->_rear = cur;
}
}
入队要分成两种情况,一种是队列中没有节点,一种是已经有节点了,没有节点的时候,我们直接让队头和队尾的指针都指向新节点就可以了,那有节点的时候呢?有节点的时候就和链表的尾插是一样的,但是要注意,此时rear
存储的是队尾,我们尾插之后,要让rear
后移一位成新队尾。
红色是我们新进队的节点,这时候队尾的指向就不对了,要指向新节点。
4.出队
void QueuePop(Queue* q)
{
assert(q);//出队列就相当于链表的头删一样
if(q->_front == NULL)
return ;
QNode *cur = q->_front->_next;
free(q->_front);
q->_front = cur;
}
出队就是头删,别忘了要把储存队头的指针后移一位。
检查队列是否为空
int QueueEmpty(Queue* q)
{
if(q->_front == NULL)
return 1;
else return 0;
}
销毁链表
void QueueDestroy(Queue* q)
{
if(q->_front == NULL)
return ;
while(q->_front)
{
QueuePop(q);
}
}
销毁链表的时候就一直执行头删,删完为止。
应用:
有时候我们会用栈实现队列,队列实现栈,在实际应用中循环的队列是使用次数比较高的,在下一篇博客中将会介绍用队列实现栈,用栈实现队列,以及循环队列的写法。
源码
Queue.h
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* _next;
QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* _front;
QNode* _rear;
}Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
Queue.c
#include"Queue.h"
// 链式结构:表示队列
//typedef struct QListNode
//{
// struct QListNode* _next;
// QDataType _data;
//}QNode;
// 队列的结构
//typedef struct Queue
///{
// QNode* _front;
// QNode* _rear;
//}Queue;
QNode* BuyQueueNode(QDataType x)
{
QNode* cur = (QNode*)malloc(sizeof(QNode));
cur->_data = x;
cur->_next = NULL;
return cur;
}
// 初始化队列
void QueueInit(Queue* q)
{
q->_front = NULL;
q->_rear = NULL;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
QNode* cur = BuyQueueNode(data);
if (q->_front == NULL)//相当于q._front ==NULL
{
q->_front = q->_rear = cur;
}
else//有节点就在队列最后尾插,让rear后移一位
{
q->_rear->_next = cur;
q->_rear = cur;
}
}
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);//出队列就相当于链表的头删一样
if (q->_front == NULL)
return;
QNode* cur = q->_front->_next;
free(q->_front);
q->_front = cur;
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
return q->_front->_data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
return q->_rear->_data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
QNode* cur;
int cnt = 0;
for (cur = q->_front; cur; cur = cur->_next)
{
cnt++;
}
return cnt;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
if (q->_front == NULL)
return 1;
else return 0;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
if (q->_front == NULL)
return;
while (q->_front)
{
QueuePop(q);
}
}
Queuetest.c
#include"Queue.h"
int main()
{
Queue q ;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 1);
QueuePush(&q, 1);
QueuePush(&q, 1);
QueuePush(&q, 1);
QueuePush(&q, 1);
}