第33课 - 双向循环链表的实现

1、双向循环链表的实现

        课程目标

                - 使用Linux内核链表实现DTLib中的双向循环链表 

                - template < typename T > class DualCircleList

    

                


            DTLib中双向循环链表的设计思路 

                        数据结点之间在逻辑上构成双向循环 

                        链表,头结点仅用于结点的定位。 




        实现思路

            -通过模板定义DualCircleList类,维承自DualLinkList类 

            -在DualCircleList内部使用Linux内核链表进行实现 

            -使用struct list_head定义DualCircleList的头结点 

            - 特殊处理:循环遍历时忽略头结点 


        实现要点 

            -通过list_head进行目标结点定位( position(i) ) 

            -通过list_entry将list_head指针转换为目标结点指针 

            -通过list_for_each实现int find(const T& e)函数

            -遍历函数中的 next() pre() 需要考虑跳过头结点


2、编程实验 

基于Linux内核链表的双向循环链表     DualCircleList.h

#ifndef DUALCIRCLELIST_H
#define DUALCIRCLELIST_H

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

namespace DTLib
{

template <typename T>
class DualCircleList : public DualLinkList<T>
{
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); //const成员函数内对成员变量取地址

        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);
    }
public:
    DualCircleList()
    {
        this->m_length = 0;
        this->m_step = 1;

        m_current = NULL;

        INIT_LIST_HEAD(&m_header);
    }
    bool insert(const T& e)
    {
        return insert(this->m_length,e);
    }
    bool insert(int i,const T& e)
    {
        bool ret = true;
        Node* node = new Node();

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

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

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

            this->m_length++;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException,"No memory to create a new element ...");
        }

        return ret;
    }
    bool remove(int i)
    {
        bool ret = true;

        i = mod(i);

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

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

            if(m_current == toDel)
            {
                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)
    {
        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))
        {
            return ret;
        }
        else
        {
            THROW_EXCEPTION(IndexOutOfBoundsExpception,"Invalid parameter i to get element ...");
        }

        return ret;
    }
    bool get(int i,T& e) const
    {
        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
    {
        int ret = -1;
        int i =0;
        list_head* slider = NULL;

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

            i++;
        }

        return ret;
    }

    int length() const
    {
        return this->m_length;
    }
    void clear()
    {
        while(this->m_length > 0)
        {
            remove(0);
        }
    }

    bool move(int i, int step=1)
    {
        bool ret = (step > 0);

        i = mod(i);

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

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

        return ret;
    }
    bool end()
    {
        return ( m_current == NULL)||(this->m_length == 0);
    }
    T current()
    {
        if(!end())
        {
            return list_entry(m_current,Node,head)->value;
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException,"No value at current position ...");
        }
    }
    bool next()
    {
        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()
    {
        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()
    {
        clear();
    }

};
}

#endif // DUALCIRCLELIST_H

main.cpp

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

using namespace std;
using namespace DTLib;


int main()
{
    DualCircleList<int> d1;

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

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

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

    while(d1.find(5) != -1)
    {
        if(d1.current() == 5)
        {
            cout<<d1.current()<<" ";
            d1.remove(d1.find(d1.current()));
        }
        else
        {
            d1.pre();
        }
    }

    cout<<endl;
    for(int i=0;i<10;i++) 
    {
        cout<<d1.get(i)<<" ";
    }
    cout<<endl;


    int i =0;
    for(d1.move(d1.length()-1);(i<d1.length())&&(!d1.end());d1.pre(),i++)
    {
        cout<<d1.current()<<" ";
    }

    return 0;
}

                        



3、小结 

            Linux内核链表是带头结点的双向循环链表 

            DualCircleList使用Linux内核链表进行内部实现 

            DualCircleList在循环遍历时需要跳过头结点 

            将 list_head指针转换为目标结点指针时, 使用list_entry宏 


4、思考题

            下面代码中的pn1和pn2是否相等?为什么? 

        




猜你喜欢

转载自blog.csdn.net/qq_39654127/article/details/80316274