数据结构--循环链表与双向链表

一.循环链表

A.循环链表的介绍
a.概念上
1.任意数据元素都有一个前驱和一个后继
2.所有数据元素的关系构成一个逻辑上的环
b.实现上
1.循环链表是一种特殊的单链表
2.尾节点的指针域保存了首结点的地址
关系图如下

循环链表的继承层次结构

二.循环链表的实现思路

A.思路
1.通过模板定义CircleList类,继承自LinkList类
2.定义内部函数last_to_first();用于将单链表首尾相连

        Node* last()const//尾节点
        {
            return this->position(this->m_length-1)->next;//返回尾节点(m_length-1)
        }

        void last_to_first()const//将链表首尾相连
        {
            last()->next=this->m_header.next;//尾节点的next指针指向首节点
        }

        int mod(int i)const//取余的实现
        {
            return (this->m_length==0) ? 0 : ( i % this->m_length);
        }

3.特殊处理:首元素的插入操作和删除操作
4.重新实现:清空操作和遍历操作
B.实现要点
a.插入位置为0时
1.头结点和尾结点均指向新结点
2.新结点成为首节点插入链表

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

            i=i%(this->m_length+1);//i值取余

            ret=LinkList<T>::insert(i,e);//调用父类的insert来实现子类的insert

            if(ret&&(i==0))
            {
                last_to_first();
            }

            return ret;
        }

b.删除位置为0时
1.头结点和尾结点指向位置为1的结点
2.安全销毁首结点

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

            i= mod(i);

            if(i==0)
            {
                Node *toDel=this->m_header.next;

                if(toDel!=NULL)
                {
                    this->m_header.next=toDel->next;
                    this->m_length--;
                    //链表不为空
                    if(this->m_length>0)
                    {
                        last_to_first();

                        if(this->m_current==toDel)
                        {
                            this->m_current=toDel->next;
                        }
                    }
                    else
                    { //链表为空,置空
                        this->m_header.next=NULL;
                        this->m_current=NULL;
                    }

                    this->destroy(toDel);//在最后一步删除首节点  避免了异常安全
                }
                else
                {
                    ret=false;
                }
            }
            else
            {
                ret=LinkList<T>::remove(i);
            }

            return ret;
        }

循环链表的完整实现代码如下

#include "LinkList.h"

namespace MyLib
{
    template <typename T>
    class CircleList:public LinkList<T>
    {
    protected:
        typedef typename LinkList<T>::Node Node;
        Node* last()const//尾节点
        {
            return this->position(this->m_length-1)->next;//返回尾节点(m_length-1)
        }

        void last_to_first()const//将链表首尾相连
        {
            last()->next=this->m_header.next;//尾节点的next指针指向首节点
        }

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

    public:
        bool insert(const T& e)//重载
        {
            return insert(this->m_length,e);//调用重载的版本
        }

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

            i=i%(this->m_length+1);//i值取余

            ret=LinkList<T>::insert(i,e);//调用父类的insert来实现子类的insert

            if(ret&&(i==0))
            {
                last_to_first();
            }

            return ret;
        }

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

            i= mod(i);

            if(i==0)
            {
                Node *toDel=this->m_header.next;

                if(toDel!=NULL)
                {
                    this->m_header.next=toDel->next;
                    this->m_length--;
                    //链表不为空
                    if(this->m_length>0)
                    {
                        last_to_first();

                        if(this->m_current==toDel)
                        {
                            this->m_current=toDel->next;
                        }
                    }
                    else
                    { //链表为空,置空
                        this->m_header.next=NULL;
                        this->m_current=NULL;
                    }

                    this->destroy(toDel);//在最后一步删除首节点  避免了异常安全
                }
                else
                {
                    ret=false;
                }
            }
            else
            {
                ret=LinkList<T>::remove(i);
            }

