课程目标

  • 使用 Linux 内核链表实现 DTLib 中的双向循环链表
  • template <typename T> class DualCircleList;

image.png

DTLib 中双向链表的设计思路

数据结点之间在逻辑上构成双向循环链表,头结点仅用于结点定位

image.png

实现思路

  • 通过模板定义 DualCircleList类,继承自 DualLinkList 类
  • 在 DualCircleList 内部使用 Linux 内核链表实现
  • 使用 struct list_head 定义 DualCircleList 的头结点
  • 特殊处理:循环遍历时忽略头结点

实现要点

  • 通过 list_head 进行目标结点定位 (posotion(i))
  • 通过 list_entry 将 list_head 指针转换为目标结点指针
  • 通过 list_for_each 实现 int find(const T &e) 函数
  • 遍历函数中的 next() 和 pre () 需要考虑跳过头结点

编程实验:基于 Linux 内核链表的双向循环链表

DualCircleList.h

#ifndef DUALCIRCLELIST_LL_H
#define DUALCIRCLELIST_LL_H

#include "LinuxList.h"
#include "DualLinkList.h"

namespace DTLib
{

template <typename T>
class DualCircleList_LL : public DualLinkList<T>
{
public:
    DualCircleList_LL()
    {
        this->m_length = 0;
        this->m_step = 1;
        m_current = nullptr;

        INIT_LIST_HEAD(&m_header);
    }

    bool insert(const T &e)
    {
        return insert(this->m_length, e);
    }

    bool insert(int i, const T &e) override
    {
        bool ret = true;
        Node *node = new Node();

        i = i % (this->m_length + 1);

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

            list_add_tail(&node->head, position(i)->next);

            ++this->m_length;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No enough memeory to create node object ...");
        }

        return ret;
    }

    bool remove(int i) override
    {
        bool ret = true;

        i = mod(i);

        ret = ((0 <= i) && (i < this->m_length));

        if (ret)
        {
            list_head *toDel = position(i)->next;

            if (toDel == m_current)
            {
                m_current = toDel->next;
            }

            list_del(toDel);

            --this->m_length;

            delete list_entry(toDel, Node, head);
        }

        return ret;
    }

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

        i = mod(i);

        ret = ((0 <= i) && (i < this->m_length));

        if (ret)
        {
           list_entry(position(i)->next, Node, head)->value = e;
        }

        return ret;
    }

    T get(int i) const
    {
        T ret;

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

        return ret;
    }

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

        i = mod(i);

        ret = ((0 <= i) && (i < this->m_length));

        if (ret)
        {
           e = list_entry(position(i)->next, Node, head)->value;
        }

        return ret;
    }

    int  find(const T &e) const override
    {
        int ret = -1;
        int i = 0;
        list_head *slider = nullptr;

        list_for_each(slider, &m_header)
        {
            if (list_entry(slider, Node, head)->value == e)
            {
                ret = i;
                break;
            }

            ++i;
        }

        return ret;
    }

    int length() const override
    {
        return this->m_length;
    }

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

        m_current = nullptr;
    }

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

        i = mod(i);

        ret = ret && ((0 <= i) && (i < this->m_length));

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

        return ret;
    }

    bool end() override // O(1)
    {
        return ((m_current == nullptr) || (this->m_length == 0));
    }

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

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

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

        if (m_current == &m_header)
        {
            m_current = m_current->next;
        }

        return (i == this->m_step);
    }

    bool pre() override
    {
        int i = 0;

        while ((i < this->m_step) && !end())
        {
            if (m_current != &m_header)
            {
                m_current = m_current->prev;
                ++i;
            }
            else
            {
                m_current = m_current->prev;
            }
        }

        if (m_current == &m_header)
        {
            m_current = m_current->prev;
        }

        return (i == this->m_step);
    }

    ~DualCircleList_LL()
    {
        clear();
    }

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

    list_head m_header;
    list_head *m_current;

    list_head *position(int i) const
    {
        list_head *ret = const_cast<list_head*>(&m_header);

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

        return ret;
    }

    int mod(int i) const
    {
        return (this->m_length == 0) ? 0 : (i % this->m_length);
    }
};

}

#endif // DUALCIRCLELIST_LL_H

文件:main.cpp

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

using namespace std;
using namespace DTLib;


int main()
{
    DualCircleList_LL<int> dl;

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

    cout << "begin" << endl;

    dl.move(dl.length() -1);

    while (dl.find(5) != -1)
    {
        if (dl.current() == 5)
        {
            cout << dl.current() << endl;

            dl.remove(dl.find(dl.current()));
        }
        else
        {
            dl.pre();
        }
    }

    cout << "end" << endl;

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

    return 0;
}

输出:

begin
5
5
5
5
5
end
4
3
2
1
0

小结

  • Linux 内核链表是带头结点的双向循环链表
  • DualCircleList 使用 Linux 内核链表进行内部实现
  • DualCircleList 在循环遍历时需要跳过头结点
  • 将 list_head 指针转换为目标结点指针时,使用 list_entry 宏

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


TianSong
737 声望139 粉丝

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