双向链表的实现(增删查改)——最好理解的链表

双向链表的实现

  • 一,双向链表的特点
  • 二,双向链表的结构
  • 三,双向链表的内容实现
    • 3.1创建node节点
    • 3.2初始化
    • 3.3打印
    • 3.4插入
      • 3.4.1尾插
      • 3.4.2头插
      • 3.4.3在pos位置上插入
    • 3.5删除
      • 3.5.1尾删
      • 3.5.2头删
      • 3.5.3删除pos位置上的数据
  • 四,调试技巧(具体示例)
  • 五,总结

一,双向链表的特点

这里的双向链表就是单链表的升级版,链表有的共性他也有感兴趣的可以先看看单链表的内容链接: link

二,双向链表的结构

typedef int LTDataType;
typedef struct ListNode
{struct ListNode* prev;LTDataType date;struct ListNode* next;
}ListNode;

这里还是老生常谈,在写比较大一些的代码的时候我们还是要重新定义一下数据类型这样方便我们以后可以更方便的使用各种数据类型。
至于双向链表,就是它有两个指针既可以指向前面的数据也可以指向后面的数据,所以这里定义的时候就分成了前指针prev和后指针next,这里贴一张图方便理解双向链表。
在这里插入图片描述

三,双向链表的内容实现

// 创建返回链表的头结点.
ListNode* BuyLTNode(LTDataType x);
//初始化
ListNode* LTInit();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

这里还要提醒一下,我们在写代码对我们的函数命名的时候尽量是按照它对应的英文单词取进行命名,这样有助于提升我们的代码质量和可读性。

3.1创建node节点

//创建节点
ListNode* BuyLTNode(LTDataType x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("malloc failed");exit(-1);}node->prev = NULL;node->date = x;node->next = NULL;return node;
}

3.2初始化

//初始化
ListNode* LTInit()
{ListNode* phead = BuyLTNode(0);phead->next = phead;phead->prev = phead;return phead;
}

初始化是所有的开始,我们前面的顺序表,单链表都有进行初始化,后边我们要学习的栈和队列也是要有初始化的。

3.3打印

//打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;printf("pHead<->");while (cur != pHead){printf("%d<->", cur->date);cur = cur->next;}printf("\n");
}

打印这里是为了方便我们进行调试代码的。

3.4插入

这里我们的双向链表的插入一共分为了三种插入分为是尾插,头插,以及在特定位置下插入

3.4.1尾插

//尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{ListNode* newnode = BuyLTNode(x);ListNode* tail = pHead->prev;newnode->prev = tail;tail->next = newnode;newnode->next = pHead;pHead->prev = newnode;
}

在这里插入图片描述
这里我们需要理解原本d3作为尾它的next是指向d1的,而d1的prev是指向d3的所以我们先记录下d1的prev的数据然后再把newnode的prev指向d3也就是tail,然后tail的next就指向的newnode,接着newnode的next就指向的头,头的prev就指向了newnode的尾。

3.4.2头插

//头插
void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = BuyLTNode(x);newnode->next = pHead->next;pHead->next->prev = newnode;pHead->next = newnode;newnode->prev = pHead;
}

在这里插入图片描述

我们还是画图分析,因为是头插,我们newnode的next就指向了phead的下面一个位置也就是d2,接着d2的prev就要指向newnode,头的next的位置就指向了newnode,newnode的prev就指向了头。

3.4.3在pos位置上插入

//在pos前面插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* prevpos = pos->prev;ListNode* newnode = BuyLTNode(x);newnode->next = pos;pos->prev = newnode;newnode->prev = prevpos;prevpos->next = newnode;
}

这里我们就不具体分析了,我们重点说一下这里的技巧,在双向链表的插入中我们可以看到其实头插和尾插都是在指定位置上插入的特例,所以我们在写双向链表的插入的时候完全可以先写这个函数然后头插和尾插也就完成了,这里我们展示一下改造后的头尾插。

//尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{ListInsert(pHead->prev, x);
}
//头插
void ListPushFront(ListNode* pHead, LTDataType x)
{ListInsert(pHead->next, x);
}

怎么样是不是非常的方便,那么举一反三我们在后面的删除中叶有这样的技巧。

3.5删除

3.5.1尾删

//尾删
void ListPopBack(ListNode* pHead)
{assert(pHead);assert(pHead->next);ListNode* tail = pHead->prev;ListNode* prevtail = tail->prev;prevtail->next = pHead;pHead->prev = prevtail;free(tail);
}

在这里插入图片描述
看图分析,我们删除了tail,记录前一个的位置也就是prevtail,让prevtail和phead再次变成循环就可以了。

3.5.2头删