            return ret;
        }

        bool set(int i, const T &e)
        {
            i=mod(i);
            return LinkList<T>::set(i,e);//调用父类函数
        }

        T get(int i)const
        {
            i=mod(i);
            return LinkList<T>::get(i);
        }

        T get(int i,const T&e) const
        {
            i=mod(i);
            return LinkList<T>::get(i,e);
        }

        int find(const T &e)const
        {
            int ret=-1;
            Node* slide=this->m_header.next;//指针slide指向首节点
            for(int i=0;i<this->m_length;i++)//slide指针遍历每个元素
            {
                if(slide->value==e)
                {
                    ret=i;
                    break;
                }
                slide=slide->next;
            }

            return ret;
        }

        void clear()
        {
            while(this->m_length>1)
            {
                remove(1);//这里取1的原因是效率更高
            }
            if(this->m_length==1)
            {
                Node* toDel=this->m_header.next;
                this->m_header.next=NULL;
                this->m_current=NULL;
                this->m_length=0;

                this->destroy(toDel);
            }
        }

        bool move(int i, int step)//i表示位置
        {
            i=mod(i);
            return LinkList<T>::move(i,step);
        }

        bool end()
        {
            return (this->m_length==0)||(this->m_current==NULL);
        }

        ~CircleList()//析构函数直接调用clear()函数
        {
            clear();
        }
    };
}

三.小结

1.循环链表是一种特殊的单链表
2.尾结点的指针域保存了首结点的地址
3.特殊处理首元素的插入操作和删除操作
4.重新实现清空操作和遍历操作

四.双向链表

由之前的单链表我们可以看到单链表存在的缺陷
1.单向性==>只能从头结点开始高效访问链表中的数据元素
2.缺陷==>如果需要逆向访问单链表中的数据元素将极其低效
新的线性表实现
设计思路:在单链表的结点中增加一个指针pre,用于指向当前结点的前驱结点
示意图

双向链表的继承层次结构

简单的图示来说明双向链表的插入和删除操作
插入操作

如图所示四个步骤完成操作
1.将插入结点的next指向next
2.current的next指向插入的结点
3.插入结点的pre指向curret
4.next的pre指向node
实现代码

bool insert(int i,const T&e)
{
    bool ret=((0<=i)&&(i<= m_length));
    if(ret)
    {
        Node* node=creat();

        if(node!=NULL)
        {
            Node* current=positon();
            Node* next=current->next;

            node->value=e;
            node->next=next;//步骤1
            current->next=node;//步骤2

            if(current!=reinterpret_cast<Node*>(&m_header))
            {
                node->pre=current;//步骤3
            }
            else
            {
                node->pre=NULL;
            }

            if(next!=NULL)
            {
                next-pre=node;
            }
            m_length++;
        }
        else
        {
            THROW_EXCEPTION(NoEoughMemoryException,"NoEoughMemory");
        }
    }
    return ret;
}

删除操作

删除部分3个步骤
1.将current的next指向next
2.将next的pre指向current
3.删除toDel
代码实现如下

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

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

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

                current->next=next;//步骤1
                if(next!=NULL)
                {
                    next->pre=toDel->pre;//步骤2
                }

                m_length--;

                destroy(toDel);//步骤3

                //m_length--;
            }

            return ret;
        }

双向链表的具体实现

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

namespace MyLib
{
    template <typename T>
    class DuaLinkList:public List<T>
    {
    protected:
        struct Node :public Object
        {
            T value;//数据域   保存数据的值
            Node* next;//指针域 指向后继节点的指针
            Node* pre;
        };

        mutable struct:public Object//没有类型名的结构
        {
            char reserved[sizeof(T)];
            Node* next;
            Node* pre;
        }  m_header;//头节点  辅助定位元素
        int m_length;
        int m_step;
        Node* m_current;

