数据结构与算法C++描述(8)---队列

本文介绍了队列这一数据结构的概念及其两种实现方式:公式化描述和链表实现。重点讲解了队列的基本操作,包括添加元素和删除元素,并提供了详细的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、队列的概念

和堆栈一样,队列也是一种特殊的线性表。与堆栈不同的是,队列的插入和删除操作分别在线性表的两端进行,是一种先进先出的线性表。将添加新元素的那一端称为队尾(rear),删除元素的一端称为队首(front)。队列的相关操作见下图(图片来源于参考文献[1])
这里写图片描述
上图中:

  1. 图a)表示了一个具有3个元素的队列,其中front指向队首,rear指向队尾;
  2. 图b)展示了队列的删除元素操作,将原先的队首元素A删除,并且front指向了下一个元素B;
  3. 图c)展示了队列的添加元素操作,在队尾添加了元素D,并且rear指向了队尾元素D。

本文将利用公式化描述的方法和链表的方法来描述队列。

2、公式化描述的队列

2.1 描述队列公式的选择

在参考文献[1]中,分别分析了一下三个公式:
这里写图片描述
文中主要从元素的添加和删除操作的时间效率方面进行了分析,现加以总结。

  1. 对于公式1,考虑方式同普通的数组,初始时索引值从0~n-1。
    (1)执行添加元素操作,即在队尾添加一个元素,直接可添加到location=n的位置,接着将real指向添加元素即可,时间复杂度为 O(1)
    (2)执行删除操作,即删除队列首元素,将首元素删除后,剩余元素的索引值必须满足公式1,从而必须将之前的n-1个元素依次前移一个位置。时间复杂度为 Θ(n1)
  2. 对于公式2,考虑初始时具有n个元素。队首元素location=location(1),队尾元素location=location(1)+n-1。
    (1)如若执行删除操作,直接将队首元素删除,并将front指向下一个元素即可,时间复杂度为 O(1)
    (2)执行添加操作,将元素添加至当前real的下一个位置,并将real指向最后一个元素。考虑队列最大容量有限,那么在添加元素时,会遇到real=maxSize的情形,此时,若执行了队列的删除操作,在队列前部会有空余空间,若想充分利用此空间,则必须将所有的元素移到队首部分,此时时间复杂度为 Θ(m) ,其中m为队列中需要移动的元素个数。
  3. 对于公式3,比公式2多了“%maxSize”部分,单独考虑这部分可知,当 location(1)+i1 的值大于maxSize时,不会报出内存不足异常,若队列前部有剩余空间,会接着向队列中添加元素,并且元素索引满足公式3。太巧妙了!!!!!!但是,,这里要考虑一个极端情况,real的值会大于或等于front。为了避免此种情况,限制队列的最大长度=最大容量-1,即队列不会被填满。
    对于公式3,可借助于下图理解。
    这里写图片描述

2.2 队列公式化描述的C++实现

下面开始撸代码。。。。。。

2.2.1 类的声明与简单函数的实现
/*--------------------------基于公式Queue(i)=(Queue(1)+i-1)%MaxQueue描述的队列---------------------------*/
template <class T>
class Queue
{
public:
    Queue(int MaxSize = 10);
    ~Queue() { delete[] element; };
    bool IsEmpty()  const         //队列是否为空
    {   
        return Rear == Front;  
    }
    bool IsFull() const          //队列是否已满
    {
        return ((Rear + 1) % MaxQueueSize== Front )? 1 : 0;
    }
    int Length() const;          //队列中元素个数
    T getFront() const;          //获得队列首元素
    T getRear() const;           //获得队列末元素
    Queue<T> &Add(const T &x);   //插入元素x
    Queue<T> &Delete(T &x);      //删除末尾元素,并赋给x
    void Input(istream &in)      //输入一个队列
    {
        int size;
        cout << "请输入队列的大小: ";
        cin >> size;
        cout << "请输入队列元素:  " << endl;
        for(int i=0;i<size;i++)
        {
            T x;
            in >> x;
            Add(x);
        }
        cout << endl;
    }
    void Output(ostream &out)
    {
        while (!IsEmpty())
        {
            int x;
            Delete(x);
            out << x<< "  ";
        }
        cout << endl;
    }

private:
    T *element;                  //队列元素指针
    int MaxQueueSize;            //队列最大容量
    T Rear;                      //队列末元素
    T Front;                     //队列前段,指向首元素的前一个位置
};
2.2.2 创建队列—构造函数实现
template <class T>
Queue<T>::Queue(int MaxSize=10)
{
    //采用公式Queue(i)=(Queue(1)+i-1)%MaxQueue来描述队列,为了避免当real==front时,队列有可能为空或队列已满。
    //将要求的队列大小+1后,使得real的最大值为MaxQueueSize-1=MaxSize,刚好能存储MaxSize个元素,且避免了real==front时
    //带来的歧义。此时,当队列为空时,满足real==front;当队列满时,满足(real+1)%MaxQueueSize==front。
    MaxQueueSize = MaxSize + 1;       
    element = new T[MaxQueueSize];  //新建队列元素数组
    Rear = Front = 0;               //初始时将Real和Front置零
}
2.2.3 获取队首元素
//获得队列首元素
template <class T>
T Queue<T>::getFront() const
{
    if (!IsEmpty()) //队列不为空
        return element[(Front+1)%MaxQueueSize];
    else            //队列为空
        throw OutOfRange();
}
2.2.4 获取队尾元素
//获得队列末元素
template <class T>
T Queue<T>::getRear() const
{
    if (!IsEmpty())  //队列不为空
        return element[Rear];
    else            //队列为空
        throw OutOfRange();
}
2.2.5 插入元素x
 //插入元素x
