【五】双向链表

1、双向链表的定义
在单链表的结点中增加一个指向其前驱的p r e指针!

这里写图片描述

2、数据结构的定义

//dlinklist.h
/*
  对外做数据封装,屏蔽线性表的实现
*/
typedef void DLinklist;
typedef struct _struct_linklistNode DLinklistNode;
struct _struct_linklistNode
{
  DLinklistNode *pre;
  DLinklistNode *next;
};
//dlinklist.c
//内部头结点的真实定义
typedef struct _struct_dlinklist
{
  DLinklistNode header;
  //游标成员
  DLinklistNode *silder;
  int length;
}TDLinklist;
//main.c
//被插入元素的定义
typedef struct _struct_value
{
  DLinklistNode node;
  int v;
}Value;

3、创建操作

/*
该方法用于创建并且返回一个空的线性表
*/
DLinklist* List_Create()
{
  TDLinklist *tlist = NULL;
  tlist = (TDLinklist*)malloc(sizeof(TDLinklist));
  if(tlist != NULL)
  {
    tlist->header.pre = NULL;
    tlist->header.next = NULL;
    tlist->silder = NULL;
    tlist->length = 0;
  }
  return tlist;
}

4、插入操作

示意图:
这里写图片描述

/*
该方法用于向一个线性表list的pos位置处插入新元素node
返回值为1表示插入成功,0表示插入失败
*/
int List_Insert(DLinklist* list, DLinklistNode* node, int pos)
{
  int iret = 1;
  TDLinklist *tlist = (TDLinklist*)list;
  iret = iret && (tlist != NULL) && (node != NULL) && (pos >= 0);
  if(iret)
  {
    DLinklistNode *current = (DLinklistNode*)tlist;
    DLinklistNode *next = NULL;
    for(int i = 0; (i<pos) && (current->next != NULL); i++)
    {
      current = current->next;
    }
    next = current->next;

    current->next = node;
    node->next = next;

    if(next != NULL)
    {
      next->pre = node;
    }
    node->pre = current;
    if(tlist->length == 0)
    {
      //如果插入的是第一个元素,则默认让游标指向它
      //游标默认指向第一个插入的元素
      tlist->silder = tlist->header.next;
    }
    //如果插入的位置是第一个元素,则修正新的第一个元素的pre指针
    if(current == (DLinklistNode*)tlist)
    {
      //让第一个元素的pre指向NULL
      node->pre = NULL;
      //添加如下的代码,可让游标始终指向链表的第一个元素
      //tlist->silder = tlist->header.next;
    }
    tlist->length++;
  }
  return iret;
}

5、删除操作

示意图:
这里写图片描述

/*
该方法用于删除一个线性表list的pos位置处的元素
返回值为被删除的元素,NULL表示删除失败
*/
DLinklistNode* List_Delete(DLinklist* list, int pos)
{
  DLinklistNode *ret = NULL;
  TDLinklist *tlist = (TDLinklist*)list;
  if(tlist != NULL)
  {
    //要保证被删除的元素存在, && (tlist->length > 0)
    if((pos >= 0)&&(pos < tlist->length))
    {
      DLinklistNode *current = (DLinklistNode*)tlist;
      DLinklistNode *next = NULL;
      for(int i = 0;i<pos;i++)
      {
        current = current->next;
      }
      ret = current->next;
      next = ret->next;

      current->next = ret->next;

      if(next != NULL)
      {
        next->pre = current;
        //以下条件等价于:current == (DLinklistNode*)tlist
        if(pos == 0)
        {
          next->pre = NULL;
        }
      }
      //如果游标当前指向了要删除的元素,则让游标指向它的下一个元素
      if(ret == tlist->silder)
      {
        tlist->silder = ret->next;
      }

      ret->pre = NULL;
      ret->next = NULL;
      tlist->length--;
    }
  }
  return ret;
}

6、增加游标后的新操作

  • 获取当前游标指向的数据元素
  • 将游标重置指向链表中的第一个数据元素
  • 将游标移动指向到链表中的下一个数据元素
  • 将游标移动指向到链表中的上一个数据元素
  • 直接指定删除链表中的某个数据元素