        Node* position(int i) const//程序优化
        {
            Node* ret=reinterpret_cast<Node*>(&m_header);//reinterpret_cast强制类型转换

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

    public:
        DuaLinkList()
        {
            m_header.next=NULL;
            m_header.pre=NULL;
            m_length=0;
            m_step=1;
            m_current=NULL;
        }

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

        bool insert(int i,const T&e)//i表示插入的位置,e表示插入的数据
        {
            bool ret=((0<=i)&&(i<= m_length));//m_length表示链表的长度

            if(ret)
            {
                Node* node=create();

                if(node!=NULL)
                {
                    Node* current=position(i);//定位位置
                    Node* next=current->next;//表示插入的节点的后继节点

                    node->value=e;
                    node->next=next;
                    current->next=node;
                    if(current!=reinterpret_cast<Node*>(&m_header))
                    {
                        node->pre=current;
                    }
                    else
                    {
                        node->pre=NULL;
                    }

                    if(next!=NULL)
                    {
                        next->pre=node;
                    }

                    m_length++;
                }
                else
                {
                    THROW_EXCEPTION(NoEoughMemoryException,"No ...");
                }
            }

            return ret;
        }

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

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

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

                current->next=next;
                if(next!=NULL)
                {
                    next->pre=toDel->pre;
                }

                m_length--;

                destroy(toDel);

                //m_length--;
            }

            return ret;
        }

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

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

             return ret;
        }

        int find(const T&e) const
        {
            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;
        }

        virtual T get(int i)const
        {
            T ret;

            if(get(i,ret))
            {
                return ret;
            }
            else
            {
                THROW_EXCEPTION(indexOutOfBoundsException,"...");
            }

            return ret;
        }

        bool get(int i,T&e)const
        {
            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_length>0)
            {
                remove(0);
            }
        }

        virtual bool move(int i,int step=-1)
        {
            bool ret= (0<=i)&&(i<m_length)&&(step>0);

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

            return ret;
        }

        virtual bool end()
        {
            return (m_current==NULL);
        }

        virtual T current()
        {
            if(!end())
            {
                return m_current->value;
            }
            else
            {
                THROW_EXCEPTION(InvalidOperationException,"...");
            }
        }

        virtual bool next()
        {
            int i=0;

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

        virtual bool pre()
        {
            int i=0;

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

        ~DuaLinkList()
        {
            clear();
        }
    };
}

小结

1.双向链表是为了弥补单链表的缺陷而重新设计的
2.在概念上,双向链表不是单链表,没有继承关系
3.双向链表中的游标能够直接访问当前结点的前驱和后继
4.双向链表是线性表概念的最终实现

原文地址:http://blog.51cto.com/13475106/2345297

时间: 2024-10-10 07:13:08

数据结构--循环链表与双向链表的相关文章

数据结构与算法--线性表系列(循环链表、双向链表)

hello,everybody,今天我们来学习线性表的最后两种形式,循环链表.双向链表.这两种链表,是链式存储结构的不同形式.书归正传,我们先来看看循环链表吧. 大家思考一个问题,我们把线性表各个元素比作下图的路线图上的城市: 我们的线性表各个结点的指针,都是指向唯一的后继结点,线性表的终端结点的指针为空.这样的话,如果我们在南京,我们需要先访问南京右j边的城市,再访问南京左边的城市.根据线性表的结构,我们只能返回上海,从上海依次访问到北京.因为我们的终端结点的指针为空,如果直接访问南京右边的城

[读书笔记]-大话数据结构-3-线性表(三)-静态链表、循环链表和双向链表

