当前位置: 首页 > news >正文

链表中插入新的节点

/* 节点结构体定义 */
struct xLIST_ITEM
{TickType_t xItemValue;             /* 辅助值,用于帮助节点做顺序排列 */			struct xLIST_ITEM *  pxNext;       /* 指向链表下一个节点 */		struct xLIST_ITEM *  pxPrevious;   /* 指向链表前一个节点 */	void * pvOwner;					   /* 指向拥有该节点的内核对象,通常是TCB */void *  pvContainer;		       /* 指向该节点所在的链表 */
};
typedef struct xLIST_ITEM ListItem_t;  /* 节点数据类型重定义 */
/* 链表结构体定义 */
typedef struct xLIST
{UBaseType_t    uxNumberOfItems;    /* 链表节点计数器 */ListItem_t *   pxIndex;			/* 链表节点索引指针 */MiniListItem_t xListEnd;		/* 链表最后一个节点 */
} List_t;
/* 将节点插入到链表的尾部 */
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{ListItem_t * const pxIndex = pxList->pxIndex;pxNewListItem->pxNext = pxIndex;                        //1pxNewListItem->pxPrevious = pxIndex->pxPrevious;        //2pxIndex->pxPrevious->pxNext = pxNewListItem;            //3pxIndex->pxPrevious = pxNewListItem;                    //4/* 记住该节点所在的链表 */pxNewListItem->pvContainer = ( void * ) pxList;         //5/* 链表节点计数器++ */( pxList->uxNumberOfItems )++;                          //6
}

学习过“野火”freeRTOS的童鞋们一定会对这些代码眼熟,我刚学,只是发表一下个人的观点和见解,大神可以直接跳过,写的理解可能只适用于刚接触链表有关知识点的小伙伴,不敢说通俗易懂,但是对于我本身这种菜鸟很容易接受和理解。重点讲解一下第三段代码的逻辑!!!!不喜欢可以敬请喷,我会及时当勉励的。

初始链表结构

假设链表当前结构如下(以便于理解,假设链表已有两个有效节点 AB),并且 pxIndex 是伪头节点,它指向链表的末尾:

NULL <- A <-> B <-> pxIndex -> NULL

我们可以将链表节点看成是互相握手的小人,他们按照一定的顺序排列。我们一步步看看新小人 C 是如何加入到一群已经排好队的小人 AB 和尾巴节点 pxIndex 中的。

初始状态

假设我们有一个双向链表队伍,它的排列如下:

A <-> B <-> pxIndex

这里的每个箭头 <-> 表示一个节点和它相邻节点之间的双向关系。pxIndex 可以看作是一个站在队伍最后的标记节点,不是真正的队伍成员。

  • A:队伍中的第一个小人,没有人站在他前面。
  • B:A 后面的一个小人。
  • pxIndex:一个标记队伍末尾的小人,没有其他人站在他后面。

此时,队伍中的人数为 2(A 和 B),由变量 pxList->uxNumberOfItems 记录。

目标

现在,我们想让小人 C 加入队伍,站在 BpxIndex 之间。最终目标是让队伍变成:

A <-> B <-> C <-> pxIndex

插入过程一步步拆解

1. 让 C 知道他后面是谁

代码的第一步是让 CpxNext 指针指向 pxIndex,也就是告诉 C 站在他后面的是 pxIndex

pxNewListItem->pxNext = pxIndex;

现在 CpxNext 指针指向了 pxIndex,表示他知道了自己后面是队伍的尾巴节点。

A <-> B     C -> pxIndex
2. 让 C 知道他前面是谁

接下来,我们需要告诉 C 站在他前面的是 B。我们通过 pxIndex->pxPrevious 找到 B,然后把 B 的地址赋给 CpxPrevious 指针:

pxNewListItem->pxPrevious = pxIndex->pxPrevious;

现在 CpxPrevious 指针指向了 B,表示 C 知道了他前面是谁。

A <-> B <-> C -> pxIndex
3. 让 B 知道他后面多了一个人

在队伍中,双向链表是彼此双向连接的。所以我们还需要告诉 B,让他知道后面站了一个新小人 C。我们通过 pxIndex->pxPrevious->pxNext 找到 BpxNext,然后把 C 的地址赋给它:

pxIndex->pxPrevious->pxNext = pxNewListItem;

这样 BpxNext 指针指向了 C,表示 B 知道他后面站了一个新成员 C

A <-> B <-> C -> pxIndex
4. 更新队尾指针,表示 C 是最后一个人

队尾指针 pxIndex->pxPrevious 需要指向新插入的节点 C,表示队伍的最后一个有效成员是 C。因此,pxIndex->pxPrevious = pxNewListItem;

pxIndex->pxPrevious = pxNewListItem;

现在 pxIndex->pxPrevious 指向了 C,表示 C 成为新的尾巴前的最后一个节点。

A <-> B <-> C <-> pxIndex
5. 记录新节点 C 的所属链表

我们把 pxList 的地址存储在 C->pvContainer 中,方便以后找到 C 属于哪个链表:

pxNewListItem->pvContainer = ( void * ) pxList;
6. 更新链表节点计数器

最后,链表节点数量增加了 1:

( pxList->uxNumberOfItems )++;

现在链表计数器变成了 3,准确记录了链表中有 ABC 三个节点。

总结

整个过程简单来说,就是让新节点 C 找到他前后的人,同时告诉他前后的人有了新成员 C,最后更新链表的头尾标记和计数器。

看到这里,你是不是已经完全理解和消化掉了如何插入新节点呢?会不会有一点点疑问,但是又不好意思问呢?我自我提问几个问题吧,因为太菜因为c学的不到位,所以就问题多多了一些。

Question One:

为什么pxIndex->pxPrevious代表的是B?????

答:

在这个链表结构中,pxIndex 是一个伪尾节点(或称标记节点),它作为链表的终点标记。因为 pxIndex 并不是真正的有效数据节点,它的作用只是指示链表的尾部。链表中的实际有效节点依次连接,而 pxIndex 在链表结构上只是方便操作的一个标记。

让我们来看看为什么 pxIndex->pxPrevious 会代表链表的最后一个有效节点 B

伪尾节点(pxIndex)的工作方式

在这个双向链表中:

  • pxIndex 总是指向链表的尾部,并且它的 pxNext 通常指向 NULL 或自己,以表示链表的终止。
  • pxIndex->pxPrevious 是链表最后一个有效节点。这是因为链表的逻辑结构让 pxIndex 成为尾节点标记,那么它的 pxPrevious 指针自然指向链表中真正的最后一个有效节点。

当前链表的结构示例

假设链表中有两个有效节点 AB,链表的结构如下:

NULL <- A <-> B <-> pxIndex

在这个结构中:

  • A 是链表的第一个有效节点,所以 A->pxPreviousNULL
  • B 是链表中最后一个有效节点,所以 B->pxNext 指向 pxIndex
  • pxIndex 是链表的伪尾节点,所以 pxIndex->pxPrevious 指向 B

插入新节点的操作

当我们向链表末尾插入新节点 C 时,步骤如下:

  1. 新节点 CpxPrevious 被设置为当前最后一个有效节点,即 pxIndex->pxPrevious,也就是 B
  2. B->pxNext 被更新为 C,表示 B 的后面现在是 C
  3. pxIndex->pxPrevious 更新为 C,表示 C 成为链表中的新最后一个有效节点。

最终,链表结构变为:

NULL <- A <-> B <-> C <-> pxIndex

小结

因此,pxIndex->pxPrevious 总是指向链表的最后一个有效节点。这是因为 pxIndex 是一个伪尾节点,它自己没有有效数据,只是标记链表的终点。

Question Two:

pxIndex->pxPrevious->pxNext = pxNewListItem;为什么pxNewListItem是c的地址,pxNewListItem不是包括C的next和previous嘛?

答:

在这段代码中,pxNewListItem 是一个指向新节点的指针,表示整个新节点 C 的地址。这个指针本身包含了新节点的所有信息,包括它的 pxNextpxPrevious 指针。

当我们写 pxIndex->pxPrevious->pxNext = pxNewListItem; 时,我们在做的实际上是更新链表中前一个节点的 pxNext 指针,使它指向新节点 C。这一步的关键在于理解双向链表节点之间的指针如何相互指向,并构成链表结构。

