数据结构 || 带头非循环双链表

双链表

一:双链表简介
  • 学习了带头结点的单链表后,我们发现我们只能从某个结点开始往后遍历整个单链表.要想访问该结点前面的结点只能从头结点重新开始遍历.因此双链表 诞生了.
  • 这里先介绍带头非循环的双链表(带头结点的循环双链表下篇文章再介绍)
带头非循环双链表示意图

在这里插入图片描述

  • 特点:
  • 1.每个结点都有两个指针域(一个指向其前面的结点,一个指针指向其后一个结点).
  • 2.链表的头结点的prev指针和尾结点的next指针都指向NULL
  • 3.物理上非连续,逻辑上连续
二:双链表的具体实现
2.1:双链表结构的定义
typedef int DLDataType;

typedef struct DListNode{
  DLDataType _value;      //头结点中的value没有意义
  struct  DListNode* _prev; //指向该结点的前一个结点位置
  struct  DListNode* _next;//指向当前结点的下一个结点
}DListNode;

2.2:定义指向双链表头结点的头指针
typedef struct DList{
  DListNode* head;  //表示链表中的头节点 
} DList;
2.3:双链表的初始化
  • 注意:双链表的头结点的数据域中不存储值,值设置为0
//初始化
void DListInit(DList *dlist){
  assert(dlist!=NULL);
  dlist->head=DListBuyNode(0);
  dlist->head->_prev=NULL;
  dlist->head->_next=NULL; 
}
2.4:定义一个专门用来申请新结点的函数
  • 因为在后面的操作中,每插入一个结点都要申请结点大小的内存空间,所以为了方便我们封装一个函数用来减少工作量
//申请空间的函数
DListNode * DListBuyNode(DLDataType value){
  DListNode* new_node=(DListNode*)malloc(sizeof(DListNode));
  new_node->_value=value;
  new_node->_prev=NULL;
  new_node->_next=NULL;
  return new_node;
}
2.5:清空双链表(保留了头结点)
  • 算法思想
  • 1.定义一个指针cur用于遍历整个双链表的每一个结点
  • 2.定义一个指针tmp指向指针 cur 所指的那个结点
  • 3.指针cur再指向其所结点的下一个结点位置
  • 4.释放tmp所指的空间 ,只到cur下一个结点为空
  • 动图演示
    在这里插入图片描述
//1.清空链表
void DListClear(DList *dlist){
  assert(dlist!=NULL);
  assert(dlist->head!=NULL);

  DListNode* cur=dlist->head->_next;
  while(cur->_next!=NULL){ 
    DListNode* tmp=cur;
    cur=cur->_next;
    free(tmp);
  }
  dlist->head->_next=dlist->head->_prev=NULL; 
}

2.6:清空双链表(将头结点也释放)
  • 算法思想
  • 1.调用双链表结点的清除函数
  • 2.将头结点也释放了,并将头指针置为NULL
//2.彻底的清空链表,彻底销毁
void DListDestory(DList *dlist){
  assert(dlist!=NULL);
  
  DListClear(dlist);
  free(dlist->head);
  dlist->head=NULL;
}

2.7:双链表的头插
  • 算法思想
  • 1.定义一个指向双链表头结点的指针cur(用于遍历整个双链表)
  • 2.申请一个新的结点并将值赋为所给的值
  • 3.让新结点的next 指针指向cur的下一个结点
  • 4.让cur的next指针指向的结点的prev指针指向新结点
  • 5.让新结点的prev指针指向头结点,让cur的next指向新结点
  • 代码实现
//增
//头插
void  DListPushFront(DList *dlist,DLDataType value){
  assert(dlist!=NULL);
  assert(dlist->head!=NULL);

  DListNode* cur=dlist->head;
  DListNode* new_node=DListBuyNode(value);
  new_node->_next=cur->_next;
  if(cur->_next!=NULL)
    cur->_next->_prev=new_node;
  new_node->_prev=cur;
  dlist->head->_next=new_node;
  }
