数据结构——Queue队列(C++)

队列的概述

队列(Queue)也是一种基本的数据结构,与栈类似,队列可以用来存储数据,并对数据进行插入删除操作,与栈不同的是,队列遵循的顺序是先进先出(你一定还记得是后进先出,我们用的刷盘子来举例),想象一列火车进隧道,车头先进的隧道,那么先出隧道的也是车头,这便是队列的原理。

本文将使用C++对队列进行封装。

知识基础

阅读本文,你需要具备以下知识基础:

  1. C++基础知识:类的封装、模版类、操作符重载。
  2. 代码能力:单链表的实现。

队列的基本操作

在队列中,允许进行数据插入的一端称为队尾,进行数据删除的一端称为队首。队列的基本操作主要有两个:

  1. 入队(push):在队尾插入一个元素。
  2. 出队(pop):从栈首移除一个元素。

除了这两个基本操作外,队列通常还有以下几个附加操作:

  1. 查看队首元素(front):查看队首的元素。
  2. 查看队尾元素(back):查看队尾的元素。
  3. 判断队列是否为空(empty):检查队列中是否含有元素。
  4. 获取队列的大小(size):返回队列中元素的个数。
  5. 清空队列(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 1m,n100

答案

#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;
}
  • 27
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值