【數據結構】37_隊列的概念及實現 (下)

順序隊列的問題

當數據元素爲類類型,StaticQueue 的對象在建立時會屢次調用元素類型的構造函數,影響效率!node

文件:main.cppios

#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.hide

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

每次插入新元素,都須要遍歷整個鏈表到尾部!!spa

鏈式存儲實現的優化

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.cppcode

#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 中入隊和出隊操做能夠在常量時間內完成

以上內容整理於狄泰軟件學院系列課程,請你們保護原創!

相關文章
相關標籤/搜索