目录
嗨,感谢你打开这篇文章!👋 如果阅读中发现任何笔误或表述不清的地方,欢迎随时留言告诉我~你的视角会让内容变得更完善!
一、队列概述
1、队列的基本概念
队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。
2、队列的具体实现
-
顺序队列
-
链式队队
二、顺序队列
在实现顺序队列之前,我们先来看一看对于顺序队列的操作:
顺序队列可以使用一维数组实现,在顺序队列中有两个指针,一个指针front指向队列的队首(数组的首个元素),一个指针rear指向队列的队尾(最后一个放入队列的元素的后一个空间)。
1、关于顺序队列操作的思考
1.如果对顺序队列进行出队操作,队首指针该如何移动?
答:队首指针往后移动
2.对顺序队列进行出队操作后,被出队的元素所占用的空间怎处理?
答:只能暂时空着,等待下次循环使用
3.如何判断顺序队列是否为满,是否为空呢?
答:判断队列为满和空的依据都是front == rear,因此会有冲突
4.如何判断队列是否溢出呢?
答:(1)当front为0,rear等于队列最大长度时为真溢出
(2)当front不为0,rear等于队列最大长度时为假溢出
解决方案:使用循环队列。
2、循环队列的概念
队列上的各个元素逻辑上形成一个圆环状
1.如何判断队列是否为满呢?
答:(1)将队列上的一个位置作为空闲位置
(2) 假设队列的长度为M,当 (rear + 1)% M == font时,认为队列为满
3、代码实现一个顺序队列
1、顺序队列结构体的描述
#define TRUE 1
#define FALSE 0
typedef int Elemtype;
typedef unsigned int uint;
#define QUEUE_INIT_LEN 10 //初始化长度
//顺序队列结构体的描述
typedef struct sqQueue
{
Elemtype* base; //存储堆上元素的地址空间的首地址
uint front; //队首指针 下标索引 用下标当成指针
uint rear; //队尾指针
}sqQueue;
2、循环顺序队列的初始化
/*
*@brief 初始化一个顺序队列
* @param len 顺序队列的初始化长度(可以存储的元素的最大个数)
* @return 返回初始化的顺序队列
* */
sqQueue sqQuene_init(uint len)
{
sqQueue s;
s.base = (Elemtype*)malloc(len * sizeof(Elemtype));
s.front = 0;
s.rear = 0;
return s;
}
3、获取顺序队列的长度(实际存储的元素的个数)
/*
* @brief 获取顺序队列的长度
* @param s 顺序队列
* @return 顺序队列的长度
* */
int SqQueue_length(sqQueue s)
{
return s.rear - s.front;
}
4、判断顺序队列是否为空
/*
* @brief 判断顺序队列是否为空
* @param 顺序队列
* @return 为空返回TRUE,不为空返回FALSE
* */
int is_empty(sqQueue s)
{
if (s.front == s.rear)
{
return TRUE;
}
else
{
return FALSE;
}
}
5、判断顺序队列是否为满
/*
* @brief 判断顺序队列是否为满
* @param 顺序队列
* @return 为满返回TRUE,不为满返回FALSE
* */
int is_full(sqQueue s)
{
if ((s.rear + 1) % QUEUE_INIT_LEN == 0)
return TRUE;
else
return FALSE;
}
6、入队
/*
* @brief 循环顺序队列入队
* @param data 需要入队得元素
* @return 成功返回TRUE, 失败返回FALSE
* */
int EnQueue(sqQueue* s, Elemtype data)
{
if (NULL == s )
{
return FALSE;
}
if (is_full(*s) == TRUE)
{
printf("[%s %d] queue is empty ...\n", __FUNCTION__, __LINE__);
return FALSE;
}
//将需要入队的元素保存在队尾指针rear所指向的空间中
s->base[s->rear] = data;
//队尾指针rear往后移一个位置
s->rear = (s->rear + 1) % QUEUE_INIT_LEN;
return TRUE;
}
7、顺序队列打印的元素
/*
* @brief 打印顺序队列中的元素
* @param s 顺序队列结构体
* @return
* */
int printf_sqQueue(sqQueue s)
{
if (is_empty(s) == TRUE)
{
printf("[%s %d] queue is empty ...\n", __FUNCTION__, __LINE__);
return FALSE;
}
int i = s.front;
printf("printf sqQueue:");
while (i != s.rear)
{
printf(" %d", s.base[i]);
i = (i + 1) % QUEUE_INIT_LEN;
}
printf("\n");
return TRUE;
}
8、出队
/*
* @brief 循环顺序队列出队
* @param data 存储出队元素的指针
* @return 成功返回TRUE, 失败返回FALSE
* */
int DeQueue(sqQueue* s, Elemtype* data)
{
if (NULL == s || NULL == data)
{
return FALSE;
}
if (is_empty(*s) == TRUE)
{
printf("[%s %d] queue is empty ...\n", __FUNCTION__, __LINE__);
return FALSE;
}
//取出队首元素:将队首元素存储到的data指针所指向的空间
*data = s->base[s->front];
//队首指针往后移动一个位置
s->front = (s->front + 1) % QUEUE_INIT_LEN;
return TRUE;
}
9、完整代码
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int Elemtype;
typedef unsigned int uint;
#define QUEUE_INIT_LEN 10 //初始化长度
//顺序队列结构体的描述
typedef struct sqQueue
{
Elemtype* base; //存储堆上元素的地址空间的首地址
uint front; //队首指针 下标索引 用下标当成指针
uint rear; //队尾指针
}sqQueue;
/*
*@brief 初始化一个顺序队列
* @param len 顺序队列的初始化长度(可以存储的元素的最大个数)
* @return 返回初始化的顺序队列
* */
sqQueue sqQuene_init(uint len)
{
sqQueue s;
s.base = (Elemtype*)malloc(len * sizeof(Elemtype));
s.front = 0;
s.rear = 0;
return s;
}
/*
* @brief 获取顺序队列的长度
* @param s 顺序队列
* @return 顺序队列的长度
* */
int SqQueue_length(sqQueue s)
{
return s.rear - s.front;
}
/*
* @brief 判断顺序队列是否为空
* @param 顺序队列
* @return 为空返回TRUE,不为空返回FALSE
* */
int is_empty(sqQueue s)
{
if (s.front == s.rear)
{
return TRUE;
}
else
{
return FALSE;
}
}
/*
* @brief 判断顺序队列是否为满
* @param 顺序队列
* @return 为满返回TRUE,不为满返回FALSE
* */
int is_full(sqQueue s)
{
if ((s.rear + 1) % QUEUE_INIT_LEN == 0)
return TRUE;
else
return FALSE;
}
//打印队列数据
int printf_sqQueue(sqQueue s)
{
if (is_empty(s) == TRUE)
{
printf("[%s %d] queue is empty ...\n", __FUNCTION__, __LINE__);
return FALSE;
}
int i = s.front;
printf("printf sqQueue:");
while (i != s.rear)
{
printf(" %d", s.base[i]);
i = (i + 1) % QUEUE_INIT_LEN;
}
printf("\n");
return TRUE;
}
/*
* @brief 循环顺序队列入队
* @param data 需要入队得元素
* @return 成功返回TRUE, 失败返回FALSE
* */
int EnQueue(sqQueue* s, Elemtype data)
{
if (NULL == s )
{
return FALSE;
}
if (is_full(*s) == TRUE)
{
printf("[%s %d] queue is empty ...\n", __FUNCTION__, __LINE__);
return FALSE;
}
//将需要入队的元素保存在队尾指针rear所指向的空间中
s->base[s->rear] = data;
//队尾指针rear往后移一个位置
s->rear = (s->rear + 1) % QUEUE_INIT_LEN;
return TRUE;
}
/*
* @brief 循环顺序队列出队
* @param data 存储出队元素的指针
* @return 成功返回TRUE, 失败返回FALSE
* */
int DeQueue(sqQueue* s, Elemtype* data)
{
if (NULL == s || NULL == data)
{
return FALSE;
}
if (is_empty(*s) == TRUE)
{
printf("[%s %d] queue is empty ...\n", __FUNCTION__, __LINE__);
return FALSE;
}
//取出队首元素:将队首元素存储到的data指针所指向的空间
*data = s->base[s->front];
//队首指针往后移动一个位置
s->front = (s->front + 1) % QUEUE_INIT_LEN;
return TRUE;
}
int main()
{
sqQueue s;
s = sqQuene_init(QUEUE_INIT_LEN);
int i = 0;
for (i = 0; i < 9; i++)
{
EnQueue(&s, i);
}
printf_sqQueue(s);
printf("printf DeQueue:");
while (is_empty(s) != TRUE)
{
Elemtype data;
DeQueue(&s, &data);
printf(" %d", data);
}
printf("\n");
return 0;
}
三、链式队列
链式队列可以理解为对单向链表的操作,入队就是单向链表的尾插法,出队则需要销毁第一个数据结点(类似删除链表上的第一个数据结点)。
1、链式队列结构体的描述
#define TRUE 1
#define FALSE 0
typedef int Elemtype;
typedef unsigned int uint;
/*
* 链式队列可以理解成一个通过尾插法创建的链表(可以带头结点也可以不带头结点)
* */
//定义一个结构体描述的链表的结点
typedef struct lnode
{
Elemtype data;
struct lnode* next; //指向当前结点的下一个结点的指针(保存了下一个结点的存储位置)
}lnode;
//定义一个结构体描述的链表队列
typedef struct Linkqueue
{
lnode* front;//链表队列首指针
lnode* rear; //链表队列尾指针
}Linkqueue;
2、链式队列的初始化
*
* @brief 初始化一个链式队列
* @return 代表链式队列的结构体
* */
Linkqueue queue_init()
{
//创建一个链式队列
Linkqueue s;
s.front = NULL;
s.rear = NULL;
return s;
}
3、入队
/*
* @brief 入队
* @param s 链式队列的指针
* @param data 需要插入的元素
* @return 成功返回TRUE,失败返回FALSE
* */
int EnQueue(Linkqueue* s, Elemtype data)
{
if (NULL == s)
return FALSE;
//创建一个结点
lnode* p = (lnode*)malloc(sizeof(lnode));
if (NULL == p)
{
return FALSE;
}
p->data = data;
p->next = NULL;
//先判断队列是否为空队列
if (NULL == s->front)
{
s->front = p;
s->rear = p;
return TRUE;
}
//如果队列不为空,那么将rear指向新的结点
s->rear->next = p;
s->rear = p;
return TRUE;
}
4、打印链式队列的元素
/*
* @brief 打印链式队列中的元素
* @param s 链式队列结构体
* @return
* */
void print_queue(Linkqueue s)
{
lnode* temp;
temp = s.front;
printf("printf queue:");
while (temp != NULL)
{
printf(" %d", temp->data);
temp = temp->next;//让tmp指针指向下一个结点
}
printf("\n");
}
5、出队
/*
* @brief 出队
* @param s 链式队列的指针
* @param data 需要得到的元素的地址
* @return 成功返回TRUE,失败返回FALSE
* */
int DeQueue(Linkqueue* s, Elemtype* data)
{
if (NULL == s || NULL == data)
return FALSE;
if (NULL == s->front)
return FALSE;
//首先保存链式队列上的第一个结点的值
*data = s->front->data;
//使用临时指针执行队首
lnode *temp = s->front;
//队首指针指向后一个结点(出队后原来队列上的第二个结点变成了新的队首结点)
s->front = s->front->next;
//删除原来的队首结点
free(temp);
return TRUE;
}
6、完整代码
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int Elemtype;
typedef unsigned int uint;
/*
* 链式队列可以理解成一个通过尾插法创建的链表(可以带头结点也可以不带头结点)
* */
//定义一个结构体描述的链表的结点
typedef struct lnode
{
Elemtype data;
struct lnode* next; //指向当前结点的下一个结点的指针(保存了下一个结点的存储位置)
}lnode;
//定义一个结构体描述的链表队列
typedef struct Linkqueue
{
lnode* front;//链表队列首指针
lnode* rear; //链表队列尾指针
}Linkqueue;
/*
* @brief 初始化一个链式队列
* @return 代表链式队列的结构体
* */
Linkqueue queue_init()
{
//创建一个链式队列
Linkqueue s;
s.front = NULL;
s.rear = NULL;
return s;
}
/*
* @brief 打印链式队列中的元素
* @param s 链式队列结构体
* @return
* */
void print_queue(Linkqueue s)
{
lnode* temp;
temp = s.front;
printf("printf queue:");
while (temp != NULL)
{
printf(" %d", temp->data);
temp = temp->next;//让tmp指针指向下一个结点
}
printf("\n");
}
/*
* @brief 入队
* @param s 链式队列的指针
* @param data 需要插入的元素
* @return 成功返回TRUE,失败返回FALSE
* */
int EnQueue(Linkqueue* s, Elemtype data)
{
if (NULL == s)
return FALSE;
//创建一个结点
lnode* p = (lnode*)malloc(sizeof(lnode));
if (NULL == p)
{
return FALSE;
}
p->data = data;
p->next = NULL;
//先判断队列是否为空队列
if (NULL == s->front)
{
s->front = p;
s->rear = p;
return TRUE;
}
//如果队列不为空,那么将rear指向新的结点
s->rear->next = p;
s->rear = p;
return TRUE;
}
/*
* @brief 出队
* @param s 链式队列的指针
* @param data 需要得到的元素的地址
* @return 成功返回TRUE,失败返回FALSE
* */
int DeQueue(Linkqueue* s, Elemtype* data)
{
if (NULL == s || NULL == data)
return FALSE;
if (NULL == s->front)
return FALSE;
//首先保存链式队列上的第一个结点的值
*data = s->front->data;
//使用临时指针执行队首
lnode *temp = s->front;
//队首指针指向后一个结点(出队后原来队列上的第二个结点变成了新的队首结点)
s->front = s->front->next;
//删除原来的队首结点
free(temp);
return TRUE;
}
int main()
{
//创建一个链式队列
Linkqueue s = queue_init();
int i = 0;
for (i = 0; i < 5; i++)
{
EnQueue(&s, i);
}
print_queue(s);
Elemtype data = 0;
printf("printf dequeue:");
while (DeQueue(&s, &data) != FALSE)
{
printf(" %d", data);
}
printf("\n");
return 0;
}