双向链表所需要头文件

首先重定义类型名 意义我前几篇讲过几次了,这里就不在赘述了,(顺序表,单链表的开头都有说明)

然后我们需要一个结构体

结构体包含 : 存储数据的 a 指向一个节点的指针 next 指向上一个节点的指针 prve

双向链表实现的函数功能有 :1.申请动态内存空间 2.初始化 3.双向链表的头,尾的插入与删除,在合法的任意位置进行插入和删除 4.释放链表,5.打印链表

1.申请动态内存空间

因为进行插入和初始化需要用到此函数 所以我们要先实现此功能

1.先申请一个动态内存,并名为newnode

2.把x存入newnode存放数据的a中

3.把newnode的上,下指针都置为空

4.最后再返回此申请的空间

2.初始化

我们先创建一个链表的头 并且把上,下指针置空,并且返回链表的头

并且我们再命名一个plist,用来接收,之后穿参数的时候,就可以直接用pilst

 3.双向链表的尾部插入

1.先创建一个指针 指向头的一个节点

2.申请一块内存 用来插入

因为双向链表的优势 ,我们不需要像单链表那样依次遍历找到最后一个节点,双向链表,表头,的上一个就是链表的最后一个节点

所以我们可以一步就找到表尾

尾插入原理:

找到表尾后,1.在表尾后再插入一个新数据(用next指针指向newnode(新数据)),2.把新数据指向原先的表尾(用prve指向原先的表尾)就完成了一次的连接

1.再让现在的表尾 (就是newnode(新数据))指向表头(newnode->next=phead(表头))  2.(再让表头指向上一个的指针(就是prve)指向newnode(新数据))

这里可能会有点绕,需要读者慢慢理解,但是原理是简单的 ,就是让1个数据与它的上一个数据和下一个数据进行对接

4.双向链表的尾部删除

原理:

1.找到表尾,再找到表尾的上一个数据(倒数第二个数据)

2.然后让倒数第二个数据与表头对接,倒数第二个数据就成为了表尾数据,

3.再把原先的表尾数据进行释放

end就是最后一个节点,cur就是倒数第二个节点

1.让cur指向下一个的指针指向表头 

2.把表头的指向上一个的指针指向cur

3.释放原先的表尾,并且把指针置空

读者要搞清楚 next 和prve的作用 再搞清楚end和cur的位置,最后运用他们进行对接

5.双向链表的头部插入