template <class T>
Queue<T> &Queue<T>::Add(const T &x)
{
    if (IsFull())                 //若队列已满,则抛出内存不足异常
        throw NoMerm();
    else
    {
        //指向下一个位置,并赋值
        Rear = (Rear + 1) % MaxQueueSize;
        element[Rear] = x;
    }
    return *this;
}
2.2.6 删除元素,并赋给x
//删除队首元素,并赋给x
template <class T>
Queue<T> &Queue<T>::Delete(T &x)
{
    if (IsEmpty())               //若队列为空,则抛出异常
        throw OutOfRange();
    else {
        //获得队首元素,并将Front指向下一个位置
        x = element[(Front + 1) % MaxQueueSize];
        Front = (Front + 1) % MaxQueueSize;
    }
    return *this;
}
2.2.7 获取队列长度
//队列中元素个数
template <class T>
int Queue<T>::Length() const
{
    if (IsEmpty())
        return 0;
    else if (IsFull())
        return MaxQueueSize-1;     //体现出公式3的精妙之处
    else
        return (Rear-Front);
}
2.2.8 输入输出运算符重载
//重载“>>”
template <class T>
istream &operator>>(istream &in, Queue<T> &Q)
{
    Q.Input(in);
    return in;
}
//重载“<<”
template <class T>
ostream &operator<<(ostream &out, Queue<T> &Q)
{
    Q.Output(out);
    return out;
}
2.2.9 测试
/*-------------------------------公式化描述的队列----------------------------------*/
        //队列对象
        Queue<int> Q(5);
        Q.Add(1).Add(2).Add(3).Add(4);
        int x;
        //Q.Delete(x);
        //Q.Add(6);
        cout << "队首元素:  "<<Q.getFront() << endl<< "队尾元素:   "<<Q.getRear() << endl;
        //cout << "删除的元素:" << x << endl;
        cout << "队列长度:  " << Q.Length() << endl;
        Q.Add(7);
        //Q.Delete(x);
        cout << "队列长度:  " << Q.Length() << endl;
        //Queue<int> qq(10);
        //cin >> qq;
        //cout << qq << endl;;

3. 链表实现队列

链表实现队列时,原理很简单,就是将队列的限制条件(先进先出)应用于链表中,外加指针的相关操作。

3.3.1 链表队列类的声明及简单函数的实现

