形状
队列是一种先进后出的线性表,它允许插入的一端叫队尾,允许删除的一端叫队头。
与平时排队一样,从队伍后面排,从队伍前面出。
当多个任务同时运行且需要通过同一个通道输出时,就要按照请求顺序来排队,此时便可使用队列。
顺序表示(顺序队列)
头指针front始终指向队头,当队列未满时,rear始终指向队尾元素的下一个。
#define MAXQSIZE 100
typedef struct
{
QElemType *base;//存储空间基地址
int front;//头指针,int类型,存储队伍头地址
int rear;//尾指针,int类型,存储队伍尾地址
}SqQueue;
为解决入队出队导致空间有些没用到的问题(如下图所示,经过四次操作后,尾指针已经指向最后一个位置,而前面是空但是因为头指针不能插入了),我们使队列变成一个环形结构,称为循环队列。
其他与原理一致,只不过在循环队列中头尾指针加一时使用模运算来实现:
- Q.rear =(Q.rear + 1) % MAXQSIZE
- Q.front =(Q.front + 1) % MAXQSIZE
判断队满队空:少用一个数据元素(有一个地方始终为空),即m个位置,m-1个元素则算队列满了:
- 队空 -> Q.rear = Q.front
- 队满 -> (Q.rear + 1) % MAXQSIZE = Q.front
初始化
Status InitQueue(SqQueue& Q)
{
Q.base = new SqElemType[MAXQSIZE];//顺序表,需要开辟一段空间
if (!Q.base) exit(OVERFLOW);//分配失败
Q.rear = Q.front = 0;//空队列
return ok;
}
获取长度
int QueueLength(SqQueue Q)
{
return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;//循环队列可能出现尾减头为负数的情况,所以要加上MAXQSIZE;当为正数加上MAXQSIZE会大于实际大小,则取余MAXQSIZE,得出大小
}
入队
Status EnQueue(SqQueue& Q, QElemType e)
{
if ((Q.rear + 1) % MAXQSIZE == Q.front) return error;//队列已满
Q.base[Q.rear] = e;//把新元素放入队列
Q.rear = (Q.rear + 1) % MAXQSIZE;//尾指针加一
return ok;
}
出队
Status DeQueue(SqQueue& Q, QElemType& e)
{
if (Q.rear == Q.front) return error;//队列为空
e = Q.base[Q.front];//把元素取出
Q.front = (Q.front + 1) % MAXQSIZE;//头指针加一,队列头出队后,要往后移,地址是增加的
return ok;
}
取队头元素
QElemType GetHead(SqQueue Q)
{//返回队头元素,不改变头指针
if(Q.front != Q.rear)//队列非空
return Q.base[Q.front];
}
链式表示(链队列)
队列的删除插入地方不一样,故而需要两个指针,为了安全和方便操作,使用结构将其封装起来;
所以需要先创造节点结构,再创造链表结构,使链表结构中有整个队列的头尾指针,区分操作;
而栈只在栈顶操作,所以只需要一个指针,在函数内直接定义一个即可串起来全部。
无论是栈还是队列,在链式表示中,在栈底与队列头都需要一个全空的节点作为判断。
#define MAXQSIZE 100
typedef struct QNode
{
QElemType data;
struct QNode *next;//存储空间基地址
}QNode, *QueuePtr;
typedef struct
{
QueuePtr front;//头指针,int类型,存储队伍头地址
QueuePtr rear;//尾指针,int类型,存储队伍尾地址
}LinkQueue;
初始化
Status InitQueue(LinkQueue& Q)
{
Q.front = Q.rear = new QNode;//申请一个头节点
Q.front->next = NULL;//头指针的next置空
return ok;
}
入队
Status EnQueue(LinkQueue& Q, QElemType e)
{//不需要判断队列满没有,因为链表是动态的;下面只改变尾指针是因为头指针只能删除用,故而用尾指针插入
QNode p = new QNode;//创建一个节点
p->data = e;
p->next = NULL;
Q.rear->next = p;//使新节点插入队尾
Q.rear = p;//队尾指针指向队尾元素
return ok;
}
出队
Status DeQueue(LinkQueue& Q, QElemType& e)
{
if (Q.rear == Q.front) return error;//队列为空
p = Q.front->next;//用一个新节点代替队头节点,便于等会挪完队头指针后,在内存空间删除它
e = p->data;
Q.front->next = p->next;//修改头节点,指向下一个
if(Q.rear = p) Q.rear = Q.front;//若最后一个元素被删除了,队尾指针指向队头指针
delete p;
return ok;
}
取队头元素
QElemType GetHead(LinkQueue Q)
{//返回队头元素,不改变头指针
if(Q.front != Q.rear)//队列非空
return Q.front->next->data;
}
在STL中队列的主要操作函数
1.queue<> q
创建一个空队列
queue<队列内数据的类型> q;
2. push()
将元素加入队尾(back)。
queue<int> q;
q.push(10); // 将 10 加入队尾
q.push(20); // 将 20 加入队尾
3. pop()
移除队头(front)元素。注意,pop()
不会返回被删除的元素。
q.pop(); // 移除队头元素 10
4. front()
返回队头元素的引用,但不删除它。
int front_element = q.front(); // 获取队头元素,值为 20
5. back()
返回队尾元素的引用,但不删除它。
int back_element = q.back(); // 获取队尾元素,值为 20
6. empty()
检查队列是否为空,若为空则返回 true
,否则返回 false
。
bool is_empty = q.empty(); // 检查队列是否为空
7. size()
返回队列中元素的数量。
size_t count = q.size(); // 队列中的元素数量
示例代码
#include <iostream>
#include <queue>//使用STL需要包含这个头文件
using namespace std;
int main() {
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
cout << "队头元素: " << q.front() << endl; // 输出 1
cout << "队尾元素: " << q.back() << endl; // 输出 3
q.pop(); // 移除队头元素 1
cout << "移除队头后的新队头: " << q.front() << endl; // 输出 2
cout << "队列的大小: " << q.size() << endl; // 输出 2
cout << "队列是否为空: " << (q.empty() ? "是" : "否") << endl;
return 0;
}