一、知识介绍
1.基本原理
在顺序队列中,我们使用一个固定大小的数组来存储队列中的元素,并使用两个指针(front
和 rear
)来分别表示队头和队尾的位置。
-
队列为空的条件:
front == rear
-
队列满的条件:
rear + 1 ≡ front (mod MAX_SIZE)
为了区分“队列满”和“队列空”的情况,我们牺牲一个存储单元,即当队尾指针的下一个位置(循环计算)等于队头指针时,认为队列已满,而不是等到队列完全填满。
2.实现方法
a.数据结构
b.判空和判满
-
判空:
front == rear
-
判满:
(rear + 1) % MAX_SIZE == fron
c.入队操作
当队列未满时,将元素添加到队尾,并更新队尾指针:
d.出队操作
当队列非空时,移除队头元素,并更新队头指针:
二、关键点解析
1.判断队列是否已满逻辑解析
核心逻辑:
队列满的条件是:队尾指针的下一个位置(循环计算)等于队头指针。换句话说,当 (rear + 1) % MAX_SIZE == front
时,队列已满。
为什么这样判断?
-
循环队列的特性:队列使用一个固定大小的数组来存储元素,并通过
front
和rear
指针来管理元素的插入和删除。 -
牺牲一个存储单元:为了区分“队列满”和“队列空”的情况,我们牺牲一个存储单元。
当rear + 1
等于front
时,认为队列已满,而不是等到队列完全填满。
假设 MAX_SIZE = 5,队列的数组大小为 5。
情况 1:队列满:
front = 0
rear = 4
(4 + 1) % 5 = 0,等于 front,所以队列满。
情况 2:队列未满
front = 0
rear = 3
(3 + 1) % 5 = 4,不等于 front,所以队列未满。
2. 入队逻辑
a. 判断队列是否已满
调用 IsFull
函数判断队列是否已满。
如果队列已满,打印提示信息并返回 false
,表示入队失败。
b.入队操作
将元素 value
存入队列的尾部位置(q->rear
)。
c.更新队尾指针
队尾指针 rear
加 1,并对数组大小 MAX_SIZE
取模,是为了确保指针在数组范围内循环。
d. 返回成功
3.出队逻辑
a.函数定义
返回值类型:bool
,表示出队操作是否成功。
参数:
SeqQueue *q
:指向队列的指针。
int *value
:指向一个整型变量的指针,用于存储出队的元素值。
b.判断队列是否为空
调用 IsEmpty
函数判断队列是否为空。
如果队列为空,打印提示信息并返回 false
,表示出队失败。
c.获取队头元素
将队列中队头位置(q->front
)的元素值赋给传入的指针变量 *value
。
-
q->data
是存储队列元素的数组。 -
q->front
是队头指针,指向当前队列的第一个元素的位置。 -
q->data[q->front]
通过数组索引访问队头元素。 -
*value
是一个指针解引用操作,将队头元素的值存储到传入的变量中。
d.更新队头指针
队头指针 front
加 1,并对数组大小 MAX_SIZE
取模,确保指针在数组范围内循环。
4.查找元素
a.检查队列是否为空
调用 IsEmpty
函数判断队列是否为空。
如果队列为空,打印提示信息并返回 -1
,表示无法查找。
b. 初始化变量
index
:初始化为队头指针 front
,用于遍历队列。
count
:初始化为0,用于记录当前遍历到的位置。
c. 遍历队列
循环条件:index != q->rear
,表示队列中还有元素未遍历。
查找逻辑:
如果当前索引位置的元素等于要查找的值,返回 count
,从0开始表示找到了元素。
如果未找到,更新 index
指针为下一个位置,并对数组大小 MAX_SIZE
取模,确保指针在数组范围内循环。
count
加1,记录当前遍历到的位置。
d.未找到元素
如果循环结束仍未找到元素,返回 -1
。
5.遍历逻辑
a.检查队列是否为空
调用 IsEmpty
函数判断队列是否为空。
如果队列为空,打印提示信息并返回,表示无法遍历。
b.打印队列元素并遍历
打印提示信息,表示接下来将输出队列中的元素。
初始化索引:index
初始化为队头指针 front
,用于遍历队列。
循环条件:index != q->rear
,表示队列中还有元素未遍历。
打印元素:通过 q->data[index]
访问当前索引位置的元素并打印。
更新索引:index = (index + 1) % MAX_SIZE
,更新索引为下一个位置,并对数组大小 MAX_SIZE
取模,确保指针在数组范围内循环。
打印换行符,使输出更整洁。
6.计算长度逻辑
a.为什么可以这样计算?
1. 直接计算 rear - front
在非循环队列中,队列的长度可以直接通过 rear - front
计算。例如:
-
如果
front = 0
,rear = 3
,队列长度为3 - 0 = 3
。 -
如果
front = 1
,rear = 4
,队列长度为4 - 1 = 3
。
2. 循环队列的特殊情况
在循环队列中,rear
可能小于 front
,因为队列是循环的。例如:
-
如果
front = 3
,rear = 0
,直接计算rear - front
会得到负数-3
,这显然不符合实际队列长度。
3. 解决负数问题
为了避免负数,公式中加上了 MAX_SIZE
这样可以确保结果始终是正数。例如:
-
如果
front = 3
,rear = 0
,MAX_SIZE = 5
:-
0 - 3 + 5 = 2
,表示队列中有 2 个元素。
-
4. 取模操作
这一步确保即使 rear
和 front
的差值超过 MAX_SIZE
,结果仍然正确
假设 MAX_SIZE = 5
:
情况 1:rear
大于 front
-
front = 0
-
rear = 3
-
计算:
(3 - 0 + 5) % 5 = 8 % 5 = 3
,队列长度为 3。
情况 2:rear
小于 front
-
front = 3
-
rear = 0
-
计算:
(0 - 3 + 5) % 5 = 2 % 5 = 2
,队列长度为 2。
情况 3:队列为空
-
front = 0
-
rear = 0
-
计算:
(0 - 0 + 5) % 5 = 5 % 5 = 0
,队列长度为 0。
三、完整代码
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>#define MAX_SIZE 10 // 队列的最大容量 // 定义队列结构
typedef struct
{int data[MAX_SIZE]; // 存储队列元素的数组 int front; // 队头指针int rear; // 队尾指针
}SeqQueue;// 初始化队列
void InitQueue(SeqQueue* q)
{q->front = 0;q->rear = 0;
}// 判断队列是否已满
bool isFull(SeqQueue* q)
{return ((q->rear + 1) % MAX_SIZE) == q->front;
}// 判断队列是否为空
bool isEmpty(SeqQueue* q)
{return q->front == q->rear;
}// 入队操作
bool Enqueue(SeqQueue* q, int value)
{if (isFull(q)){printf("队列已满,无法入队!\n");return false;}q->data[q->rear] = value;q->rear = (q->rear + 1) % MAX_SIZE;return true;
}// 出队操作
bool Dequeue(SeqQueue* q, int* value)
{if (isEmpty(q)){printf("队列为空,无法出队!\n");return false;}*value = q->data[q->front];q->front = (q->front + 1) % MAX_SIZE;return true;
}// 查找元素
int FindElement(SeqQueue* q, int value)
{if (isEmpty(q)){printf("队列为空,无法查找!\n");return -1;}int index = q->front;int count = 1;while (index != q->rear){if (q->data[index] == value){return count; // 返回元素在队列中的位置(从0开始)}index = (index + 1) % MAX_SIZE;count++;}return -1;
}// 遍历队列
void TraverseQueue(SeqQueue* q)
{if (isEmpty(q)){printf("队列为空,无法遍历!\n");return;}printf("队列元素:");int index = q->front;while (index != q->rear){printf("%d ", q->data[index]);index = (index + 1) % MAX_SIZE;}printf("\n");
}// 计算队列长度
int QueueLength(SeqQueue* q)
{return ((q->rear) - (q->front) + MAX_SIZE) % MAX_SIZE;
}void Menu()
{printf("\n===== 顺序队列操作菜单 =====\n");printf("1. 入队\n");printf("2. 出队\n");printf("3. 查找元素\n");printf("4. 遍历队列\n");printf("5. 计算队列长度\n");printf("0. 退出程序\n");printf("请输入您的选择:");
}int main()
{SeqQueue queue;InitQueue(&queue);int choice = 0;int value = 0;int pos = 0;while (1){Menu();scanf_s("%d", &choice);switch (choice){case 1:printf("请输入要入队的元素:>");scanf_s("%d", &value);if (Enqueue(&queue, value)){printf("入队成功!当前队列长度:>%d\n", QueueLength(&queue));}break;case 2:if (Dequeue(&queue, &value)){printf("出队元素:%d,当前队列长度:%d\n", value, QueueLength(&queue));}break;case 3:printf("请输入要查找的元素:");scanf_s("%d", &value);pos = FindElement(&queue, value);if (pos != -1){printf("元素 %d 在队列中的位置为:%d\n", value, pos);}else{printf("队列中不存在元素 %d\n", value);}break;case 4:TraverseQueue(&queue);break;case 5:printf("当前队列长度:%d\n", QueueLength(&queue));break;case 0:printf("程序结束!\n");exit(0);break;default:printf("无效的选择,请重新输入!\n");}}return 0;
}
以上是基于VS2022编译器,用c语言实现——顺序队列。判断队列已满或者空的情况是通过牺牲一个存储单元的方法来实现的。支持用户输入交互、入队、出队、查找、遍历等功能。