双向链表详解

https://blog.csdn.net/cainv89/article/details/51301631

https://www.2cto.com/kf/201608/540936.html


1. 双向链表的概念


1.1 双向链表的定义
双向链表又称为双链表,是链表的一种。


1.2 双向链表的结点结构
双向链表的结点包括三个部分:前驱指针域、数据域和后继指针域。 
(1)前驱指针域(lLink),又称为左链指针,用于存放一个指针,该指针指向上一个结点的开始存储地址。 
(2)数据域(data),用于存储该结点的数据元素,数据元素类型由应用问题决定。 
(3)后继指针域(rLink),又称为右链指针,用于存放一个指针,该指针指向下一个结点的开始存储地址。
双向链表的结点结构示意图: 

 

1.3 双向链表中各结点的链接方式
双向链表常采用带附加头结点的循环链表的链接方式。
一个双向链表有一个附加头结点,由链表的头指针first指示,它的data域或者不放数据,或者存放一个特殊要求的数据,它的lLink指向链表的尾结点(即最后一个结点),它的rLink指向链表的首元结点(即第一个结点)。
双向链表的首元结点的左链指针lLink和尾结点的右链指针rLink都指向附加头结点first。
从双向链表中的任意一个结点开始,就可以直接访问它的前驱结点和后继结点。
双向链表(带附加头结点的)中各结点的链接方式示意图示意图: 

2. 双向链表的实现

/*dlist.h*/
 
    #ifndef DList_H  
    #define DList_H  
    typedef  int Item;  
    typedef struct Node * PNode;  //节点指针
    typedef PNode Position;  //节点位置
    /*定义节点类型*/ 
    typedef struct Node  
    {  
        Item data;      /*数据域*/ 
        PNode previous; /*指向前驱*/ 
        PNode next;     /*指向后继*/ 
    }Node;  
    /*定义链表类型*/ 
    typedef struct  
    {  
        PNode head;     /*指向头节点*/ 
        PNode tail;     /*指向尾节点*/ 
        int size; //链表长度
    }DList;  
 
    /*分配值为i的节点,并返回节点地址*/ 
    Position MakeNode(Item i);  
 
    /*释放p所指的节点*/ 
    void FreeNode(PNode p);  
 
    /*构造一个空的双向链表*/ 
    DList* InitList();  
 
    /*删除一个双向链表*/ 
    void DestroyList(DList *plist);  
 
    /*将一个链表置为空表,释放原链表节点空间*/ 
    void ClearList(DList *plist);  
 
    /*返回头节点地址*/ 
    Position GetHead(DList *plist);  
 
    /*返回尾节点地址*/ 
    Position GetTail(DList *plist);  
 
    /*返回链表大小*/ 
    int GetSize(DList *plist);  
 
    /*返回p的直接后继位置*/ 
    Position GetNext(Position p);  
 
    /*返回p的直接前驱位置*/ 
    Position GetPrevious(Position p);  
 
    /*将pnode所指节点插入第一个节点之前*/ 
    PNode InsFirst(DList *plist,PNode pnode);  
 
    /*将链表第一个节点删除并返回其地址*/ 
    PNode DelFirst(DList *plist);  
 
    /*获得节点的数据项*/ 
    Item GetItem(Position p);  
 
    /*设置节点的数据项*/ 
    void SetItem(Position p,Item i);  
 
    /*删除链表中的尾节点并返回其地址,改变链表的尾指针指向新的尾节点*/ 
    PNode Remove(DList *plist);  
 
    /*在链表中p位置之前插入新节点S*/ 
    PNode InsBefore(DList *plist,Position p,PNode s);  
 
    /*在链表中p位置之后插入新节点s*/ 
    PNode InsAfter(DList *plist,Position p,PNode s);  
 
    /*返回在链表中第i个节点的位置*/ 
    PNode LocatePos(DList *plist,int i);  
 
    /*依次对链表中每个元素调用函数visit()*/ 
    void ListTraverse(DList *plist,void (*visit)());
    /*打印data*/
    void print(Item data);   
    #endif  
 
 
 