//头删
void ListPopFront(ListNode* pHead)
{assert(pHead);assert(pHead->next);ListNode* first = pHead->next;ListNode* second = first->next;free(first);second->prev = pHead;pHead->next = second;
}

头删也时同样的记录下一个的数据在跟前面链接free掉第一个数据。

3.5.3删除pos位置上的数据

//删除pos的数据
void ListErase(ListNode* pos)
{assert(pos);ListNode* nextpos = pos->next;ListNode* prevpos = pos->prev;free(pos);prevpos->next = nextpos;nextpos->prev = prevpos;
}

同样的,尾删和头删也是包括在这一种删除中的,所以我们也可以进一步简化我们的代码。

//尾删
void ListPopBack(ListNode* pHead)
{ListErase(pHead->prev);
}
//头删
void ListPopFront(ListNode* pHead)
{ListErase(pHead->next);
}

四,调试技巧(具体示例)

我们在写大型的代码的时候有一种调试方法可以极大的简便的让我们去检查代码就是写一查一
例如我们写了尾插的代码。

void Text1()
{ListNode* plist = LTInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);ListPrint(plist);
}
int main()
{Text1();return 0;
}

在这里插入图片描述

我们就可以用Text1去检查我们的尾插有没有错误。
同样的我们写完头插之后再写一个Text2去检查我们的函数有没有错误。把一个复杂的大型代码去分解为一个个小的单元会让我们的效率提升很多。

五,总结

双向链表虽然是单链表之后的内容,但是我们会发现因为有两个指针的原因他的所有的操作比单链表比起来更加的方便,更易于理解。总之,还是要自己上手操作才能加深自己的印象。数据结构重点就是:画图!!画图!!画图!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/140008.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html页面仿word文档样式(vue页面也适用)

目录 文章title&#xff1a; 标题&#xff1a; 正文&#xff1a; 完整代码&#xff1a; 页面效果&#xff1a; 文章title&#xff1a; <div><h3 style"display: flex;justify-content: center; align-items: center; color: #000;">实验室招新报名公…

【100天精通Python】Day69:Python可视化_实战:导航定位中预测轨迹和实际轨迹的3D动画,示例+代码

目录 1. 预测的3D轨迹和实际轨迹的动画图&#xff0c;同时动态更新 2 真值轨迹设置为静态的&#xff0c;预测轨迹不断更新 3 网格的三维坐标系有旋转运动&#xff0c;以此全方位展示预测轨迹和真值轨迹之间的空间关系 1. 预测的3D轨迹和实际轨迹的动画图&#xff0c;同时动态更…

Nginx 防止跨站脚本 Cross-Site Scripting (XSS)

1、修改 nginx 配置 在 nginx.conf 配置文件中&#xff0c;增加如下配置内容&#xff1a; add_header X-XSS-Protection "1; modeblock";X-XSS-Protection 的字段有三个可选配置值&#xff0c;说明如下&#xff1a; 0&#xff1a; 表示关闭浏览器的XSS防护机制&…

ad18学习笔记十一:显示和隐藏网络、铺铜

如何显示和隐藏网络&#xff1f; Altium Designer--如何快速查看PCB网络布线_ad原理图查看某一网络的走线_辉_0527的博客-CSDN博客 AD19(Altium Designer)如何显示和隐藏网络 如何显示和隐藏铺铜&#xff1f; Altium Designer 20在PCB中显示或隐藏每层铺铜-百度经验 AD打开与…

怎么将自己的Maven项目上传到Maven中央仓库/Maven阿里云云效仓库

前言 对于工作了多年的老程序员来说&#xff0c;往往会总结出一些比较好用的开发工具包。那么如果把这些好的工具插件共享出来供大家快速的使用呢&#xff0c;最好的方式就是将这些工具插件上传到Maven中央仓库/Maven阿里云云效仓库&#xff0c;这样&#xff0c;有需要用到这些…

八大排序(一)冒泡排序,选择排序,插入排序,希尔排序

一、冒泡排序 冒泡排序的原理是&#xff1a;从左到右&#xff0c;相邻元素进行比较。每次比较一轮&#xff0c;就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。 以从小到大排序为例&#xff0c;第一轮比较后&#xff0c;所有数中最大的那个数就会浮…

verilog学习笔记(1)module实例化

兜兜转转又回来学硬件了&#xff0c;哎&#xff0c;命啊&#xff01; 我的答案&#xff08;有bug&#xff09;&#xff1a; module top_module ( input a, input b, output out );wire w1;wire w2;wire w3;mod_a mod_a_inst1(.in1(w1),.in2(w2),.out(w3) );assign w1 a…

