【 C 】在双链表中插入一个新值的简明程序
上两篇博文讲了如何在单链表中插入一个值:
【 C 】在单链表中插入一个新节点的尝试(一)
【 C 】在单链表中插入一个新节点的尝试(二)
这篇博文讲解如何在双链表中插入一个值。
单链表的替代方案就是双链表。在一个双链表中,每个节点都包含两个指针——指向前一个节点的指针和指向后一个节点的指针。这可以使我们以任何方向遍历双链表,甚至可以忽前忽后地在双链表中访问。下图展示了一个双链表:
下面是节点类型的声明:
typedef struct NODE
{struct NODE *fwd;struct NODE *bwd;int value;
} Node;
现在,存在两个根指针:一个指向链表的第一个节点,另一个指向链表的最后一个节点。这两个指针允许我们从链表的任何一端开始遍历链表。
我们可能想把两个根指针分开声明为两个变量。但这样一来,我们必须把两个指针都传递给插入函数。为根指针声明一个完整的节点更为方便,(下面的例子也是这么做的),只是它的值字段绝不会被使用。在我们的例子中,这个技巧只是浪费了一个整型值的内存空间。对于值字段非常大的链表,分开声明两个指针可能更好一些。另外,我们也可以在根节点的值字段中保存其他一些关于链表的信息,例如链表当前包含的节点数量。
根节点的fwd字段指向链表的第一个节点,根节点的bwd字段指向链表的最后一个节点。如果链表为空,这两个字段都为NULL。链表的第一个节点的bwd字段和最后一个节点的fwd字段都为NULL。在一个有序的链表中,各个节点将根据value字段的值以升序排列。
下面,我们编写一个函数,把一个值插入一个有序的双链表中。dll_insert函数接受两个参数:一个指向根节点的指针和一个需要插入的整型值。
开头提到的两个博文,在单链表中插入函数把重复的值也添加到链表中。在有些应用程序中,不插入重复的值可能更为合适。下面在双链表中插入一个值就是这种情况,dll_insert函数只有当欲插入的值原先不存在于链表中时才将其插入!
让我们以一种更为规范的方法来编写这个函数。当我们把一个节点插入一个链表中时,可能出现4中情况:
- 新值可能必须插入到链表的中间位置;
- 新值可能必须插入到链表的起始位置;
- 新值可能必须插入到链表的结束位置;
- 新值可能既插入到链表的起始位置又插入到链表的结束位置。
在情况1和2,新节点的fwd字段必须设置为指向链表的下一个节点的指针,链表的下一个节点的bwd字段必须设置为指向这个新节点。
在情况3和4,新节点的fwd字段必须设置为NULL,根节点的bwd字段必须设置为指向新节点。
在情况1和3,新节点的bwd字段必须设置为指向链表的前一个节点,而链表前一个节点的fwd字段必须设置为指向新节点 。
在情况2和4,新节点的bwd字段必须设置为NULL,根节点的fwd字段必须设置为指向新节点。
为了让认识更为清晰,我画了一个示意图,示意双链表节点之间的情况:
下面的程序就是根据这来设计的,认真体会,或许更为清晰:
程序:
//简明的双链表插入函数
//把一个值插入到一个双链表中,rootp是一个指向根节点的指针,value是要插入的新值
//返回值:如果欲插值原先已经存在与链表中,函数返回0
//如果内存不足导致无法插入,返回返回-1;如果插入成功,函数返回1#include < stdlib.h>
#include < stdio.h >
#include < dll_node.h > //这是自己建立的一个头文件,用于保存双链表的节点声明int dll_insert( Node *rootp, int new_value )
{Node *this; //指向新节点之前的那个节点Node *next; //指向新节点之后的那个节点Node *newnode; //指向新节点本身//查看value是否已经存在于链表中,如果是就返回。//否则,为新值创建一个新的节点(newnode将指向它)for( this = rootp; ( next = this->fwd ) != NULL; this = next ){if( next->value == new_value ) //如果插入的新值在链表中已经存在,则返回0,不需要插入return 0;if( next->value > new_value ) //如果插入的新值小于当前节点的值,则直接跳出循环,也就是需要在该节点之前插入新值break;//如果上面两种情况都不是,则插入的新值大于当前节点的值,继续循环}//为新节点开辟内存空间newnode = ( Node*)malloc( sizeof( Node ) );if( newnode == NULL )return -1; //如果内存分配不成功,则返回 -1;newnode->value = new_value; //为新节点赋新值if( next != NULL ) //这个条件的意思是新节点并非位于链表尾部{if(this != rootp ) //新节点并非位于链表起始位置{newnode->fwd = next;this->fwd = newnode;newnode->bwd = this;next->bwd = newnode;}else //新节点位于链表起始位置{newnode->fwd = next;rootp->fwd = newnode;newnode->bwd = NULL;next->bwd = newnode;}}else //新节点位于链表的尾部{if( this != rootp ) //新节点并非位于链表的起始位置(链表不为空){newnode->fwd = NULL;this->fwd = newnode;newnode->bwd = this;rootp->bwd = newnode;}else{//新节点既位于起始位置,又位于尾部,也就是说原链表为空newnode->fwd = NULL;rootp->fwd = newnode;newnode->bwd = NULL;rootp->bwd = newnode;}}return 1;}
一开始,函数使 this 指向根节点。next 指针始终指向 this 之后的那个节点。它的思路是这两个指针同步前进,直到新节点应该插入这两者之间。for循环检查next所指向的节点的值,判断是否到达需要插入的位置。
如果在链表中找到新值,也就是说链表中本身就存在需要插入的新值:
if( next->value == new_value ) //如果插入的新值在链表中已经存在,则返回0,不需要插入return 0;
那么就简单的返回。否则,当到达链表尾部(
( next = this->fwd ) == NULL
)或者找到适当的插入位置:
if( next->value > new_value ) //如果插入的新值小于当前节点的值,则直接跳出循环,也就是需要在该节点之前插入新值break;
循环终止。
在任何一种情况下,新节点都应该插入到 this 指向的节点的后面。注意,在我们决定新值是否应该实际插入到链表之前,并不为它分配内存。如果事先分配内存,如果发现新值原先已经存在于链表中,就有可能发生内存泄露。
当我们把12插入到链表中时,观察下情况:下面这张图显示了for循环终止之后几个变量的状态。
然后,函数为新节点分配内存,下面几条语句执行之后,
newnode->fwd = next;
this->fwd = newnode;
链表的样子如下:
然后,执行下列语句:
newnode->bwd = this;
next->bwd = newnode;
这就完成了把新值插入到链表中的过程:
【 C 】在双链表中插入一个新值的简明程序相关推荐
- 【 C 】简化双链表插入函数(对在双链表中插入一个新值的简明程序的简化)
目录 背景 第一个技巧是语句提炼(statement factoring) 第二个简化技巧 最终简化版本 背景 上篇博文:[ C ]在双链表中插入一个新值的简明程序,讲了一个简明的双链表插入函数,那个 ...
- 【 C 】在单链表中插入一个新节点的尝试(二)
在上篇博文中:[ C ]在单链表中插入一个新节点的尝试(一),我们最后提到了如果向单链表的开头(起始位置)插入一个节点,上篇博文中给出的程序显然完成不了这任务. 这篇博文中,我们将解决这个问题,给出一 ...
- 【 C 】在单链表中插入一个新节点的尝试(一)
根据<C和指针>中讲解链表的知识,记录最终写一个在单链表中插入一个新节点的函数的过程,这个分析过程十分的有趣,准备了两篇博文,用于记录这个过程. 链表是以结构体和指针为基础的,所以结构体和 ...
- 数据结构例16.试设计一个算法, 使得在一个有序的单链表中插入一个元素后仍然有序。
/* 16.试设计一个算法, 使得在一个有序的单链表中插入一个元素后仍然有序. */ # include <iostream> # include <stdlib.h> # i ...
- 双链表中插入节点(C语言实现)
文章目录 1. 相关背景介绍 1.1 双链表概念 1.2 双链表的优势与劣势 1.3 双链表插入节点的位置 2. 不同位置插入数据 2.1 在DLL的前端添加节点 2.2 在给定节点之前添加节点 2. ...
- 链表相关操作:创建链表、遍历链表、求链表长度、链表中删除一个节点、链表中插入一个节点、反转单链表...
1 #include<iostream> 2 #include<stdlib.h> 3 4 typedef struct node 5 { 6 int data; 7 stru ...
- C++ 单链表基本操作分析与实现 链表 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结
C++ 单链表基本操作分析与实现 链表 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以 ...
- 链表问题2——在双链表中删除倒数第K个节点
题目 实现一个函数,可以删除双链表中倒数第K个节点. 要求 如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1). 思路 双链表的思路与前一篇文章单链表的思路基本一致,注意last指针 ...
- 如何在Java中创建一个新的List
本文翻译自:How to make a new List in Java We create a Set as: 我们创建一个Set为: Set myset = new HashSet() How d ...
最新文章
- TensorFlow AI 新品更易用!联手NVIDIA,支持Swift和JavaScript
- 熬夜变傻有科学依据,人类睡觉时会被“洗脑”,科学家首次拍下全程
- Delphi XE5 常见问题解答
- 路由器中的管理间距和量度参数
- 再谈fedora下的音乐和视频播放器的安装
- 更新KB915597补丁后导致“您的windows副本不是正版”的解决方案
- 使用Gradle引导旧式Ant构建
- 关于 springcloud gateway 设置 context-path 的问题
- Oracle中“不等于”的使用
- Keil 中的预处理命令const
- windowswps怎么以文件形式发送_wps怎么以文件形式分享
- E9启动后无法打印日志
- 詹姆斯——永远的皇帝
- JetBrains下载历史版本(IDEA、PyCharm、WebStorm、PhpStorm等)
- 假设检验中两类错误及最小样本量计算
- ZYNQ之AXI简介
- 解读运营指标:DAU/MAU
- uniapp设置百度小程序索引页dynamicLib、usingComponents、swan-sitemap-list
- 挖个冰块就能修自己!用「冰」做的科考机器车
- 西游记中会七十二变的三个人
热门文章
- python flask 教程_Flask 教程 第一章:Hello, World!
- 华为java安全编码规范_Java安全编码之SQL注入
- proto文件支持继承吗_搞懂 Javascript中this 指向及继承原理
- linux7开放3306端口,CentOS 7 开放3306端口访问
- SQL之 Stuff和For xml path
- clob存base64文件存不进去_Kafka 和 RocketMQ 底层存储之那些你不知道的事
- python的迭代器for_python特性(二):迭代器与for语句
- linux系统core dump设置,linux coredump设置
- 从0 开始 DIY你的Arduino UNO
- 交叉熵损失函数公式_交叉熵损失函数对其参数求导