2.8双链表的尾插
  • 算法思想
  • 1.定义指向结点类型的指针cur 指向头结点的下一个结点(用于遍历整个双链表)
  • 2.让cur遍历双链表的结点知道遇见最后一个结点
  • 3.让cur 的next指针指向新结点
  • 4.让新结点的prev指针指向原来的尾结点,再让新结点的next指针指向NULL
  • 代码实现
void DListPushBack(DList *dlist, DLDataType value){
  assert(dlist!=NULL);

  DListNode* node=DListBuyNode(value);
  DListNode* cur=dlist->head;
  while(cur->_next!=NULL){
    cur=cur->_next;
  }
  cur->_next=node;
  node->_prev=cur;
  node->_next=NULL;
}
2.9:双链表中查找结点值与指定值相等的结点
  • 算法思想
  • 1.定义指向头结点下一个结点的指针cur(用于遍历真个双链表)
  • 2.开始查找结点值与指定值相等的结点,此过程中cur不断的指向它的下一个结点位置
  • 3.找到返回该结点的位置,否则返回NULL
  • 代码实现
DListNode * DListFind(DList *dlist,DLDataType value){
  assert(dlist!=NULL);

  DListNode* cur=dlist->head->_next;
  while(cur!=NULL){
    if(cur->_value==value)
      return cur;
    cur=cur->_next;
  }
  return NULL;
}
2.10:在指定双链表的指定位置插入新的结点
  • 算法思想
  • 1.申请一个新的结点,将结点的值设为指定的值
  • 2.让新结点的next指针指向pos位置的结点
  • 3.让pos位置结点的prev所指结点的next指针指向新的结点
  • 4.让新结点的prev指针指向pos位置结点的prev指针指向的结点
  • 5.让pos位置结点的prev指针指向新结点
  • 代码实现
//插
// 在pos的前面进行插入
void DListInsert(DListNode *pos, DLDataType value){
 DListNode* node=DListBuyNode(value);
 
 node->_next=pos;
 node->_prev=pos->_prev;
 pos->_prev->_next=node;
 pos->_prev=node;
}
2.11:删除指定位置的结点
  • 算法思想
  • 1.让pos位置结点的prev指针指向结点的next指针指向pos位置结点的next结点
  • 2.让pos位置结点的next指针指向结点的prev指向pos位置结点的前一个结点
  • 3.释放pos所在位置的结点空间
  • 代码实现
// 删除pos位置的节点
void DListErase(DListNode *pos){

  pos->_prev->_next = pos->_next ;
  pos->_next->_prev=pos->_prev; 
  free(pos);
}
2.12 双链表的头删
  • 算法思想
  • 1.定义一个指向双链表头结点的指针cur
  • 2.让双链表头指针指向该指针(cur)所指向结点的下一个结点
  • 3.释放cur所指向的结点空间
  • 4.让头结点的prev指针指向NULL
  • 代码实现
//删
//头删
void DListPopFront(DList *dlist){
  assert(dlist!=NULL);

  DListNode* cur=dlist->head;
  dlist->head=cur->_next;
  free(cur);
  dlist->head->_prev=NULL;
}
2.12:双链表的尾删
  • 算法思想
  • 1.定义一个指向双链表头结点的指针cur
  • 2.让指针不断的往后移动,直到cur指向该链表的最后一个结点位置
  • 3.将尾节点的prev指针指向结点的next指针指向NULL
  • 4.释放cur所指的空间(原有的尾结点)
  • 代码实现
//尾删
void DListPopBack(DList *dlist){
  assert(dlist!=NULL);
  DListNode* cur=dlist->head;
  while(cur->_next!=NULL){
    cur=cur->_next;
  }
  cur->_prev->_next=NULL;
  free(cur);
}
3.源代码链接

C语言版的源代码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值