数据结构之队列

一、队列的定义

  1.队列是一种特殊的线性表
  
  2.队列只能在线性表的两端进行操作:
   队头(Front):取出数据元素的一段;
   队尾(Rear):插入数据元素的一段。

二、队列的特性  

  由于队列只能在队头(Front)取数据,在队尾(Rear)插入数据,故队列的特性为:先进先出(First In First Out)。如下图所示:
  
这里写图片描述
(注:此图引用自狄泰软件学院唐老师ppt)

三、队列的操作
  • 创建队列(Queue())
  • 销毁队列(~Queue())
  • 清空队列(clear())
  • 进队列(add())
  • 出队列(remove())
  • 获取队列头元素(front())
  • 获取队列的长度(length())
四、队列的实现

1.队列的抽象实现

Queue.h

#ifndef QUEUE_H
#define QUEUE_H

namespace Queue {

template <typename T>
class Queue
{
public:
    virtual void add(const T& e) = 0;
    virtual void remove() = 0;
    virtual T front() = 0;
    virtual void clear() = 0;
    virtual int length() const = 0;
};

}
#endif // QUEUE_H

2.静态队列的实现

设计要点:
  1.使用原生数组作为队列的存储空间;
  
  2.队列的存储空间在创建队列的时候指定(由模板参数指定),且不能动态的改变。
  
  3.为了提高队列的效率,采用循环计数方式:
  
  关键操作:

进队列:m_space[m_rear] = e; m_rear = (m_rear + 1) % N;
出队列:m_front = (m_front + 1) % N;

  队列状态:

队空:(m_length == 0) && (m_front == m_rear);
队满:(m_length == N) && (m_front == m_rear);

下面给出静态队列的实现:

#ifndef STATICQUEUE_H
#define STATICQUEUE_H

#include "Queue.h"

namespace Queue{

template <typename T, int N>
class StaticQueue : public Queue<T>
{
protected:
    T m_space[N];   // 存储空间
    int m_front;    // 对头标识
    int m_rear;     // 队尾标识
    int m_length;   // 当前队列的长度
public:
    StaticQueue()
    {
        m_front = 0;
        m_rear = 0;
        m_length = 0;
    }

    int capacity() const    // O(1)
    {
        return N;
    }

    void add(const T& e)    // O(1)
    {
        if (m_length < N) {
            m_space[m_rear] = e;
            m_rear = (m_rear + 1) % N;  // 采用循环计数,以提高效率
            m_length ++;
        } else {
            throw("No space in current queue ...");
        }
    }

    void remove()    // O(1)
    {
        if (m_length > 0) {
            m_front = (m_front + 1) % N;  // 采用循环计数,以提高效率
            m_length --;
        } else {
            throw("No element in current queue ...");
        }
    }

    T front()    // O(1)
    {
        if (m_length > 0) {
            return m_space[m_front];
        } else {
            throw("No element in current queue ...");
        }
    }

    void clear()    // O(1)
    {
        m_front = 0;
        m_rear = 0;
        m_length = 0;
    }

    int length() const    // O(1)
    {
        return m_length;
    }
};

}

#endif // STATICQUEUE_H

3.链式队列的实现

  由于静态队列一次性创建指定大小的空间,如果队列的数据元素为类类型的话,就需要调用N次构造函数,故效率会比较低;另静态队列没法动态的改变队列的大小。
  
  基于以上两点,链式队列便可以解决上述两个问题。 

设计要点:
  1.用类模板实现,是抽象父类Queue的直接子类;
  
  2.在内部使用链式结构实现元素的存储;
  
  3.为了提高队列操作的效率,内部采用双向循环链表存储数据。
  
  4.只在链表的头部和尾部进行操作;

下面给出链式队列的实现:

#ifndef LINKQUEUE_H
#define LINKQUEUE_H

#include "Queue.h"

namespace Queue {

template <typename T>
class LinkQueue : public Queue<T>
{
protected:
    struct Node
    {
        T value;
        struct Node* next;
        struct Node* prev;
    };
    // 为了防止泛指类型T在构造函数中抛出异常,导致队列初始化失败,故重新实现头结构
    struct    
    {
        char reserved[sizeof(T)];
        struct Node* next;
        struct Node* prev;
    } m_header;
    int m_length;
public:
    LinkQueue()
    {
        m_length = 0;

        m_header.next = reinterpret_cast<Node*>(&m_header);
        m_header.prev = reinterpret_cast<Node*>(&m_header);
    }

    void add(const T& e)    // O(1)
    {
        Node* node = new Node();

        if (NULL != node) {
            node->value = e;

            Node* prev = m_header.prev;

            /*将元素插入到链表的最后位置*/
            m_header.prev = node;   
            node->prev = prev;
            prev->next = node;
            node->next = reinterpret_cast<Node*>(&m_header);

            m_length ++;
        } else {
            throw("No memory to add element ...");
        }
    }

    void remove()   // O(1)
    {
        if (m_length > 0) {
            Node* toDel = m_header.next;
            Node* next = toDel->next;

            /*删除第一个元素*/
            m_header.next = next;
            next->prev = reinterpret_cast<Node*>(&m_header);

            m_length --;

            delete toDel;
        } else {
            throw("No element in current queue ...");
        }
    }

    T front()   // O(1)
    {
        if (m_length > 0) {
            return m_header.next->value;
        } else {
            throw("No element in current queue ...");
        }
    }

    void clear()    // O(n)
    {
        while (m_length > 0) {
            remove();
        }
    }

    int length() const  // O(1)
    {
        return m_length;
    }

    ~LinkQueue()
    {
        clear();
    }
};

}

#endif // LINKQUEUE_H

测试:
main.cpp

#include <iostream>
#include <string>

#include "LinkQueue.h"

using namespace std;
using namespace Queue;

int main()
{  
    LinkQueue<int> queue;

    for (int i=0; i<5; i++) {
        queue.add(i);
    }

    while (queue.length() > 0) {
        cout <<queue.front() << endl;

        queue.remove();
    }

    return 0;
}

运行结果如下图所示:

这里写图片描述

  由上图可以得出,最先进对列的数被先取出来,即先进先出,符合队列的特性。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值