我们先找到两个位置 一个是表头 一个是表头的下一个节点(这个节点才是真正存放数据的节点,表头只是其带头作用,里面只是存放0,并且打印链表都是从表头的一个节点开始打印

找到两个位置之后,创建一个动态内存 进行插入

接下来大致原理就是 让插入的数据与表头和表头的下一个节点,在他们两个中间插入(就是对接)

表头原先指向的是表头的下一个节点1.现在我们让表头的指向下一个节点的指针指向插入的数据

 2.让插入的数据指向上一个节点的指针指向表头  3.让插入的数据指向下一个节点的指针指向原先表头的下一个节点(进行对接)

6.双向链表的头部删除

头部删除也是删除表头的下一个节点

那么我们要找到两个位置 1.表头,2.表头的下一个节点的下一个节点(就是要删除的位置的下一个节点)

这里我们把表头命名为cur 把表头的下一个节点的下一个节点(就是要删除的位置的下一个节点)命名为end

1.然后我们让 cur和end进行对接

2.然后释放要删除数据

把cur指向下一个节点的指向指向end 把end指向上一个节点的指针指向cur

7.双向链表的查找

这里我们只需要从表头的下一个节点开始,一直到表尾,进行遍历,如果找到就返回改位置。如果知道表尾都没找到(就是循环结束的时候)那么我们就返回空(NULL)

然后我们用pos来接收

 7.对pos的前一个位置进行插入数据

我们首先需要先对pos进行判断,如果找到了,才开始插入,若找不到,就不执行函数

因为是插入数据 所以我们要先申请一个内存

又因为我们已经得到了pos的位置,所以我们可以直接找到pos的上一个数据

原理在于找两个位置 一个是pos 一个是pos的上一个数据

pos我们已经有了它的位置 pos的上一个位置我们只需要用指针指向它就可以了

pos的上一个位置命名为end

然后让新数据在pos的上一个数据与pos中间进行插入(就是对接)

8.对pos这个位置进行删除

因为我们有了pos的位置

那么我们用指针 next和prve找到 pos的上一个位置(命名为cur),和pos的下一个位置(end)进行对接

然后再释放掉pos,为把pos置空

 9.链表的打印

从表头的下一个数据开始,进行遍历,并依次打印

10.链表的释放 

因为链表的内存是一块块申请的,所以我们要遍历,并且释放

先从找到表头的下一个节点(命名为end),再找到表头的下一个节点的下一个节点(命名为cur)

先释放表头的下一个节点,再把cur赋值给end,就此循环。就把表头以后的数据全释放完了,

最后把表头给释放,并且置空,就可以了。

双向链表的全部函数功能就实现好了!

这里添加一个注释

之所以双向链表传参不用(&)取地址 是因为SL* plist=ListCreate(); 这里是用指针接收,指针接收的就是地址,我们传参数的时候就是用地址传入了。

不用二级指针是因为我们不需要改变这个双向链表的哨兵位,只是改变它的指向,这个哨兵位是不会变的(不管头插、尾插、头删、尾删)

而单链表的头插,头删是会改变这个头的,比如(头删)释放这个头,让下一个节点成为头,这些都是会改变头的,所以我们要传二级指针。

接下来是代码段 ,需要自取。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int STDataType;typedef struct linode
{STDataType a;struct linode* next;struct linode* prve;
}SL;SL* Bylist(STDataType x)//申请动态内存
{SL* newnode = (SL*)malloc(sizeof(SL));newnode->a = x;newnode->next = NULL;newnode->prve = NULL;return newnode;
}SL* ListCreate()//初始化
{SL* phead = Bylist(0);phead->next = phead;phead->prve = phead;return phead;
}void ListDestory(SL* pHead)//释放链表
{SL* end = pHead->next;while (end != pHead){SL* cur = end->next;free(end);end = cur;}free(pHead);pHead = NULL;
}void ListPrint(SL* pHead)//打印链表
{SL* end = pHead->next;while (end != pHead){printf("%d->", end->a);end = end->next;}printf("phead");
}void ListPushBack(SL* pHead, STDataType x)//尾插
{SL* end = pHead->prve;SL* newnode = Bylist(x);//因为双向链表的优势 所以我们不需要依次遍历,头的上一个就是最后一个节点end->next = newnode;newnode->prve = end;newnode->next = pHead;pHead->prve = newnode;
}void ListPopBack(SL* pHead)//尾删
{SL* end = pHead->prve;//找到最后一个节点SL* cur = end->prve;//最后一个节点的上一个cur->next = pHead;pHead->prve = cur;free(end);end = NULL;
}void ListPushFront(SL* pHead, STDataType x)//头插
{SL* end = pHead;SL* cur = end->next;SL* newnode = Bylist(x);end->next = newnode;newnode->prve = end;newnode->next = cur;cur->prve = newnode;
}void ListPopFront(SL* pHead)//头删
{SL* cur = pHead->next;SL* end = cur->next;pHead->next = end;end->prve = pHead;free(cur);
}SL* ListFind(SL* pHead, STDataType x)//查找
{SL* cur = pHead->next;while (cur != pHead){if (cur->a == x){return cur;}cur = cur->next;}return NULL;
}void ListInsert(SL* pos, STDataType x)//插入
{SL* newnode = Bylist(x);SL* end = pos->prve;end->next = newnode;newnode->prve = end;newnode->next = pos;pos->prve = newnode;
}void ListErase(SL* pos)//删除
{SL* cur = pos->prve;SL* end = pos->next;cur->next = end;end->prve = cur;free(pos);pos - NULL;
}void Intenode()
{// 创建返回链表的头结点.SL* plist= ListCreate();//   双向链表尾插ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);//    双向链表尾删ListPopBack(plist);//     双向链表头插ListPushFront(plist, 5);ListPushFront(plist, 6);ListPushFront(plist, 7);ListPushFront(plist, 8);//    双向链表头删ListPopFront(plist);//    双向链表查找SL* pos=ListFind(plist, 5);if (pos){// 双向链表在pos的前面进行插入ListInsert(pos, 50);}//    双向链表删除pos位置的节点ListErase(pos);//    // 双向链表打印ListPrint(plist);//    // 双向链表销毁ListDestory(plist);
}

C语言---双向链表(详解)---数据结构相关推荐

  1. 代码检查规则:Java语言案例详解

    本节课程为<代码检查规则:Java语言案例详解>, 通常情况下Java的代码检查规则可以分为以下十类: 接下来,让我们具体来看看每个分类的内容. 一.源文件规范 该类规范主要从文件名.文件 ...

  2. Go语言slice详解

    Go语言slice详解 Go语言中的slice表示一个具有相同类型元素的可变长序列,语言本身提供了两个操作方法: 创建:make([]T,len,cap) 追加: append(slice, T -) ...

  3. R语言——数据类型详解

    R语言--数据类型详解 R语言支持的数据类型 数值型 整数型 逻辑型 字符型 复数型 原生型 R语言的数据对象类型包括 向量:一个向量只能有一种数据类型 矩阵:一个矩阵只能有一种数据类型 数组:一个数 ...

  4. python语言编程基础-Python语言入门详解!快速学成Python!

    原标题:Python语言入门详解!快速学成Python! 很多技能是被职场所需要的,但很可惜... 这些技能在大学中并学习不到. 大学和职场现实存在的横沟对大部分同学来说难以跨越或碰得头破血流... ...

  5. python语言入门m-Python语言入门详解!快速学成Python!

    今日主题 "Python语言入门详解" 近两年来,Python语言借着数据科学和人工智能的"东风"成为了最流行的编程语言--街头巷尾人们口口相传.同时,Pyth ...

  6. C语言之详解#ifdef等宏

    C语言之详解#ifdef等宏 这几个宏是为了进行条件编译.一般情况下,源程序中所有的行都参加编译.但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是&qu ...

  7. c语言 宏教程 pdf,C语言之详解_ifdef等宏.pdf

    C 语言之详解 #ifdef 等宏 指令用途 #空指令,无任何效果 #include 包含一个源代码文件 #define 定义宏 #undef 取消已定义的宏 #if 如果给定条件为真,则编译下面代码 ...

  8. 代码检查规则:Python语言案例详解

    在之前的文章中代码检查规则:Java语言案例详解学习了Java的检查规则.我们今天将学习<代码检查规则:Python语言案例详解>,内容主要分为两个部分:Python的代码检查规则和Pyt ...

  9. Linux_arm_启动_c语言部分详解,[原创]Linux arm 启动 c语言部分详解第四讲

    Linux arm启动c语言部分详解第四讲(from setup_per_cpu_areas();) Written by leeming 上面的setup_arch花了我们大量的篇幅,现在我们要继续 ...

  10. c 语言中 %是什么运算符,C 语言基础----详解C中的运算符

    C语言中又有哪些运算符呢? 如下所示: ※ 算术运算符 ※ 赋值运算符 ※ 关系运算符 ※ 逻辑运算符 ※ 三目运算符 C语言基本算术运算符如下表: 除法运算中注意: 如果相除的两个数都是整数的话,则 ...

最新文章

  1. SCCM 2012 SP1系列(十)配置补丁更新-3
  2. Ubuntu14.04安装nvidia-docker2
  3. Mongodb数据库初识
  4. linux环境切换python3版本
  5. JAVA的Future类
  6. 关于xinetd报错
  7. Halcon 学习总结——错误处理方法
  8. linux查找某个命令属于哪个rpm包
  9. java hsqldb_Java HsqlDB的初步使用和技巧总结
  10. 显示 think-cell 用户界面时出现错误
  11. 如何在MFC界面使用OCX控件
  12. 使计算机拒绝远程桌面服务,拒绝通过远程桌面服务登录
  13. PS 图片背景变为透明
  14. 文本挖掘之情感分析在网络视频弹幕的应用 ——以《都挺好》弹幕数据为例
  15. 私有云和服务器虚拟化的区别,私有云和服务器有什么区别
  16. java老王博客_老王的JAVA基础课:第5课 面向对象
  17. u盘中毒文件为html文档,u盘中毒文件被隐藏了?教你如何快速恢复隐藏文件
  18. pta 03-树1 树的同构 SDUT 3340 数据结构实验之二叉树一:树的同构
  19. 使用微PE安装U盘windows系统
  20. Android高版本上传图片出现旋转问题(三星手机,小米8)

热门文章

  1. 基于jsp、ssm在线考试系统
  2. pc端常见的几种布局:分栏布局,通栏布局,版心布局,版心布局
  3. 医疗机构人脸识别测温健康码防控系统如何部署?
  4. 可见光通信!触摸6G科技,玩转光联万物
  5. 工银e生活开发脱坑日志(4)工行页面及jsAPI交互接口hybrid_app.js登录情况说明
  6. 爬虫神器之selenium的使用
  7. layui表格数据的批量操作
  8. python之类的静态方法
  9. Android FCM onMessageReceived的运行时机总结
  10. 【JavaScript 教程】浏览器—window 对象