/*--------------------------基于链表描述的队列---------------------------*/
template <class T> class LinkedQueue;
//队列链表节点类
template <class T>
class Node
{
    friend LinkedQueue<T>;     //声明为LinkedQueue类的友类
public:
    Node() {};
    ~Node() {};

private:
    T data;
    Node<T> *link;
};
//链表队列类
template <class T>
class LinkedQueue
{
public:
    LinkedQueue() { front = rear = 0; };
    ~LinkedQueue();
    bool IsEmpty() const               //判断是否为空
    {
        return front == 0; 
    }
    bool IsFull() const;               //判断队列是否已满
    T Frist() const;                   //获取队列首元素
    T Last() const;                    //获取队列末尾元素
    int Length();                      //队列长度
    LinkedQueue<T> &Add(const T &x);   //添加元素
    LinkedQueue<T> &Delete(T &x);      //删除元素
    void Input(istream &in)            //输入链表队列
    {
        T x;
        int size;                      //队列大小
        cout << "请输入队列大小:  "<<endl;
        cin >> size;
        cout << "请输入队列元素:    " << endl;
        while (size && !IsFull())
        {
            in >> x;
            Add(x);
            size--;
        }
    }
    void Output(ostream &out)          //输出链表队列
    {
        if (IsEmpty())
            throw OutOfRange();
        else
        {
            Node<T> *next=front;
            while (next!= 0)
            {
                out << next->data<<"  ";
                next = next->link;
            }
            cout << endl;
        }
    }

private:
    Node<T> *front;                    //头节点
    Node<T> *rear;                     //末节点
};

3.2 删除链表队列—析构函数实现

//析构函数
template <class T>
LinkedQueue<T>::~LinkedQueue()
{
    Node<T> *next=new Node<T>;

    while (front)              //未到队列末尾
    {
        next = front->link;    //指向下一个节点
        delete front;          //删除当前节点
        front = next;          //循环
    }
}

3.3 判断队列是否已满

//判断队列是否已满
template <class T>
bool LinkedQueue<T>::IsFull() const
{       
    Node<T> *p;
    try {
        p = new Node<T>;         //若不能新建节点元素,则会引发new的异常NoMerm
        delete p;
        return false;
    }
    catch (NoMerm)               //捕获异常,存在于Error.h文件中
    {
        return true;
    }
}

3.4 获取队首元素

//获取队列首元素
template <class T>
T LinkedQueue<T>::Frist() const
{
    if (IsEmpty())              //队列为空,抛出越界异常
        throw OutOfRange();
    else                        //队列不为空,返回首元素
    {
        return front->data;
    }
}

3.5 获取队尾元素

//获取队列末尾元素
template <class T>
T LinkedQueue<T>::Last() const
{
    if (IsEmpty())               //队列为空,抛出越界异常
        throw OutOfRange();
    else                         //队列不为空,返回队列末尾元素
        return rear->data;
}

3.6 队列长度

 //队列长度
template <class T>
int LinkedQueue<T>::Length()
{
    if (front == rear)
        return 0;
    Node<T> *next=front;

    int len = 0;
    while (next)
    { 
        next = next->link;
        len++;
    }
    return len;
}

3.7 添加元素

//添加元素
template <class T>
LinkedQueue<T> &LinkedQueue<T>::Add(const T &x)
{
    Node<T> *p=new Node<T>;
    p->data = x;
    p->link = 0;

    if (front)             //队列不为空,当前末节点指向p
        rear->link=p;       
    else                   //队列为空
        front=p;           //p为队列首元素        
    rear = p;              //更新队列末节点
    return *this;
}

3.8 删除元素

//删除元素
template <class T>
LinkedQueue<T> &LinkedQueue<T>::Delete(T &x)
{
    if (IsEmpty())
        throw OutOfRange();
    else
    {
        x = front->data;
        Node<T> *next=front;
        front = front->link;
        delete next;
    }
    return *this;
}

3.9 重载输入输出运算符

//重载“>>”
template <class T>
istream &operator >> (istream &in, LinkedQueue<T> &Q)
{
    Q.Input(in);
    return in;
}
//重载“<<”
template <class T>
ostream &operator<<(ostream &out, LinkedQueue<T> &Q)
{
    Q.Output(out);
    return out;
}

3.10 测试

    /*--------------------------------链表描述的队列----------------------------------*/
        //队列对象
        LinkedQueue<int> Q;

        Q.Add(1).Add(2).Add(3).Add(4);
        int x;
        Q.Delete(x);
        Q.Add(6);
        cout << "队首元素:  " << Q.Frist()  << endl << "队尾元素:   " << Q.Last() << endl;
        cout << "删除的元素:" << x << endl;
        cout << "队列长度:  " << Q.Length() << endl;
        Q.Add(7);
        //Q.Delete(x);
        cout << "队列长度:  " << Q.Length() << endl;

        LinkedQueue<int> LQ;
        cin >> LQ;
        cout << LQ;

参考文献:
[1] 数据结构算法与应用:C++描述(Data Structures, Algorithms and Applications in C++ 的中文版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值