文章目录

  • 一.引入
  • 二. 双向链表的定义
  • 三.双向链表与单链表对比
    • 3.1图示对比
    • 3.2代码对比
  • 四.双向链表的操作
    • 4.1双向链表的创建
    • 4.2双向链表的插入
    • 4.3双向链表的删除
    • 4.4双向链表的销毁
  • 五.总结
  • 六.全部代码

一.引入

我们在单链表中,有了next指针,这个指针是用来指向下一个节点的,如果我们需要查找下一个结点的时间复杂度为o(1),如果我们需要查找上一个节点的时候,那么时间复杂度就变为o(n)了,需要从头进行遍历一遍;这时我们就会想:如果可以向前查找就方便了许多,因此我们引入了双向链表。

二. 双向链表的定义

双向链表:是在单链表的每个节点中,再设置一个指向前驱结点的指针域。

顾名思义就是链表由单向的变成了双向的,每一个节点由原来的一个指针变为两个指针,一个用来指向直接后继,另一个用来指向直接前驱。

三.双向链表与单链表对比

通过对比可以更好认识二者的联系与区别。

3.1图示对比

单链表:

双向链表:

3.2代码对比

单链表代码如下:

typedef struct Node{ //定义单链表结点类型int data; //数据域,可以是别的各种数据类型struct Node *next; //指针域
}LNode, *LinkList;

双向链表代码如下:

typedef struct DulNode{int data;         //  数据域struct DulNode *prior;       //  向前的指针struct DulNode *next;      //  向后的指针
}DulNode,*DuLinkList;

四.双向链表的操作

双向链表是单链表中扩展出来的结构,所以有很多的操作是和单链表相同的,如求长度,查找元素,获取一个元素,这里我们对双向链表进行创建,插入,删除,销毁的一系列操作。

4.1双向链表的创建

双向链表在初始化时,要给首尾两个节点分配内存空间。成功分配后,需要将首节点的prior指针和尾节点的next指针都指向NULL,这是十分关键的一步,因为这是之后用来判断空表的条件。并且当链表为空时,要将首节点的next指向尾节点,尾节点的prior指向首节点。

pElem CreatList(){pElem head = (pElem)malloc( sizeof(eElem) );assert( head != NULL );      //进行断言head->next = head->prior = NULL;//初始化链表指针置空return head;
}

4.2双向链表的插入

双向链表的插入其实并不复杂,只是在原有单链表的基础上多了连接一个向前的指针而已。但是需要注意的是操作的顺序很重要,不可以写反了。
以下面这个为例,假设存储元素e的结点为s,要实现将结点s插入到结点p和p->next之间

核心代码就只有以下四行:

s->prior=p; //把p赋值给s的前驱
s->next=p->next;// 把p->next赋值给s的后继
p->next->prior=s;// 把s赋值给p->next的前驱
p->next=s;   //把s赋值给p的后继

切记顺序不可以记错 在写代码的时候可以将操作步骤画出来,理清实施步骤的顺序。

4.3双向链表的删除

如果将插入操作的原理理解后,那么删除就很好理解了。
删除只需要两个步骤:

核心代码只有三行

p->prior->next=p->next; //把p->next赋值给p->prior的后继
p->next->prior=p->prior;//把p->prior赋值给p->next的前驱
free(p); //释放结点

4.4双向链表的销毁

销毁一个双向链表的操作同单链表的相似。指针不断向后运动,每运动一个结点,释放上一个结点。

代码如下:

void DestroyList( pElem head ){pElem tmp;while( head->next != NULL ){tmp = head;        //  指针不断后移head = head->next;free(tmp);}free(head);
}

五.总结

双向链表相比于单链表来说,是更复杂一些的,毕竟多了一个prior指针,对于插入和删除需要特别注意这两种操作的核心思想以及操作顺序。另外双向链表,带来了方便,可以有效提高算法的时间性能。

六.全部代码

这里引用一位大佬写的代码,将头插法和尾插法创建表都写了,写的很细节,很清楚大家可以参考一下。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int status;
typedef int elemtype;
typedef struct node{elemtype data;struct node * next;struct node * prior;
}node;
typedef struct node* dlinklist;status visit(elemtype c){printf("%d ",c);
}/*双向链表初始化*/
status initdlinklist(dlinklist * head,dlinklist * tail){(*head)=(dlinklist)malloc(sizeof(node));(*tail)=(dlinklist)malloc(sizeof(node));if(!(*head)||!(*tail))return ERROR;/*这一步很关键*/ (*head)->prior=NULL;(*tail)->next=NULL;/*链表为空时让头指向尾*/(*head)->next=(*tail);(*tail)->prior=(*head);
}/*判定是否为空*/
status emptylinklist(dlinklist head,dlinklist tail){if(head->next==tail)return TRUE;elsereturn FALSE;
} /*尾插法创建链表*/
status createdlinklisttail(dlinklist head,dlinklist tail,elemtype data){dlinklist pmove=tail,pinsert;pinsert=(dlinklist)malloc(sizeof(node));if(!pinsert)return ERROR;pinsert->data=data;pinsert->next=NULL;pinsert->prior=NULL;tail->prior->next=pinsert;pinsert->prior=tail->prior;pinsert->next=tail;tail->prior=pinsert;
} /*头插法创建链表*/
status createdlinklisthead(dlinklist head,dlinklist tail,elemtype data){dlinklist pmove=head,qmove=tail,pinsert;pinsert=(dlinklist)malloc(sizeof(node));if(!pinsert)return ERROR;else{pinsert->data=data;pinsert->prior=pmove;pinsert->next=pmove->next;pmove->next->prior=pinsert;pmove->next=pinsert;}
}/*正序打印链表*/
status traverselist(dlinklist head,dlinklist tail){/*dlinklist pmove=head->next;while(pmove!=tail){printf("%d ",pmove->data);pmove=pmove->next;}printf("\n");return OK;*/dlinklist pmove=head->next;while(pmove!=tail){visit(pmove->data);pmove=pmove->next;}printf("\n");
}/*返回第一个值为data的元素的位序*/
status locateelem(dlinklist head,dlinklist tail,elemtype data){dlinklist pmove=head->next;int pos=1;while(pmove&&pmove->data!=data){pmove=pmove->next;pos++;}return pos;
}/*返回表长*/
status listlength(dlinklist head,dlinklist tail){dlinklist pmove=head->next;int length=0;while(pmove!=tail){pmove=pmove->next;length++;}return length;
}/*逆序打印链表*/
status inverse(dlinklist head,dlinklist tail){dlinklist pmove=tail->prior;while(pmove!=head){visit(pmove->data);pmove=pmove->prior;}printf("\n");
}/*删除链表中第pos个位置的元素,并用data返回*/
status deleteelem(dlinklist head,dlinklist tail,int pos,elemtype *data){int i=1;dlinklist pmove=head->next;while(pmove&&i<pos){pmove=pmove->next;i++;}if(!pmove||i>pos){printf("输入数据非法\n");return ERROR;}else{*data=pmove->data;pmove->next->prior=pmove->prior;pmove->prior->next=pmove->next;free(pmove);}
}/*在链表尾插入元素*/
status inserttail(dlinklist head,dlinklist tail,elemtype data){dlinklist pinsert;pinsert=(dlinklist)malloc(sizeof(node));pinsert->data=data;pinsert->next=NULL;pinsert->prior=NULL;tail->prior->next=pinsert;pinsert->prior=tail->prior;pinsert->next=tail;tail->prior=pinsert;return OK;
}
int main(void){dlinklist head,tail;int i=0;elemtype data=0;initdlinklist(&head,&tail);if(emptylinklist(head,tail))printf("链表为空\n");elseprintf("链表不为空\n");printf("头插法创建链表\n"); for(i=0;i<10;i++){createdlinklisthead(head,tail,i);}traverselist(head,tail);for(i=0;i<10;i++){printf("表中值为%d的元素的位置为",i); printf("%d位\n",locateelem(head,tail,i));}printf("表长为%d\n",listlength(head,tail));printf("逆序打印链表");inverse(head,tail);for(i=0;i<10;i++){deleteelem(head,tail,1,&data);printf("被删除的元素为%d\n",data);}traverselist(head,tail);if(emptylinklist(head,tail))printf("链表为空\n");elseprintf("链表不为空\n");printf("尾插法创建链表\n");for(i=0;i<10;i++){//inserttail(head,tail,i);createdlinklisttail(head,tail,i);}traverselist(head,tail);printf("逆序打印链表");inverse(head,tail);
}

数据结构 双向链表及其基本操作相关推荐

  1. 搬砖:数据结构之链表基本操作总结

    数据结构之链表基本操作总结 2017年05月11日 18:22:11 Lily_whl 阅读数:19151 https://blog.csdn.net/Lily_whl/article/details ...

  2. 数据结构 — 双向链表

    目录 文章目录 目录 双向链表 双向链表结点的数据结构 双向链表的操作集合 应用示例 创建双向链表 清理双向链表 查询链表结点 更新链表结点的数据 插入链表结点 删除结点 打印链表数据 双向链表 双向 ...

  3. 数据结构——双向链表的实现

    双向链表主要为了解决单链表找前驱的问题.除了插入.删除操作之外,其他操作与单链表都相同.因此这里只是比较简单的写了双向链表的插入和删除操作.画出结点结构图的话,思路会很清晰,线性表这块还算是比较简单的 ...

  4. 数据结构 —— 双向链表(超详细图解 接口函数实现)

    系列文章目录 数据结构 -- 顺序表 数据结构 -- 单链表 数据结构 -- 双向链表 数据结构 -- 队列 数据结构 -- 栈 数据结构 -- 堆 数据结构 -- 二叉树 数据结构 -- 八大排序 ...

  5. 数据结构线性表基本操作

    数据结构线性表基本操作 基本内容 线性表的顺序表示和实现 线性表的顺序存储表示 顺序表中基本操作的实现 1.顺序表的初始化 2.取值 3.查找 4.插入 5.删除 线性表的链式表示和实现 单链表的定义 ...

  6. 双向链表和循环双向链表的基本操作

    双向链表和循环双向链表的基本操作 #include<stdio.h> #include<malloc.h> typedef struct LNode {int data;str ...

  7. 8.C语言数据结构 双向链表基本操作

    前面学习了如何创建一个双向链表,本节学习有关双向链表的一些基本操作,即如何在双向链表中添加.删除.查找或更改数据元素. 本节知识基于已熟练掌握双向链表创建过程的基础上,我们继续上节所创建的双向链表来学 ...

  8. C++数据结构链表的基本操作

    这篇文章主要为大家介绍了C++数据结构链表基本操作的示例过程有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪 首先创建好一个节点 typedef struct node {in ...

  9. 数据结构树的基本操作_《数据结构》树的基本操作.doc

    <数据结构>树的基本操作 实验四 课程名称:完成日期:姓名:学号:指导教师:实验名称:实验序号:实验成绩:一.实验目的及要求 二.实验环境.实验内容 求出它的深度. .调试过程及实验结果 ...

最新文章

  1. Redis和消息队列使用实战
  2. Geany编辑器怎么使中文不报错?
  3. Keepalived配置日志文件
  4. oracle离线文档查dbms_Oracle的健康检查–dbms_hm的使用 | 学步园
  5. android arcgis多变形边框颜色,ArcGIS制图技巧—边框的选择
  6. 软件测试的定义与分类
  7. php7版本一句话木马,一句话查找 PHP 木马
  8. C++ Primer 读书笔记 - 第二章
  9. 上次被人说TK不好咯,这次给你整个高大上的
  10. linux下c爬取天气的源码,一个在conky中实现获取本地天气的c源代码
  11. WEB前端超多知识总结
  12. 64位计算机安装xp,练习u盘如何安装XP 64位系统
  13. f1c100A运行linux,荔枝派 Nano 全志 F1C100s 编译运行 Linux 笔记
  14. PPT设置自动保存时间 mac_你对着PPT抓狂的那几秒,像极了爱情(1)
  15. Azide连接的不同基团Azide-NHS Ester/BDP FL/diSulfo/Pyrene
  16. [Luogu3554] Poi2013 Triumphal arch
  17. 慧荣SM2246EN主控如何进行RDT测试开卡
  18. vue引入svg图片
  19. 变压器的这些冷知识,你知道吗?
  20. 稀疏表征:稀疏字典构建

热门文章

  1. java中的值传递和引用传递的区别?
  2. python零基础教程总结30天
  3. python 实现三维平面拟合 附纸质版最小二乘法的推导与程序的实现
  4. Java通过-jni调用c语言
  5. python pip如何upgrade
  6. UE5/C++ 基于GAS的怪物AI 6.2.1 AIController中寻敌方法
  7. 旧的机器装了XP以后频繁死机
  8. 雪碧图/精灵图使用教程(CSS Sprites)
  9. 作为一名程序员我不忘初心,在线面试指南
  10. 新唐MS51FB9AE规格书ms51fb9ae手册N76E003AT20和MS51FB9AE单片机MS51FB9AE8s003f3p6是什么芯片ms51fb9ae烧录新唐MS51FB9AE