【數據結構】24_單鏈表的遍歷與優化

問題:如何遍歷單鏈表中的每個元素?

當前單鏈表的遍歷方法

LinkList<int> list;
for (int i=0; i<5; ++i)
{
    list.insert(0, i);
}
for (int i=0; i<list.length(); ++i)
{
    cout << list.get(i) << endl;
}
  • 遺憾的事實:node

    • 不能以線性的時間複雜度完成單鏈表的遍歷
  • 新的需求ios

    • 爲單鏈表提供新的方法,在線性時間內完成遍歷

設計思路(遊標)

  • 在單鏈表的內部定義一個遊標(Node *m_current)
  • 遍歷開始前將遊標指向位置爲0的數據元素
  • 獲取遊標指向的數據元素
  • 經過結點的 next 指針移動遊標
提供一組遍歷相關的函數,以線性的時間複雜度遍歷鏈表。
函數 功能說明
move() 將遊標定位到目標位置
next() 移動遊標
current() 獲取遊標所指向的數據元素
end() 遊標是否到達尾部(是否爲空)

遍歷函數原型設計

  • bool move(int i, int step = 1);
  • bool end();
  • T current();
  • bool next();

編程實驗:單鏈表的遍歷

文件: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;

        m_step        = 1;
        m_current     = nullptr;
    }

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

    bool insert(int i, const T &e) override  // O(n)
    {
        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  // O(n)
    {
        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  // O(n)
    {
        bool ret = ((0 <= i) && (i < m_length));

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

        return ret;
    }

    T get(int i) const  // O(n)
    {
        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  // O(n)
    {
        bool ret = ((0 <= i) && (i < m_length));

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

        return ret;
    }

    int  find(const T &e) override  // O(n)
    {
        int ret = -1;

        int i = 0;
        Node *node = m_header.next;
        while (node)
        {
            if (node->value == e)
            {
                ret = i;
                break;
            }
            else
            {
                node = node->next;
                ++i;
            }
        }

        return ret;
    }

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

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

            --m_length;
        }
    }

    bool move(int i, int step = 1)  // O(n)
    {
        bool ret = ((0 <= i) && (i < m_length) && (step > 0));

        if (ret)
        {
            m_current = position(i)->next;
            m_step = step;
        }

        return ret;
    }

    bool end()  // O(1)
    {
        return (m_current == nullptr);
    }

    T current()  // O(1)
    {
        if (!end())
        {
            return m_current->value;
        }
        else
        {
             THROW_EXCEPTION(InvalidOpertionExcetion, " No value at current posotion ...");
        }
    }

    bool next()  // O(n)
    {
        int i = 0;

        while ((i < m_step) && !end())
        {
            m_current = m_current->next;
            ++i;
        }

        return (i == m_step);
    }

    ~LinkList()  // O(n)
    {
        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;
    int m_step;
    Node *m_current;

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

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

        return ret;
    }
};

}

#endif // LINKLIST_H

文件:main.cppide

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

    for (list.move(0); !list.end(); list.next())
    {
        cout << list.current() << endl;
    }

    cout << "main end" << endl;

    return 0;
}

輸出:函數

main begin
4
3
2
1
0
main end

單鏈表內部的一次封裝

virtual Node *create()
{
    return new Node();
}

virtual void destory(Node *pn)
{
    delete pn;
}

編程實驗:內部的封裝

LinkList.hspa

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

        m_step        = 0;
        m_current     = nullptr;
    }

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

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

        if (ret)
        {
            Node *node = create();
            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  // O(n)
    {
        bool ret = ((0 <= i) && (i < m_length));

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

            Node *toDel = current->next;
            current->next = toDel->next;
            destroy(toDel);

            --m_length;

        }

        return ret;
    }

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

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

        return ret;
    }

    T get(int i) const  // O(n)
    {
        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  // O(n)
    {
        bool ret = ((0 <= i) && (i < m_length));

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

        return ret;
    }

    int  find(const T &e) override  // O(n)
    {
        int ret = -1;

        int i = 0;
        Node *node = m_header.next;
        while (node)
        {
            if (node->value == e)
            {
                ret = i;
                break;
            }
            else
            {
                node = node->next;
                ++i;
            }
        }

        return ret;
    }

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

    void clear()  // O(n)
    {
        while (m_header.next)
        {
            Node *toDel = m_header.next;
            m_header.next = toDel->next;
            destroy(toDel);

            --m_length;
        }
    }

    bool move(int i, int step = 1)  // O(n)
    {
        bool ret = ((0 <= i) && (i < m_length) && (step > 0));

        if (ret)
        {
            m_current = position(i)->next;
            m_step = step;
        }

        return ret;
    }

    bool end()  // O(1)
    {
        return (m_current == nullptr);
    }

    T current()  // O(1)
    {
        if (!end())
        {
            return m_current->value;
        }
        else
        {
             THROW_EXCEPTION(InvalidOpertionExcetion, " No value at current posotion ...");
        }
    }

    bool next()  // O(n)
    {
        int i = 0;

        while ((i < m_step) && !end())
        {
            m_current = m_current->next;
            ++i;
        }

        return (i == m_step);
    }

    ~LinkList()  // O(n)
    {
        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;
    int m_step;
    Node *m_current;

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

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

        return ret;
    }

    virtual Node *create()
    {
        return new Node();
    }

    virtual void destroy(Node *pn)
    {
        delete pn;
    }
};

}

#endif // LINKLIST_H
問題:封裝 create 和 destroy 函數的意義是什麼?

To be continued...設計

小結

  • 單鏈表的遍歷須要在線性時間內完成
  • 在單鏈表內部定義遊標變量,經過遊標變量提升效率
  • 遍歷相關的成員函數是相互依賴,相互配合的關係
  • 封裝結點的申請和刪除操做更有利於加強擴展性

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

相關文章
相關標籤/搜索