让我们深入理解一下这里的具体含义。

理解 pxNewListItem 的作用

  1. pxNewListItem 作为新节点的整体指针
    pxNewListItem 指向整个节点 C,即包含了 CpxNextpxPrevious 等信息。因此,pxNewListItem 并不是指向 C 的某个特定成员,而是指向节点 C 本身的起始地址

  2. 在链表中建立前后关系
    在链表中,每个节点的 pxNextpxPrevious 指针都用于指向相邻节点。这段代码:

pxIndex->pxPrevious->pxNext = pxNewListItem;
  1. 等效于 B->pxNext = pxNewListItem,其中 BpxIndex->pxPrevious 指向的最后一个有效节点。这一步就是让 B->pxNext 指向新节点 C 的整体地址(即 pxNewListItem),表示 B 的下一个节点现在是 C。通过这一指向关系的设置,BC 之间的链接建立起来了。

  2. 为什么是整个 C 的地址
    pxNewListItem 的确包含 CpxNextpxPrevious 指针,但在这行代码中,我们只关心将 pxNewListItem 作为一个完整的节点地址赋给 B->pxNext,表示 B 后面的节点是 CpxNextpxPrevious 的详细信息只有在访问节点 C 本身时才会被读取和使用。

代码工作流程小结

  1. pxNewListItem->pxNext = pxIndex;:将 C->pxNext 指向 pxIndex,表示 C 后面是伪尾节点 pxIndex
  2. pxNewListItem->pxPrevious = pxIndex->pxPrevious;:将 C->pxPrevious 指向 B(原最后一个节点)。
  3. pxIndex->pxPrevious->pxNext = pxNewListItem;:将 B->pxNext 指向 C,即 B 现在知道他后面是 C
  4. pxIndex->pxPrevious = pxNewListItem;:将 pxIndex->pxPrevious 更新为 C,即 C 成为新的最后一个有效节点。

总结

pxIndex->pxPrevious->pxNext = pxNewListItem; 中,pxNewListItem 表示整个新节点 C 的地址,而不只是它的 pxNextpxPrevious。我们通过这个赋值操作让 B->pxNext 指向 C 的地址,从而把 C 插入到 BpxIndex 之间。

http://www.lryc.cn/news/475602.html

相关文章:

  • AUTOSAR从入门到精通-BswM模块(二)
  • Spring DispatcherServlet详解
  • JS | 软件制作的流程是什么?
  • 简单工厂模式
  • 【django】Django REST Framework 序列化与反序列化详解
  • 【Golang】Golang的Map的线程安全问题
  • 指向指针的指针+ 值传递的理解
  • CSS常用定位
  • 【Linux】从零开始使用多路转接IO --- select
  • ArcGIS Pro SDK (二十一)渲染
  • FPGA在物联网边缘计算中的应用!!!
  • 【解决】Linux环境中mysqlclient安装失败问题
  • ✨ Midjourney中文版:创意启航,绘梦无界 ✨
  • 软件(1)
  • linux perf 环境部署和基本测试(基于Ubuntu20.04)
  • 【网络面试篇】HTTP(1)(笔记)——状态码、字段、GET、POST、缓存
  • HTML 基础标签——分组标签 <div>、<span> 和基础语义容器
  • SS928V100 ISP常见问题列表
  • AI写诗:自动版大唐宫体诗
  • Java复习31(PTA)
  • 【Linux系列】Linux 系统中的软连接管理
  • @layer(级联层)
  • nginx代理websocket服务
  • 第二十七章 Vue异步更新之$nextTick
  • 【51 Pandas+Pyecharts | 深圳市共享单车数据分析可视化】
  • 【Clikhouse 探秘】ClickHouse 物化视图:加速大数据分析的新利器
  • 线程相关题(线程池、线程使用、核心线程数的设置)
  • 2181、合并零之间的节点
  • powerlaw:用于分析幂律分布的Python库
  • 工作管理实战指南:利用Jira、Confluence等Atlassian工具打破信息孤岛,增强团队协作【含免费指南】