顺序队列的问题

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

文件:main.cpp

#include <iostream>
#include "StaticQueue.h"

using namespace std;
using namespace DTLib;

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

int main()
{
    StaticQueue<Test, 5> queue;

    cout << "queue.length() = " << queue.length() << endl;

    return 0;
}

输出:

Test()
Test()
Test()
Test()
Test()
queue.length() = 0
~Test()
~Test()
~Test()
~Test()
~Test()

队列的链式存储实现

image.png

链式队列的设计要点

  • 类模板,抽象父类 Queue 的直接子类
  • 在内部使用链式结构实现元素的存储
  • 只在链表的头部和尾部进行操作

image.png

编程实验:基于 LinkList 的队列

文件:LinkQueue.h

#ifndef LINKQUEUE_H
#define LINKQUEUE_H

#include "Queue.h"
#include "LinkList.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class LinkQueue : public Queue<T>
{
public:
    LinkQueue() = default;

    void add(const T &e) override  // O(n)
    {
        m_list.insert(e);
    }

    void remove() override  // O(1)
    {
        if (m_list.length() > 0)
        {
            m_list.remove(0);
        }
        else
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "No element in current StaticQueue ...");
        }
    }

    T front() const override  // O(1)
    {
        if (m_list.length() > 0)
        {
           return m_list.get(0);
        }
        else
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "No element in current StaticQueue ...");
        }
    }

    void clear() override  // O(n)
    {
        m_list.clear();
    }

    int length() const override  // O(1)
    {
        return m_list.length();
    }

    ~LinkQueue()  // O(n)
    {
        clear();
    }

protected:
    LinkList<T> m_list;
};

}

#endif // LINKQUEUE_H

文件:main.cpp

#include <iostream>
#include "LinkQueue.h"

using namespace std;
using namespace DTLib;

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

int main()
{
    LinkQueue<Test> queue_t;

    cout << "-----" << endl;

    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;
}

输出:

-----
0
1
2
3
4

问题:使用 LinkList 类实现链表式队列是否合适?是否有更好的方案?
void add(const T &e) override  // O(n)
{
    m_list.insert(e);
}

每次插入新元素,都需要遍历整个链表到尾部!!

链式存储实现的优化

image.png

image.png

编程实验:基于 Linux 内核链表的队列

文件:LinkQueue.h

#ifndef LINKQUEUE_H
#define LINKQUEUE_H

#include "Queue.h"
#include "LinuxList.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class LinkQueue : public Queue<T>
{
public:
    LinkQueue()
    {
       m_length = 0;

       INIT_LIST_HEAD(&m_header);
    }

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

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

            list_add_tail(&node->head, &m_header);

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

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

            list_del(toDel);

            --m_length;

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

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

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

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

    ~LinkQueue()  // O(n)
    {
        clear();
    }

protected:
    struct Node : public Object
    {
        list_head head;
        T value;
    };

    list_head m_header;
    int m_length;
};

}

#endif // LINKQUEUE_H

文件:main.cpp

#include <iostream>
#include "LinkQueue.h"

using namespace std;
using namespace DTLib;

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

int main()
{
    LinkQueue<Test> queue_t;

    cout << "-----" << endl;

    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;
}

输出:

-----
0
1
2
3
4

小结

  • SaticQueue 在初始化时可能多次调用元素类型的构造函数
  • LinkList 的组合使用实现了队列的功能,但是不够高效
  • LinkQueue 的最终实现组合使用了 Linux 内核链表
  • LinkQueue 中入队和出队操作可以在常量时间内完成

以上内容整理于狄泰软件学院系列课程,请大家保护原创!


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