目录
- 队列的概述
- 知识基础
- 队列的基本操作
- 队列的存储方式
- 代码实现(C++)
- 类头(Linked_Queue.h)
- 类的方法实现(Linked_Queue.cpp)
- 构造函数
- 拷贝构造函数
- 析构函数
- 判断队列是否为空(empty)
- 入队(push)
- 出队(pop)
- 清空队列(clear)
- 访问队首(front)与队尾(back)
- 操作符重载=
- 获取元素个数(size)
- 练习:约瑟夫问题
- 题目描述
- 输入格式
- 输出格式
- 样例 #1
- 样例输入 #1
- 样例输出 #1
- 提示
- 答案
队列的概述
队列(Queue)也是一种基本的数据结构,与栈类似,队列可以用来存储数据,并对数据进行插入删除操作,与栈不同的是,队列遵循的顺序是先进先出(你一定还记得栈是后进先出,我们用的刷盘子来举例),想象一列火车进隧道,车头先进的隧道,那么先出隧道的也是车头,这便是队列的原理。
本文将使用C++对队列进行封装。
知识基础
阅读本文,你需要具备以下知识基础:
- C++基础知识:类的封装、模版类、操作符重载。
- 代码能力:单链表的实现。
队列的基本操作
在队列中,允许进行数据插入的一端称为队尾,进行数据删除的一端称为队首。队列的基本操作主要有两个:
- 入队(push):在队尾插入一个元素。
- 出队(pop):从栈首移除一个元素。
除了这两个基本操作外,队列通常还有以下几个附加操作:
- 查看队首元素(front):查看队首的元素。
- 查看队尾元素(back):查看队尾的元素。
- 判断队列是否为空(empty):检查队列中是否含有元素。
- 获取队列的大小(size):返回队列中元素的个数。
- 清空队列(clear):将队列内元素清空。
队列的存储方式
在编程语言中,队列可以通过数组或者链表来实现。两种实现方式各有优缺点:数组队列访问元素更快,实现更加简单,但是大小固定,可能存在队列满的情况;链式队列的大小动态可变,但是访问元素需要通过指针,速度相对较慢。
在实际应用中,我们更多的将是使用链表队列,因此,本文只进行链表队列实现的讲解。
代码实现(C++)
类头(Linked_Queue.h)
#ifndef LINKED_QUEUE_H
#define LINKED_QUEUE_H
// 链表节点结构
template<typename T> struct node {
T data; // 数据域
node<T>* next; // 指向下一个节点的指针
};
// 链式队列类
template<typename T> class Linked_Queue {
protected: //设为private也可,protected用于方便继承
node<T>* front_node; // 队列的头节点指针
node<T>* back_node; // 队列的尾节点指针
public:
Linked_Queue(); // 构造函数
Linked_Queue(const Linked_Queue<T>& a); // 拷贝构造函数
bool empty(); // 判断队列是否为空
void push(T data); // 入队操作
void pop(); // 出队操作
void clear(); // 清空队列
const T front(); // 获取队首元素
const T back(); // 获取队尾元素
void operator =(const Linked_Queue<T>& a); // 赋值重载函数
int size(); // 返回队列元素个数
~Linked_Queue(); // 析构函数
};
#endif
类的方法实现(Linked_Queue.cpp)
构造函数
template<typename T> Linked_Queue<T>::Linked_Queue() {
// 前态:无
// 后态:创建一个空的Linked_Queue对象
front_node = NULL;
back_node = NULL;
}
拷贝构造函数
template<typename T> Linked_Queue<T>::Linked_Queue(const Linked_Queue<T>& a) {
// 前态:当前Linked_Queue对象为空
// 后态:当前Linked_Queue对象包含了a中所有元素的副本
front_node = NULL;
back_node = NULL;
node<T>* current = a.front_node;
while (current != NULL) { //对a进行遍历,将其元素push进入this队列
push(current->data);
current = current->next;
}
}
析构函数
template<typename T> Linked_Queue<T>::~Linked_Queue() {
// 前态:Linked_Queue对象可能包含元素
// 后态:Linked_Queue对象不包含任何元素
clear();
}
判断队列是否为空(empty)
template<typename T> bool Linked_Queue<T>::empty() {
// 前态:Linked_Queue对象可能包含元素
// 后态:Linked_Queue对象可能包含含元素
if (front_node == NULL) return true;
else return false;
}
入队(push)
template<typename T> void Linked_Queue<T>::push(T data) {
// 前态:Linked_Queue对象可能包含元素
// 后态:Linked_Queue对象包含了一个新元素data
node<T>* p = new node<T>;
p->data = data;
p->next = NULL;
if (back_node == NULL) back_node = p; //队列为空的情况
else {
back_node->next = p;
back_node = p;
}
if (front_node == NULL) front_node = p; //队列为空的情况
}
出队(pop)
template<typename T> void Linked_Queue<T>::pop() {
// 前态:Linked_Queue对象可能包含元素
// 后态:Linked_Queue对象中第一个元素被移除
if (front_node == NULL) { //队列为空
cout << "Empty!" << endl;
return;
}
node<T>* p = front_node->next;
delete front_node;
front_node = p;
if (front_node == NULL) back_node = NULL;
}
清空队列(clear)
template<typename T> void Linked_Queue<T>::clear() {
// 前态:Linked_Queue对象可能包含元素
// 后态:Linked_Queue对象不包含任何元素
while (front_node != NULL) { //遍历释放节点
node<T>* p = front_node->next;
delete front_node;
front_node = p;
}
front_node = NULL;
back_node = NULL;
}
访问队首(front)与队尾(back)
template<typename T> const T Linked_Queue<T>::front() {
// 前态:Linked_Queue对象可能包含元素
// 后态:无
if (front_node == NULL) {
cout << "Empty!" << endl;
return -1;
}
return front_node->data;
}
template<typename T> const T Linked_Queue<T>::back() {
// 前态:Linked_Queue对象可能包含元素
// 后态:无
if (back_node == NULL) {
cout << "Empty!" << endl;
return -1;
}
return back_node->data;
}
操作符重载=
template<typename T> void Linked_Queue<T>::operator =(const Linked_Queue<T>& a) {
// 前态:当前Linked_Queue对象包含的元素可能和a对象不同
// 后态:当前Linked_Queue对象包含了a中所有元素的副本
clear();
node<T>* current = a.front_node;
while (current != NULL) {
push(current->data);
current = current->next;
}
}
获取元素个数(size)
template<typename T> const int Linked_Queue<T>::size() {
// 前态:Linked_Queue对象可能包含元素
// 后态:无
int count = 0;
node<T>* p = this->front_node;
while (1) { //遍历查询节点个数
if (p == NULL) break;
count++;
p = p->next;
}
return count;
}
练习:约瑟夫问题
题目描述
n n n 个人围成一圈,从第一个人开始报数,数到 m m m 的人出列,再由下一个人重新从 1 1 1 开始报数,数到 m m m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
输入格式
输入两个整数 n , m n,m n,m。
输出格式
输出一行 n n n 个整数,按顺序输出每个出圈人的编号。
样例 #1
样例输入 #1
10 3
样例输出 #1
3 6 9 2 7 1 8 5 10 4
提示
1 ≤ m , n ≤ 100 1 \le m, n \le 100 1≤m,n≤100
答案
#include<iostream>
#include<queue> //使用STL模版库中的队列
//#include"Linked_Queue.cpp" //也可使用我们刚刚自己封装的队列
using namespace std;
int main() {
int m,n;
queue<int> q;
cin>>n>>m;
for (int i=1;i<=n;i++) {
q.push(i);
}
int count=1;
while (!q.empty()) {
int tmp=q.front();
q.pop();
if (count%m==0) {
cout<<tmp<<" ";
}
else {
q.push(tmp);
}
count++;
}
return 0;
}