数据结构系列--双向链表

慧子和她的3D 慧子和她的3D 

单链表的局限:

  1. 单链表的结点都只有一个指向下一个结点的指针

    每个元素只知道后继结点是谁,不知道前驱结点

  2. 单链表的数据元素无法直接访问其前驱元素

  3. 逆向访问单链表中的元素是极其耗时的操作

    len = LinkList_Length(list);
    for (i= len-1;len >= 0;i--)
    {
      LinkListNode* p = LinkList_Get(list,i); //  访问数据元素p中的元素
    }

     LinkList_Get 每次做循环找到需要返回的元素,复杂度是o(n^2)

        在单链表的结点中增加一个指向前驱的pre指针-->双链表

    前面留有习题,单链表进行逆序操作,算法复杂度为O(n)

        对于单链表逆序访问非常耗时,可以写一个时间算法复杂度为O(n)的操作,每次运算的时候转置一下:最后一个元素变成第一个元素,进行逆序操作,因为算法复杂度为O(n),可以从原来的最后一个也就是现在的第一个进行访问,复杂度为O(n)访问结束。这里面逆序耗时很多,此时引入双向链表。

     

    将单链表代码改为双向链表工程名为:DLinkList.pro

    DLinkList.h

    #ifndef _DLINKLIST_H_
    #define _DLINKLIST_H_
    
    typedef void DLinkList;
    typedef struct _tag_DLinkListNode DLinkListNode;
    struct _tag_DLinkListNode
    {
      DLinkListNode* Next;
      DLinkListNode* Pre;// 增加一个指向前驱数据元素的结点
    }
    DLinkList* DLinkList_Creat(int capacity);
    void DLinkList_Destory(DLinkList* list);
    void DLinkList_Clear(DLinkList* list);
    int DLinkList_Length(DLinkList* list);
    // int DLinkList_Capacity(DLinkList* list);
    int DLinkList_Insert(DLinkList* list,DLinkListNode* node,int pos);
    DLinkListNode* DLinkList_Get(DLinkList* list,int pos);
    DLinkListNode* DLinkList_Delete(DLinkList* list,int pos);
    
    #endif
    

    源文件

    #include <stdio.h>
    #include <malloc.h>
    #include "DlinkList.h"
    // 头结点结构体
    typedef struct _tag_DlinkList
    {
    int length//静态链表最多容纳多少元素
    TDlinkListNode header;//链表头
    }TDlinkList;
    
    DlinkList* DlinkList_Create()//创建时指定静态链表的容量
    {
      TDlinkList* ret = (TDlinkList*)malloc(sizeof(TDlinkList));
      
      if(ret != NULL)//指针不为0时可以继续赋值操作
      {
        ret->length = 0;//data作为静态链表表头表示长度
        ret->header.next = NULL;
        ret->header.pre = NULL;// 初始化的时候头结点的前向指针为空,即便不用也要初始化
      }
      
      return ret;
    }
    
    void DlinkList_Destory(DlinkList* list)
    {
      free(list);
    }
    
    void DlinkList_Clear(DlinkList* list) 
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      if(sList !=NULL)//链表不为空是合法的,可以继续清空操作
      {
        sList->length = 0;
        sList->header.next = NULL;
        sList->header.pre = NULL;
      }
      
    int DlinkList_Length(DlinkList* list)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      int ret = -1;//定义一个返回值
      if(sList !=NULL)//链表不为空是合法的,可以继续清空操作
      {
        ret = sList->length;
      }
      return ret;
      }
      
    int DlinkList_Insert(DlinkList* list,DlinkListNode* node,int pos)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      int ret =(sList !=NULL) && (pos >=0) && (node != NULL);//单链表方法完成判断
      int i=0;
      
      if(ret)//在数组中找空闲位置index
      {
        DlinkListNode* current = (DlinkListNode*)sList;
        DlinkListNode* next = NULL ;// 定义指针
        // 移动到插入位置
        for(i=0;(i<pos) && (current->next != NULL);i++)//后面是个保护动作,对于位置的保护,最多放在最后
          {
            current = current->next;//当前移动到下一个位置
          }
        next = current->next;
        
        current->next = node;
        node->next = next;
          // 新插入位置是双向链表第一个
        if(next != NULL)// 双向链表第一个元素的next不能为空,不然会与76行报错
            {
              next->pre = node;
            }
        node->pre = current;
        
        if (sLsit->length == 0)// 如果是个空链表插入第一个位置,那头结点要指向空
            {
              node->pre = NULL ;
            }
        sList->length++ ;
      
         }
      // 插入位置在头结点的位置比较特殊。pre需指向空
      return ret;
    }
    
    //由表头开始通过next域移动pos次后,当前元素的next域即要获取元素在数组中的下标
    DlinkListNode* DlinkList_Get(DlinkList* list,int pos)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      
      int i = 0;
      if( (sList != NULL ) && (0 <= pos) && (pos < sList->length) )//链表不为空是合法的,长度正常
      {
          DlinkListNode* current =(DlinkListNode*)sList;
        
        for(i=0;i<pos;i++)
        {
          current = current->next;//第一个元素所在下标
        }
        
          ret = current->next;
      }
      return ret;
    }
    
    //获取第pos个元素,将第pos个元素从链表里删除
    DlinkListNode* DlinkList_Delete(DlinkList* list,int pos)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      int i = 0;
      if( (sList !=NULL ) && (0 <= pos)&&(pos < sList->length )//链表不为空是合法的,长度正常
      {
        DlinkListNode* current = (DlinkListNode*)sList;
        DlinkListNode* next = NULL;
        // 找被删除元素
         for(i=0;i<pos;i++)
        {
          current = current ->next;//第一个元素所在下标
        }
        ret = current ->next;
        next = ret->next;// 被删除元素后继
        
        current ->next = next;
        // 被删除是最后一个元素
        if(next != NULL)
        {
          next->pre = current;
          // 被删元素是第一个
          if(current == (DlinkListNode*)sList)// 强制性修正,判断当前是否是表头
          {
            next->pre = NULL;
          }
       }
        sList->length--;
    
    }
    
    

    main.c

    #include <stdio.h>
    #include <stdlib.h>
    #include "DlinkList.h"
    struct Value
    {
      DLinkListNode header;// 表头域
      int v;
    };
    
    int main(int argc,char *argv[])
    {
    int i = 0;
    DlinkList* list =  DlinkList_Creat();
    struct Value* pv = NULL;// 定义一个指针
    // 定义
    struct Value v1;
    struct Value v2;
    struct Value v3;
    struct Value v4;
    struct Value v5;
    // 赋值
    v1.v = 1;
    v2.v = 2;
    v3.v = 3;
    v4.v = 4;
    v5.v = 5;
    // 最后一个元素后面插入
    DlinkList_Insert(list,(DLinkListNode*)&v1,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v2,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v3,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v4,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v5,DLinkList_Length(list));
    for(i = 0;i <DlinkList_Length(list);i++)
      {// 调用get来取元素
        pv = (struct Value*)DlinkList_Get(list,i);
        printf("%d\n",pv->v);
      }
    
    printf("\n");
    // 删除第一个和最后一个元素
    DlinkList_Delete(list,DlinkList_Length(list)-1;);
    DlinkList_Delete(list,0);
    
    DlinkList_Destroy(list);
    
    return 0;
    }

     

    双向链表新操作

    DLinkList.h

    #ifndef _DLINKLIST_H_
    #define _DLINKLIST_H_
    
    typedef void DLinkList;
    typedef struct _tag_DLinkListNode DLinkListNode;
    struct _tag_DLinkListNode
    {
      DLinkListNode* Next;
      DLinkListNode* Pre;// 增加一个指向前驱数据元素的结点
    }
    DLinkList* DLinkList_Creat(int capacity);
    void DLinkList_Destory(DLinkList* list);
    void DLinkList_Clear(DLinkList* list);
    int DLinkList_Length(DLinkList* list);
    // int DLinkList_Capacity(DLinkList* list);
    int DLinkList_Insert(DLinkList* list,DLinkListNode* node,int pos);
    DLinkListNode* DLinkList_Get(DLinkList* list,int pos);
    DLinkListNode* DLinkList_Delete(DLinkList* list,int pos);
    // 引进游标新操作
    DLinkListNode* DLinkList_DeleteNode(DLinkList* list,DLinkListNode* node);
    DLinkListNode* DLinkList_Current(DLinkList* list);
    DLinkListNode* DLinkList_Next(DLinkList* list);
    DLinkListNode* DLinkList_Pre(DLinkList* list);
    #endif

    源文件

    #include <stdio.h>
    #include <malloc.h>
    #include "DlinkList.h"
    
    #define AVAILABLE -1//空闲位置的宏
    //链表结构体定义
    typedef struct _tag_DlinkList
    {
    int length;//静态链表最多容纳多少元素
    TDlinkListNode header;//链表头
    TDlinkListNode slider;//游标
    }TDlinkList;
    
    DlinkList* DlinkList_Create()//创建时指定静态链表的容量
    {
      TDlinkList* ret = (TDlinkList*)malloc(sizeof(TDlinkList));
      
      if(ret != NULL)//指针不为0时可以继续赋值操作
      {
        ret->length = 0;//data作为静态链表表头表示长度
        ret->header.next = NULL;
        ret->header.pre = NULL;// 初始化的时候头结点的前向指针为空,即便不用也要初始化
        ret->slider = NULL;
      }
      
      return ret;
    }
    
    void DlinkList_Destory(DlinkList* list)
    {
      free(list);
    }
    
    void DlinkList_Clear(DlinkList* list) 
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      if(sList !=NULL)//链表不为空是合法的,可以继续清空操作
      {
        sList->length = 0;
        sList->header.next = NULL;
        sList->header.pre = NULL;
        ret->slider = NULL;
      }
      
    int DlinkList_Length(DlinkList* list)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      int ret = -1;//定义一个返回值
      if(sList !=NULL)//链表不为空是合法的,可以继续清空操作
      {
        ret = sList->length;
      }
      return ret;
      }
      
    int DlinkList_Insert(DlinkList* list,DlinkListNode* node,int pos)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      int ret =(sList !=NULL) && (pos >=0) && (node != NULL);//单链表方法完成判断
      int i=0;
      
      if(ret)//在数组中找空闲位置index
      {
        DlinkListNode* current = (DlinkListNode*)sList;
        DlinkListNode* next = NULL ;// 定义指针
        // 移动到插入位置
        for(i=0;(i<pos) && (current->next != NULL);i++)//后面是个保护动作,对于位置的保护,最多放在最后
          {
            current = current->next;//当前移动到下一个位置
          }
        next = current->next;
        
        current->next = node;
        node->next = next;
          // 新插入位置是双向链表第一个
        if(next != NULL)// 双向链表第一个元素的next不能为空,不然会与76行报错
            {
              next->pre = node;
            }
        node->pre = current;
        
        if (sLsit->length == 0)// 如果是个空链表插入第一个位置,那头结点要指向空
            {
              node->pre = NULL ;
              sLsit->slider = node;// 将链表中游标指向第一个元素
            }
        sList->length++ ;
      
         }
      // 插入位置在头结点的位置比较特殊。pre需指向空
      return ret;
    }
    
    //由表头开始通过next域移动pos次后,当前元素的next域即要获取元素在数组中的下标
    DlinkListNode* DlinkList_Get(DlinkList* list,int pos)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      
      int i = 0;
      if( (sList != NULL ) && (0 <= pos) && (pos < sList->length) )//链表不为空是合法的,长度正常
      {
          DlinkListNode* current =(DlinkListNode*)sList;
        
        for(i=0;i<pos;i++)
        {
          current = current->next;//第一个元素所在下标
        }
        
          ret = current->next;
        }
      return ret;
    }
    
    //获取第pos个元素,将第pos个元素从链表里删除
    DlinkListNode* DlinkList_Delete(DlinkList* list,int pos)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      int i = 0;
      if( (sList !=NULL ) && (0 <= pos)&&(pos < sList->length )//链表不为空是合法的,长度正常
      {
        DlinkListNode* current = (DlinkListNode*)sList;
        DlinkListNode* next = NULL;
        // 找被删除元素
         for(i=0;i<pos;i++)
          {
            current = current ->next;//第一个元素所在下标
          }
        ret = current ->next;
        next = ret->next;// 被删除元素后继
        
        current ->next = next;
        // 被删除是最后一个元素
        if(next != NULL)
          {
            next->pre = current;
            // 被删元素是第一个
            if(current == (DlinkListNode*)sList)// 强制性修正,判断当前是否是表头
              {
                next->pre = NULL;
              }
           }
       // 里面只有一个元素被删除
       if(sList->slider == ret)// 删除元素是不是游标指向元素
         {
           sList->slider == next;// 移动游标指向下一个元素
         }
        sList->length--;
       }
    return ret;
    }
    // 引进游标新操作
    DLinkListNode* DLinkList_DeleteNode(DLinkList* list,DLinkListNode* node)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      int i = 0;
      // 查找删除元素
      if(sList !=NULL ) 
      {
        DlinkListNode* current = (DlinkListNode*)sList;
        // 找被删除元素
         for(i=0;i<sList->length;i++)
          {
            if(current ->next == node)
              {
                ret = current ->next;
                break;
              }
            current  = current ->next;
          }
        if(ret != NULL)// 没找到
          {
            DlinkList_Delete(sList,i);
          }
       // 里面只有一个元素被删除
       if(sList->slider == ret)// 删除元素是不是游标指向元素
           {
             sList->slider == next;// 移动游标指向下一个元素
           }
     }
        sList->length--;
       }
    return ret;
    }
    // 游标相关操作
    DLinkListNode* DLinkList_Current(DLinkList* list)
    {
     TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      
      if(sList !=NULL)//链表不为空是合法的,可以继续清空操作
        {
          ret->sList->slider ;
        }
        
    }
    DLinkListNode* DLinkList_Next(DLinkList* list)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      
      if ((sList !=NULL) && (sList->slider != NULL))//链表不为空是合法的,可以继续清空操作
      {
        ret->sList->slider ;
        sList->slider = ret->next;
      }
    }
    DLinkListNode* DLinkList_Pre(DLinkList* list)
    {
      TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
      DlinkListNode* ret = NULL;//定义一个返回值
      
      if(sList !=NULL)//链表不为空是合法的,可以继续清空操作
      {  
        ret->sList->slider ;
        sList->slider = ret->pre;// 指向当前前一个
      }
    }
    

    main.c

    #include <stdio.h>
    #include <stdlib.h>
    #include "DlinkList.h"
    struct Value
    {
      DLinkListNode header;// 表头域
      int v;
    };
    
    int main(int argc,char *argv[])
    {
    int i = 0;
    DlinkList* list =  DlinkList_Creat();
    struct Value* pv = NULL;// 定义一个指针
    // 定义
    struct Value v1;
    struct Value v2;
    struct Value v3;
    struct Value v4;
    struct Value v5;
    // 赋值
    v1.v = 1;
    v2.v = 2;
    v3.v = 3;
    v4.v = 4;
    v5.v = 5;
    // 最后一个元素后面插入
    DlinkList_Insert(list,(DLinkListNode*)&v1,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v2,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v3,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v4,DLinkList_Length(list));
    DlinkList_Insert(list,(DLinkListNode*)&v5,DLinkList_Length(list));
    for(i = 0;i <DlinkList_Length(list);i++)
      {// 调用get来取元素
        pv = (struct Value*)DlinkList_Get(list,i);
        printf("%d\n",pv->v);
      }
    
    printf("\n");
    // 删除第一个和最后一个元素
    DlinkList_Delete(list,DlinkList_Length(list)-1;);
    DlinkList_Delete(list,0);
    for(i = 0;i <DlinkList_Length(list);i++)
      {// 调用get来取元素
        pv = (struct Value*)DlinkList_Next(list);
        printf("%d\n",pv->v);
      }
      printf("\n");
      DlinkList_Resert(list);// 游标指向第一个元素
      DlinkList_Next(list);// 游标向后移一个指向位置2
      pv = (struct Value*)DlinkList_Current(list);
      printf("%d\n",pv->v);
      // 删除当前元素
      DlinkList_DeleteNode(list,( DlinkLisNode*)pv);
      pv = (struct Value*)DlinkList_Current(list);
      printf("%d\n",pv->v);
      
      DlinkList_Pre(list);
      pv = (struct Value*)DlinkList_Current(list);
      printf("%d\n",pv->v);
      printf("Length : %d\n",DlinkList_Length(list));
      
      DlinkList_Destroy(list);
    
    return 0;
    }

    小结:

    双向链表在单链表的基础上增加了指向前驱的指针

    功能上双向链表可以取代单链表的使用

    循环链表的NEXT PRE CURRENT操作可以高效的遍历链表中所有元素

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值