静态链表 对于没有指针的编程语言,可以用数组替代指针,来描述链表.让数组的每个元素由data和cur两部分组成,其中cur相当于链表的next指针,这种用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法.我们对数组的第一个和最后一个元素做特殊处理,不存数据.让数组的第一个元素cur存放第一个备用元素(未被占用的元素)下标,而数组的最后一个元素cur存放第一个有值的元素下标,相当于头结点作用.空的静态链表如下图 当存放入一些数据时("甲""乙""丁&q

数据结构开发(9):循环链表与双向链表

0.目录 1.循环链表的实现 2.双向链表的实现 3.小结 1.循环链表的实现 什么是循环链表? 概念上 任意数据元素都有一个前驱和一个后继 所有的数据元素的关系构成一个逻辑上的环 实现上 循环链表是一种特殊的单链表 尾结点的指针域保存了首结点的地址 循环链表的逻辑构成: 循环链表的继承层次结构: 循环链表的实现思路: 通过模板定义CircleList类,继承自LinkList类 定义内部函数 last_to_first(),用于将单链表首尾相连 特殊处理:首元素的插入操作和删除操作 重新实现:

单链表,循环链表,双向链表(C++实现)

首先是单链表(带附加表头),实现类代码如下: 1 template<class T> 2 struct LinkNode{//链表节点 3 T data; 4 LinkNode *link; 5 LinkNode(const T& args,LinkNode<T> *ptr=NULL){ 6 data=args; 7 link=ptr; 8 } 9 }; 10 11 template<class T> 12 class List{//带附加头节点的单链表 13

Linux 内核数据结构:Linux 双向链表

Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为广泛的数据结构,具体你可以 查看 这里. 首先让我们看一下主要的结构体: struct list_head { struct list_head *next, *prev; }; 你可以看到其与常见的结构体实现有显著不同,比如 glib 中所使用到的双向链表实现. struct GList { gp

14. C#数据结构与算法 -- 双向链表

首先,明白什么是双向链表.所谓双向链表是如果希望找直接前驱结点和直接后继结点的时间复杂度都是 O(1),那么,需要在结点中设两个引用域,一个保存直接前驱结点的地址,叫 prev,一个直接后继结点的地址,叫 next,这样的链表就是双向链表(Doubly Linked List).双向链表的结点结构示意图如图所示. 双向链表结点的定义与单链表的结点的定义很相似, ,只是双向链表多了一个字段 prev.其实,双向链表更像是一根链条一样,你连我,我连你,不清楚,请看图. 双向链表结点类的实现如下所示

数据结构 链表_双向链表的实现与分析

双向链表的实现与分析 双向链表的组成 :1.数据成员:2.指向下一个元素的next指针:3.指向前一个元素的prev指针. 数据结构DListElmt:代表双向链表中的单个元素(节点). 数据结构DList:代表双向链表数据结构,该结构的成员同前面介绍的单链表相似. 示例1:双向链表抽象数据类型的头文件 /*dlist.h*/ #ifndef DLIST_H #define DLIST_H /*定义双向链表中的元素*/ typedef struct DListLemt_ { void *data

静态链表、循环链表、双向链表(C代码实现)

静态链表 对于没有指针的编程语言,可以用数组替代指针,来描述链表.让数组的每个元素由data和cur两部分组成,其中cur相当于链表的next指针,这种用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法.我们对数组的第一个和最后一个元素做特殊处理,不存数据.让数组的第一个元素cur存放第一个备用元素(第一个未被占用的元素)下标,而数组的最后一个元素cur存放第一个有值的元素下标,相当于头结点作用.空的静态链表如下图 当存放入一些数据时("甲""乙""

数据结构&mdash;&mdash;循环链表

1.循环链表的定义循环链表是一种首尾相连的链表.特点是无需增加存储量,仅对表的链接方式修改使表的处理灵活方便.空循环链表仅由一个自成循环的头结点表示. 2.单向循环链表 在单向链表中,头指针是相当重要的,因为单向链表的操作都需要头指针,所以如果头指针丢失或者破坏,那么整个链表都会遗失,并且浪费链表内存空间. 单向循环链表的构成:如果把单链表的最后一个节点的指针指向链表头部,而不是指向NULL,那么就构成了一个单向循环链表. 3.双向循环链表 在单链表L中,查找ai的后继 Next(L, ai),