基于微信小程序的房屋租赁系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信小程序端的主要功能有&#xff1a;户主微信小程序端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文…

解决 Github port 443 : Timed out

解决方法 打开代理页面 打开 设置 --> 网络与Internet --> 查找代理 记录下当前系统代理的 IP 地址和端口号 如上图所示&#xff0c;地址与端口号为&#xff1a;127.0.0.1:7890 注意修改成自己的IP和端口号 git config --global http.proxy http://127.0.0.1:7890 gi…

Spring面试题12:Spring中IOC的优缺点是什么?IOC依赖注入方式有哪些

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring中IOC的优缺点是什么? IOC(Inversion of Control,控制反转)是Spring框架的一个重要特性,它实现了对象的创建和依赖关系的管理的反转。…

【Linux】Linux环境配置安装

目录 一、双系统&#xff08;特别不推荐&#xff09; 安装双系统的缺点&#xff1a; 安装双系统优点&#xff08;仅限老手&#xff09;&#xff1a; 二、虚拟机centos7镜像&#xff08;较为推荐推荐&#xff09; 虚拟机的优点&#xff1a; 虚拟机的缺点&#xff1a; ​ …

6年Android开发前10月的总结,写给正在求职的安卓开发

进入大厂工作对许多人来说已经是一种挑战&#xff0c;但只要充分准备&#xff0c;很多问题都可以逐步解决。当然&#xff0c;运气也起到了一定的作用&#xff0c;但最终还是与自身的努力密不可分。运气是实力的一部分&#xff0c;因为自助者天助。 每到10月进行总结时&#xff…

数据结构与算法(二)

文章目录 数据结构与算法(二)1 时间复杂度、空间复杂度、排序算法和二分法1.1 简单的排序算法1.2 二分查找2 异或运算、进一步认识对数器的重要性2.1 不用额外变量交换两个数的值2.2 不用额外变量交换数组中两个数的值2.3 一个数组中有一种数出现了奇数次,其他数都出现了偶数…

读写分离MySQL

利用Mycat控制后台数据库的读写分离和负载均衡 利用主从复制思想,实现读写分离,主库写,从库读 从库最好不要写,因为从库写入的数据不能同步到主库,只有主库写的数据才能同步到从库 balance属性值对应的含义(负载均衡) 一主一从读写分离的弊端 主节点Master宕机以后,业务系统…

docker 操作redis

1查看容器 2进入容器 exec表示在运行的容器中执行命令it表示以终端交互的方式执行命令/bin/bash表示需要指定的命令 3进入容器后可通过redis-cli命令连接容器内的redis服务器&#xff0c;可通过set创建变量&#xff0c;get获取变量的值 4key * 查看所有key 通过ping 查看redi…

【深度学习实验】前馈神经网络(final):final

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 构建数据集&#xff08;IrisDataset&#xff09; 2. 构建模型&#xff08;FeedForward&#xff09; a. __init__(初始化) b. forward(前向传播) 3.整合训练、评估…

golang优先级坑

看如下代码&#xff0c;我本以为a1, a2是相同的 package mainimport "fmt"func main() {b, c, d : 1, 0, 1a1 : b ^ c&(^d) // 1 ^a2 : c ^ b&(^d) // 0 ^fmt.Println(a1, a2) // 1 0 }但结果却是不同的&#xff0c;在golang中&的优先级^和&#xff5c;…

罗德里格斯公式

1.点乘 A ⃗ ⋅ B ⃗ ∣ A ⃗ ∣ ∣ B ⃗ ∣ c o s ⟨ A ⃗ , B ⃗ ⟩ \vec{A} \cdot \vec{B} \left | \vec{A} \right | \left | \vec{B} \right | cos\left \langle \vec{A}, \vec{B} \right \rangle A ⋅B ​A ​ ​B ​cos⟨A ,B ⟩ 对应几何意义&#xff1a;向量 A ⃗…

众佰诚:抖音店铺开网店前期需要投入多少

随着互联网的迅猛发展&#xff0c;电子商务已经成为了商业领域中的一股不可忽视的力量。而在电子商务中&#xff0c;抖音店铺已经成为了一个备受关注的平台&#xff0c;吸引了众多创业者和商家的关注。那么&#xff0c;在开设抖音店铺并转型为网店之前&#xff0c;究竟需要投入…

SVN的基本使用

一、SVN介绍 SVN&#xff08;Subversion&#xff09;是一个开源的版本控制系统&#xff0c;它专门用于管理文件和目录的变更。SVN 提供了一种集中式的版本控制方案&#xff0c;其中有一个中央仓库存储所有文件的历史记录和变更。 SVN使用方式相对简单&#xff0c;可以通过命令…