/*dlist.c*/
 
    #include"dlist.h" 
    #include<malloc.h>  
    #include<stdlib.h>  
    /*分配值为i的节点,并返回节点地址*/ 
    Position MakeNode(Item i)  //创建节点,并设置数据i
    {  
        PNode p = NULL;   //定义一个节点p指向空
        p = (PNode)malloc(sizeof(Node));  //为该节点分配Node结构体大小的空间
        if(p!=NULL) //分配成功后
        {  
            p->data = i;  //设置数据域为i
            p->previous = NULL;//节点p前继指针指向空  
            p->next = NULL; //节点p后继指针指向空
        }     
        return p;  //返回创建好的p节点指针地址
    }  
    /*释放p所指的节点*/ 
    void FreeNode(PNode p)  
    {  
         free(p);  
    }  
    /*构造一个空的双向链表*/ 
    DList * InitList()  
    {  
        DList *plist = (DList *)malloc(sizeof(DList));//为新的双向链表分配DList结构体大小的空间  
        PNode head = MakeNode(0);  //调用上面的MakeNode()函数创建数据域为0的头指针指向头节点
        if(plist!=NULL)//成功分配新链表空间后  
        {  
            if(head!=NULL) //头指针不为空
            {  
                plist->head = head;//新链表的头指针指向头节点
                plist->tail = head; //先链表的尾指针也指向头节点
                plist->size = 0; //链表长度为0
            }  
            else 
                return NULL; //头指针创建失败
        }  
        return plist; //返回创建好的空链表
    }  
 
    /*删除一个双向链表*/ 
    void DestroyList(DList *plist)  
    {  
        ClearList(plist); //因为 DList *plist = (DList *)malloc(sizeof(DList))为链表分配了空间,所以必须调用清空链表函数,释放链表节点的空间
        free(GetHead(plist)); //因为 PNode head = MakeNode(0)为头节点分配了空间,所以必须调用GetHead()函数获取当前链表的头节点地址然后进行释放
        free(plist);  
    }  
 
    /*判断链表是否为空表*/ 
    int IsEmpty(DList *plist)  
    {  
        if(GetSize(plist)==0&&GetTail(plist)==GetHead(plist)) //如果调用 GetSize()函数获取的链表大小为0并且调用GetTail函数获取的尾节点和调用
                                  //GetHead函数获取的头节点指向同一个地址,那么就是空表
            return 1;  
        else 
            return 0;  
    }  
    /*将一个链表置为空表,释放原链表节点空间*/ 
    void ClearList(DList *plist) //清空链表
    {  
        PNode temp,p;  
        p = GetTail(plist); //将节点指针p指向尾指针指向的节点地址
        while(!IsEmpty(plist))  //如果该链表不为空
        {     
            temp = GetPrevious(p); //将节点指针temp指向p指针的前继指针所指向的地址
            free(p); //将 p指针指向的尾节点释放
            p = temp; //p指针重新指向刚才释放的尾节点的前一个节点
            plist->tail = temp; //尾指针重新指向刚才释放的尾节点的前一个节点
            plist->size--;  //从后往前一个一个释放节点空间,释放一个链表大小就减1
        }  
    }  
 
    /*返回头节点地址*/ 
    Position GetHead(DList *plist)  
    {  
        return plist->head;  
    }  
 
    /*返回尾节点地址*/ 
    Position GetTail(DList *plist)  
    {  
        return plist->tail;  
    }  
 
    /*返回链表大小*/ 
    int GetSize(DList *plist)  
    {  
        return plist->size;  
    }  
 
    /*返回p的直接后继位置*/ 
    Position GetNext(Position p)  
    {  
        return p->next;  
    }  
 
    /*返回p的直接前驱位置*/ 
    Position GetPrevious(Position p)  
    {  
        return p->previous;  
    }  
 
    /*在第一个节点前插入新节点pnode*/ 
    PNode InsFirst(DList *plist,PNode pnode)  
    {  
        Position head = GetHead(plist);//获取头节点位置
 
        if(IsEmpty(plist))  //若链表为空,链表的尾指针就指向pnode节点,直接插入新节点
            plist->tail = pnode;  
        plist->size++;//链表大小加1,给新加的pnode节点  
          pnode->next = head->next; //新加的pnode节点的后继指针指向头节点指向的第一个节点  
        pnode->previous = head;//新加的pnode节点的前继指针指向头节点  
          if(head->next!=NULL)  //如果第一个节点不为空
            head->next->previous = pnode;  //第一个节点的前继指针就指向新加的pnode节点
        head->next = pnode; //头节点的后继指针指向新加的pnode节点
          return pnode;   
    }  
 
    /*删除第一个节点,返回该节点的地址*/ 
    PNode DelFirst(DList *plist)  
    {  
        Position head = GetHead(plist);//获取头节点位置  
        Position p=head->next; //获取第一个节点位置
        if(p!=NULL)  //第一个节点存在
        {  
            if(p==GetTail(plist))  //若第一个节点就是尾节点
                plist->tail = p->previous; //尾指针直接指向第一个节点的前继节点即头节点
            head->next = p->next; //头节点的后继指针直接指向第一个节点的后一个节点也就是第二个节点
            head->next->previous = head; //第二个节点的前继指针指向头节点
            plist->size--;  //删除第一个节点后链表大小减1
 
        }     
        return p;  
    }  
 
    /*获得节点的数据项*/ 
    Item GetItem(Position p)  
    {  
        return p->data;  
    }  
 
    /*设置节点的数据项*/ 
    void SetItem(Position p,Item i)  
    {  
        p->data = i;  
    }  
 
    /*删除链表中的尾节点并返回地址,改变链表的尾指针指向新的尾节点*/ 
    PNode Remove(DList *plist)  
    {  
        Position p=NULL;  
        if(IsEmpty(plist))  
            return NULL;  
        else 
        {  
            p = GetTail(plist);  //让p指向尾节点
            p->previous->next = p->next;//p的前一个节点的后继指针也就是指向尾节点的指针跳过原尾节点指向头节点  
            plist->tail = p->previous;  //尾指针重新指向原尾节点的前一个节点
            plist->size--;//删除尾节点后链表大小减1  
            return p;  
        }  
    }  
    /*在链表中p位置之前插入新节点s*/ 
    PNode InsBefore(DList *plist,Position p,PNode s)  
    {  
        s->previous = p->previous; //s节点的前继指针指向 p节点的前一个节点
        s->next = p; //s节点的后继指针指向p节点
        p->previous->next = s; //原p节点的前一个节点的后继指针指向s节点     
        p->previous = s; //p节点的前继指针指向s节点
 
        plist->size++; //插入新节点s后,链表大小加1
        return s;  
    }  
    /*在链表中p位置之后插入新节点s*/ 
    PNode InsAfter(DList *plist,Position p,PNode s)  
    {  
        s->next = p->next;  //s节点的后继指针原p节点的下一个节点
        s->previous = p;  //s节点的前继指针指向p节点
 
        if(p->next != NULL)  //原p节点下一个节点存在
            p->next->previous = s;  //原p节点下一个节点的前继指针指向新加的s节点
        p->next = s;//原p节点的后继指针指向s节点  
 
        if(p = GetTail(plist)) //如果p是尾节点
            plist->tail = s;  //那么尾指针重新指向新加的s节点,让s节点作为尾节点
 
        plist->size++; //插入新节点s后,链表大小加1  
        return s;  
    }  
 
    /*返回在链表中第i个节点的位置*/ 
    PNode LocatePos(DList *plist,int i)  
    {  
        int cnt = 0;  
        Position p = GetHead(plist); //p指向头节点
        if(i>GetSize(plist)||i<1)  //判断i大小是否在链表大小之间
            return NULL;  
 
        while(++cnt<=i) //p指针位置从头节点开始往后移动,直至第i个节点的位置
        {  
            p=p->next;  
        }  
 
        return p;  //p最后指向的位置就是所求的第i个节点的位置
    }  
 
      /*依次对链表中每个元素调用函数visit()打印链表元素值*/ 
    void ListTraverse(DList *plist,void (*visit)())  
    {  
        Position p = GetHead(plist);//p指向头节点   
        if(IsEmpty(plist))  //空链表无数据就退出
            printf("NULL");  
        else 
        {  
 
            while(p->next!=NULL)  //p不断往下遍历输出元素值
            {  
                p = p->next;  
                visit(p->data);            
            }         
        }  
    }
     /*打印data*/
    void print(Item data)
    {
        printf("%d ",data);
    }
 
 
/*test.c*/
 
#include"dlist.h"
    #include"dlist.c" 
    #include<stdio.h>
    #include<stdlib.h>   
    main()  
    {  
        int i,n;
        int select;
        DList *plist = NULL;  
        PNode p = NULL;  
        printf("开始创建空双向链表。。。。。\n");
        plist = InitList();  //创建双向链表
        system("sleep 2");
        printf("创建空双向链表完成!\n");
        printf("请输入你想创建的节点数目:");
        scanf("%d",&n);
        if(n==0)
        {
            printf("空表不能进行任何操作!\n");
            exit(0);
        }
        int a[n];
        printf("开始创建头节点。。。。。。\n");
        printf("请输入头节点的数据(空头节点请写0继续):");
        scanf("%d",&a[0]);
        printf("正在创建头节点。。。。。。\n");
        p = InsFirst(plist,MakeNode(a[0]));
        printf("头节点创建完成!数据为:%d\n",a[0]);  
           for(i=1;i<n;i++) case="" p="InsAfter(plist,p,MakeNode(a[i]));" pre="" s="" sleep="">
         
    
</n;i++)></stdlib.h></stdio.h></stdlib.h></malloc.h>

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值