四、队列的概念和实现

队列是一种特殊的线性表。

队列仅能在线性表的两端进行操作:

队头(Front):取出数据元素的一端;

队尾(Rear):插入数据元素的一端。

队列的特性——先进先出(First In First Out)

队列的操作:创建、销毁、清空(clear())、进队列(add())、出队列(remove())、获取队头元素(front())、获取队尾元素(length())。

父类队列的实现:

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

};

一、队列的顺序实现(StaticQueue)

类模板:使用原生数组作为队列的存储空间。

使用模板参数(N)决定队列的最大容量。

StaticQueue实现要点(循环计数法):

关键操作:


具体实现如下:

template <typename T, int N>  //使用模板参数决定队列的最大容量
class StaticQueue : public Queue<T>
{
protected:
    T m_space[N];  //队列存储空间,N为模板参数
    int m_front;  //队顶标识
    int m_rear;   //队尾标识
    int m_length;  //当前队列的大小
 public:
    StaticQueue()
    {
        m_rear = 0;
        m_length = 0;
        m_front = 0;
    }

    int capacity() const
    {
        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_EXCEPTION(InvalidOperationException,"No space in current queue...");
       }
    }

    void remove()//O(1)
    {
        if( 0 < m_length)
        {
           m_front = (m_front + 1) % N;   //变换队头
           m_length--;
        }
        else
        {
           THROW_EXCEPTION(InvalidOperationException,"No element in current queue...");
        }
    }

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

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

    int length() const//O(1)
    {
        cout<< m_front - m_rear<<endl;

        cout<< "printf again!" << endl;

        cout<< m_length <<endl;

        return m_length;
    }

};

运行测试代码

int main()
{
    StaticQueue<int, 5> sq;

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

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

        sq.remove();
    }

    return 0;
}

输出:01234。

小结:队列是一种特殊的线性表,具有先进先出的特性;

队列只允许在线性表的两端进行操作,一端进,一端出;

StaticQueue使用原生数组作为内部存储空间;

StaticQueue的最大容量由模板参数决定;

StaticQueue采用循环计数法来提高队列操作效率。


二、链式队列(LinkQueue)的实现

数据元素为类类型,StaticQueen的对象在创建时,会多次调用元素类型的构造函数,影响效率。

设计要点:类模板,抽象父类Queue的直接子类;

                 在内部使用链式结构实现元素的存储;

                 只在链表的头部和尾部进行操作。

在链式栈的实现中,可以使用单链表来实现,但是因为涉及到遍历操作(insert()),所以效率上并不是很高。


队列链式存储实现的优化:


通过Linux内核链表来实现链式队列:


template <typename T>
class LinkQueue : public Queue<T>
{
protected:
    //LinkList<T> m_list;

    struct Node : public Object  //带头结点 使用Linux内核链表
    {
      list_head head;
      T value;
    };

    list_head m_header;
    int m_length;

public:
    LinkQueue()
    {
        m_length = 0;

        INIT_LIST_HEAD(&m_header);  //初始化
    }

    void add(const T& e)
    {
        //m_list.insert(e);   //O(n)  与队列的元素个数成正比

        Node* node = new Node();

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

            list_add_tail(&node->head, &m_header);  //O(1)

            m_length++;
        }
        else
        {
            THROW_EXCEPTION(InvalidOperationException,"No memory to add new element...");
        }
    }

    void remove()  //O(1)
    {
        if(m_length > 0)
        {
            //m_list.remove(0);

            list_head* toDel = m_header.next; //头结点指向的位置

            list_del(toDel); //Linux内核链表的函数

            m_length--;

            delete list_entry(toDel, Node, head);
        }
        else
        {
            THROW_EXCEPTION(InvalidOperationException,"No element in current queue...");
        }
    }

    T front() const //O(1)
    {
        if( m_length > 0)
        {
            return list_entry(m_header.next, Node, head)->value;
        }
        else
        {
           THROW_EXCEPTION(InvalidOperationException,"No element in current queue...");
        }
    }

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

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

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

测试代码如下,在主函数中实现一个Test类,使用链式队列来存储这个类作为队列成员。

class Test : public Object
{
public:
    Test()
    {
      cout << "Test()" <<endl;
    }

    ~Test()
    {
        cout << "~Test()" <<endl;
    }
};


int main()
{
    LinkQueue<int> lq;
    LinkQueue<Test> lq1;
    StaticQueue<Test, 10> lq2;

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

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

        lq.remove();
    }

    return 0;
}
通过打印结果可以看出,顺序队列在实现自定义类类型数据元素存储时, 需要将每个对象的构造函数和析构函数都调用(原因参见《 二、单链表的实现和遍历,循环链表、双向循环链表的实现 》中的第二节——单链表的具体实现),效率不高。链式队列则没有这种效率上的缺点,直接将数据保存在队列中。


小结:StaticQueue在初始化的时候可能多次调用元素类型的构造函数;

LinkList的组合使用,可以实现链式队列的功能,但是效率不高;

LinkQueue的最终实现使用了Linux内核链表;

LinkQueue中入队和出队的操作可以在常量时间内完成。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值