相关定义:

/*
  删除指定结点
  如果成功,返回被删除的结点,否则返回NULL
*/
DLinklistNode* List_DeleteNode(DLinklist *list,DLinklistNode *node);


/*
  重置游标
  如果成功,返回重置后的元素,否则返回NULL
*/
DLinklistNode* List_Reset(DLinklist *list);


/*
  获取当前游标指向的元素
  如果成功,返回当前游标指向的元素,否则返回NULL
*/
DLinklistNode* List_Current(DLinklist *list);


/*
  让游标指向下一个元素
  如果成功,返回下一个元素,否则返回NULL
  特殊情况:链表最后一个元素的下一个元素也为NULL
*/
DLinklistNode* List_Next(DLinklist *list);


/*
  让游标指向前一个元素
  如果成功,返回前一个元素,否则返回NULL
  特殊情况:链表的第一个元素的前一个元素也为NULL
*/
DLinklistNode* List_Pre(DLinklist *list);

相关实现:

/*
  删除指定结点
  如果成功,返回被删除的结点,否则返回NULL
*/
DLinklistNode* List_DeleteNode(DLinklist *list,DLinklistNode *node)
{
  DLinklistNode *ret = NULL;
  TDLinklist *tlist = (TDLinklist*)list;

  if((tlist != NULL) && (node != NULL))
  {
    int i = 0;
    DLinklistNode *current = (DLinklistNode*)tlist;
    for(i = 0;i < tlist->length;i++)
    {
      current = current->next;
      if(current == node)
      {
        ret = current;
        break;
      }
    }
    //判断是否找到要删除的元素
    if(ret != NULL)
    {
        List_Delete(list,i);
    }
  }
  return ret;
}


/*
  重置游标
  如果成功,返回重置后的元素,否则返回NULL
*/
DLinklistNode* List_Reset(DLinklist *list)
{
  DLinklistNode *ret = NULL;
  TDLinklist *tlist = (TDLinklist*)list;
  if(tlist != NULL)
  {
    tlist->silder = tlist->header.next;
    ret = tlist->silder;
  }
  return ret;
}


/*
  获取当前游标指向的元素
  如果成功,返回当前游标指向的元素,否则返回NULL
*/
DLinklistNode* List_Current(DLinklist *list)
{
  DLinklistNode *ret = NULL;
  TDLinklist *tlist = (TDLinklist*)list;
  if(tlist != NULL)
  {
    ret = tlist->silder;
  }
  return ret;
}


/*
  让游标指向下一个元素
  如果成功,返回下一个元素,否则返回NULL
  特殊情况:链表最后一个元素的下一个元素也为NULL
*/
DLinklistNode* List_Next(DLinklist *list)
{
  DLinklistNode *ret = NULL;
  TDLinklist *tlist = (TDLinklist*)list;
  if(tlist != NULL)
  {
    tlist->silder =  tlist->silder->next;
    ret = tlist->silder;
  }
  return ret;
}


/*
  让游标指向前一个元素
  如果成功,返回前一个元素,否则返回NULL
  特殊情况:链表的第一个元素的前一个元素也为NULL
*/
DLinklistNode* List_Pre(DLinklist *list)
{
  DLinklistNode *ret = NULL;
  TDLinklist *tlist = (TDLinklist*)list;
  if(tlist != NULL)
  {
    tlist->silder = tlist->silder->pre;
    ret = tlist->silder;
  }
  return ret;
}

7、小结

  • 双向链表在单链表的基础上增加了指向前驱的指针
  • 功能上双向链表可以完全取代单链表的使用
  • 循环链表的N e xt,Pre和C u rre nt操作可以高效遍历链表中的所有元素

8、完整源码下载

文件名称:dlinklist-1.0.tar.gz
链接: http://pan.baidu.com/s/1c0bFUda 密码: 2asr

编译步骤:
0.1 解压缩:tar -zxvf dlinklist-1.0.tar.gz
0.2 进入目录:./configure
0.3 生成Seqlist:make
0.4 运行程序:./Dlinklist

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值