【數據結構】22_單鏈表的具體實現

課程目標

完成鏈式存儲結構線性表的實現

image.png

LinkList 設計要點

  • 類模板,經過頭節點訪問後繼節點
  • 定義內部節點類型 Node,用於描述數據域和指針域
  • 實現線性表的關鍵操做(增,刪,查,等)

LinkList 的定義

template <typename T>
class LinkList : public List<T>
{
public:
    LinkList();
    // ...
protected:
    struct Node : public Object
    {
        T value;
        Node *next;
    };
    
    Node m_header;
    int m_length;
};

編程實驗:鏈表的實現

文件:LinkList.hnode

#ifndef LINKLIST_H
#define LINKLIST_H

#include "List.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class LinkList : public List<T>
{
public:
    LinkList()
    {
        m_header.next = nullptr;
        m_length      = 0;
    }

    bool insert(const T &e) override
    {
        return insert(m_length, e);
    }

    bool insert(int i, const T &e) override
    {
        bool ret = ((0 <= i) && (i <= m_length));

        if (ret)
        {
            Node *node = new Node();
            if (node != nullptr)
            {
                Node *current = &m_header;

                for (int p=0; p<i; ++p)
                {
                    current = current->next;
                }

                node->value = e;
                node->next = current->next;
                current->next = node;

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

        return ret;
    }

    bool remove(int i) override
    {
        bool ret = ((0 <= i) && (i < m_length));

        if (ret)
        {
            Node *current = &m_header;

            for (int p=0; p<i; ++p)
            {
                current = current->next;
            }

            Node *toDel = current->next;
            current->next = toDel->next;
            delete toDel;

            --m_length;

        }

        return ret;
    }

    bool set(int i, const T &e) override
    {
        bool ret = ((0 <= i) && (i < m_length));

        if (ret)
        {
            Node *current = &m_header;

            for (int p=0; p<i; ++p)
            {
                current = current->next;
            }

            current->next->value = e;
        }

        return ret;
    }

    T get(int i) const
    {
        T ret;

        if (!get(i, ret))
        {
            THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
        }

        return ret;
    }

    bool get(int i, T &e) const override
    {
        bool ret = ((0 <= i) && (i < m_length));

        if (ret)
        {
            Node *current = &m_header;

            for (int p=0; p<i; ++p)
            {
                current = current->next;
            }

            e = current->next->value;
        }

        return ret;
    }

    int length() const
    {
        return m_length;
    }

    void clear()
    {
        while (m_header.next)
        {
            Node *toDel = m_header.next;
            m_header.next = toDel->next;
            delete toDel;

            --m_length;
        }
    }

    ~LinkList()
    {
        clear();
    }

protected:
    struct Node : public Object
    {
        T value;
        Node *next;
    };

    mutable Node m_header;
    int m_length;
};

}

#endif // LINKLIST_H

文件:main.cppios

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

using namespace std;
using namespace DTLib;

int main()
{
    cout << "main begin" << endl;

    LinkList<int> list;

    for (int i=0; i<5; ++i)
    {
        list.insert(0, i);
        list.set(0, i*i);
    }

    for (int i=0; i<list.length(); ++i)
    {
        cout << list.get(i) << endl;
    }

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

    list.remove(2);

    for (int i=0; i<list.length(); ++i)
    {
        cout << list.get(i) << endl;
    }

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

    list.clear();

    for (int i=0; i<list.length(); ++i)
    {
        cout << list.get(i) << endl;
    }

    cout << "main end" << endl;

    return 0;
}

輸出:編程

main begin
16
9
4
1
0
------------
16
9
1
0
------------
main end

問題

頭節點是否存在隱患?
實現代碼是否須要優化?ide

代碼優化

insert, remove, get, set 等操做都設計元素定位
Node *current = &m_header;

for (int p=0; p<i; ++p)
{
    current = current->next;
}

==>函數

Node *position(int i);

頭節點的隱患

struct Node : public Object
{
    T value;
    Node *next;
};

==>佈局

class Test
{
public:
    Test()
    {
        throw 0;
    }
};

會發生什麼呢?測試

int main()
{
    cout << "main begin" << endl;
    
    LinkList<Test> list;
    
    cout << "main end" << endl;
}

輸出:優化

main begin
terminate called after throwing an instance of 'int'

使用者的疑問:
儘管 Test 類對象會拋出異常,但是我沒有定義 Test 對象,只是定義了 LinkList<Test> 對象,爲何會有異常被拋出呢?編碼

代碼分析:
=> LinkList<Test> list; 觸發成員對象的構造函數調用;
=> Node m_header; 觸發成員對象的構造函數被調用;
=> Test (泛指類型) 構造函數被調用,異常拋出。spa

解決方案:
避免在構造頭節點 m_header 時對泛型對象 T 的構造,而又保證內存佈局與Node類對象相同。
==》
頭節點類型重定義。

struct Node : public Object
{
    T value;
    Node *next;
};

mutable struct : public Object
{
    char reserved[sizeof (T)];  // 僅佔位用
    Node *next;
}m_header;

編程實驗:鏈表的優化

文件:LinkList.h

#ifndef LINKLIST_H
#define LINKLIST_H

#include "List.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class LinkList : public List<T>
{
public:
    LinkList()
    {
        m_header.next = nullptr;
        m_length      = 0;
    }

    bool insert(const T &e) override
    {
        return insert(m_length, e);
    }

    bool insert(int i, const T &e) override
    {
        bool ret = ((0 <= i) && (i <= m_length));

        if (ret)
        {
            Node *node = new Node();
            if (node != nullptr)
            {
                Node *current = position(i);

                node->value = e;
                node->next = current->next;
                current->next = node;

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

        return ret;
    }

    bool remove(int i) override
    {
        bool ret = ((0 <= i) && (i < m_length));

        if (ret)
        {
            Node *current = position(i);

            Node *toDel = current->next;
            current->next = toDel->next;
            delete toDel;

            --m_length;

        }

        return ret;
    }

    bool set(int i, const T &e) override
    {
        bool ret = ((0 <= i) && (i < m_length));

        if (ret)
        {
            position(i)->next->value = e;
        }

        return ret;
    }

    T get(int i) const
    {
        T ret;

        if (!get(i, ret))
        {
            THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
        }

        return ret;
    }

    bool get(int i, T &e) const override
    {
        bool ret = ((0 <= i) && (i < m_length));

        if (ret)
        {
            e = position(i)->next->value;
        }

        return ret;
    }

    int length() const
    {
        return m_length;
    }

    void clear()
    {
        while (m_header.next)
        {
            Node *toDel = m_header.next;
            m_header.next = toDel->next;
            delete toDel;

            --m_length;
        }
    }

    ~LinkList()
    {
        clear();
    }

protected:
    struct Node : public Object
    {
        T value;
        Node *next;
    };

    mutable struct : public Object
    {
        char reserved[sizeof (T)];
        Node *next;
    }m_header;

    int m_length;

    Node *position(int i) const
    {
        Node *ret = reinterpret_cast<Node*>(&m_header);

        for (int p=0; p<i; ++p)
        {
            ret = ret->next;
        }

        return ret;
    }
};

}

#endif // LINKLIST_H

文件:main.cpp

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

using namespace std;
using namespace DTLib;

class Test
{
public:
    Test()
    {
        throw 0;
    }
};

int main()
{
    cout << "main begin" << endl;

    LinkList<Test> list;

    cout << "main end" << endl;

    return 0;
}

輸出:

main begin
main end

小結

  • 經過類模板實現鏈表,包含頭節點成員和長度成員
  • 定義節點類型,並經過堆中的節點對象構造鏈式存儲
  • 爲了不構造錯誤的隱患,頭節點類型須要重定義
  • 代碼優化是編碼完成後必不可少的環節

提示:只要代碼發生改動,就須要從新測試

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

相關文章
相關標籤/搜索