链表-双向非通用链表
2020-10-10 15:14
661 查看
目录概念 笔录草稿 双向链表 链表操作的函数代码链表初始化函数 节点初始化函数 节点插入链表尾部函数 节点有序插入链表函数 从链表中删除函数 源码集合
节点:
节点:挂钩
包含上一个
下一个
钩子等其它需要的信息
袜子:挂在到 钩子 的东西
袜子携带的信息
通用链表节点被放到信息结构体中,通过偏移找到所在的结构体(即是通过偏移找到袜子头)
而非通用链表是在节点中携带信息结构体的指针的(即是节点就携带信息)。
别人通俗理解,读者不必理会本小点
通用链表是把袜子放到晾衣架的圆形圈上,袜子与圆形圈接触部分为袜子接待的节点。(信息携带节点)
非通用链表是。(节点携带信息)
清空节点A的链表归属
链表节点计数值 -1
返回链表节点数
跳转到非通用链表完整C语言源码即可
前言
- 20201010
- 在阅读 RTOS LiteOS 内核源码时发现该内核使用的链表时通用链表,而 FreeRTOS 内核使用的时非通用链表,所以,有必要发布一下关于链表实现的笔记。
- 以下内容为个人笔记,涉及一些非专业词汇,敬请谅解,谢谢。
链接
参考
- 上面链接
- FreeRTOS 内核源码
- 野火
概念
-
正常表达
链表: 链表为 C 中一种基础的数据结构。 - 看成环形晾衣架即可。
-
节点组成链表
自理解概念
-
链表:圆形的晾衣架
-
包含被钩子
通用链表与非通用链表的区别
-
通用链表节点内容很少一般只有 上一个 和 下一个。
笔录草稿
双向链表
-
双向链表理解图
-
原理:链表包括 根节点 和 普通节点
根节点 主要管理链表的,一般包括
上一个 - 下一个
- 存在多少个等信息
普通节点 主要用于钩住袜子(即是携带信息)
节点及 56c 节点结构体代码
- 普通节点 存放节点信息
- 挂载东西(挂钩),如挂载袜子等等
/* * The linked list node */ struct LIST_ITEM_LSS_T { struct LIST_ITEM_LSS_T * pxNext; // 下一个 struct LIST_ITEM_LSS_T * pxPrevious; // 上一个 /* 节点属性,(根据个人需求增减以下内容) */ uint32_t xItemValue; // 记号值,一般用于排序 void * pvOwner; // 挂钩,即携带的信息 void * pvContainer; // 归属,即属于哪一个链表 }; typedef struct LIST_ITEM_LSS_T listItem_t;
- root节点(链表点) 存放链表的信息
- 有一个mini节点,用于驳接和定位(相当于位置校准点),不挂载如何东西,且简洁为妙
mini节点的记号值在双向链表中为最大值,因为最大是尾也是头。
/* mini 节点结构体 */ struct LIST_MINI_ITEM_LSS_T { /* 指向,( 56c 根据单、双链表删减以下内容) */ struct node *pxPrevious; // 上一个节点 struct node *pxNext; // 下一个节点 /* 节点属性,(根据个人需求增减以下内容) */ uint32_t xItemValue; // 记号值,在双向链表中为最大值 }; typedef struct LIST_MINI_ITEM_LSS_T ListMiniItem_t; /* 链表结构体,即根节点 */ struct LIST_LSS_T { /* 节点属性,(根据个人需求增减以下内容) \*/ uint32_t uxNumberOfItems; // 节点数,统计粘附在本链表上的节点数 struct LIST_ITEM_LSS_T * pxIndex; // 索引,链表索引,指向链表中的某个节点 struct LIST_MINI_ITEM_LSS_T xListEnd; // 链表根节点 }; typedef struct LIST_LSS_T List_t;
链表操作的函数代码
链表初始化函数
- 链表索引指向该链表的尾节点(尾节点,即也是头节点)
- 链表尾节点记号值赋值为最大值(根节点包含尾节点)
- 初始化尾节点的上一个及下一个,分别都指向**尾节点
- 初始化节点总数为 0。
/** * @brief 链表初始化 * @param * @retval * @author lzm */ void l 56c istInit(list_t * const list) { /* 索引指向最后:尾就是头 */ list->pxIndex = (listItem_t *) &(list->xListEnd); /* 链表最大值 */ list->xListEnd.xItemValue = lssLIST_MAX_VALUE; list->xListEnd.pxNext = ( listItem_t * ) &( list->xListEnd ); list->xListEnd.pxPrevious = ( listItem_t * ) &( list->xListEnd ); list->uxNumberOfItems = (lssLIST_BASE_TYPE)0U; }
节点初始化函数
- 初始化节点携带的信息为空
/** * @brief 节点初始化 * @param * @retval * @author lzm */ void listItemInit(listItem_t * const item) { item->pvContainer = NULL; }
节点插入链表尾部函数
注意:需要插入的节点以下称为节点A
- 获取索引(索引即游标,也就是该链表当前指向的节点)
- 节点A的下一个指向索引节点,
- 节点A的前一个指向索引节点的前一个
- 索引节点前一个的下一个指向节点A
- 索引节点的前一个指向节点A
- 设置节点A归属于哪个链表
- 链表节点计数值 +1
/** * @brief 插入链表尾部(*双向链表没有绝对的头尾,此处是以游标为参考物*) * @param * @retval * @author lzm */ void listInsertEnd( list_t * const pxList, listItem_t * const pxNewListItem ) { listItem_t * const pxIndex = pxList->pxIndex; pxNewListItem->pxNext = pxIndex; pxNewListItem->pxPrevious = pxIndex->pxPrevious; pxIndex->pxPrevious->pxNext = pxNewListItem; pxIndex->pxPrevious = pxNewListItem; /* Remember which list the item is in. */ pxNewListItem->pvContainer = ( void * ) pxList; ( pxList->uxNumberOfItems )++; }
节点有序插入链表函数
注意:需要插入的节点以下称为节点A
- 获取新节点记号值
2.找出需要插入的位置 如果记号值为链表中最大值(即和尾节点的记号值相等),则取出尾节点的前一个节点作为参考节点 - 如果记号值不为链表中最大值,则从尾节点开始找,直至找到当前节点的下一个节点的记号值为大于 节点A的记号值(即是在链表中找出红框节点B)
- 插入节点 节点A(节点A 为需要插入的节点)的下一个指向索引节点,
- 节点A的前一个指向节点B的前一个
- 节点B的前一个的下一个指向节点A
- 节点B的前一个指向节点A
- 设置节点A归属于哪个链表
- 链表节点计数值 +1
/** * @brief 按记号值值插入 * @param * @retval * @author lzm */ void listInsert( list_t * const pxList, listItem_t * const pxNewListItem ) { listItem_t *pxIterator; const lssLIST_BASE_TYPE xValueOfInsertion = pxNewListItem->xItemValue; // 获取新节点记号值 /* 按顺序寻找 */ if( xValueOfInsertion == lssLIST_MAX_VALUE ) { pxIterator = pxList->xListEnd.pxPrevious; } else { 15a8 for( pxIterator = ( listItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ { /* There is nothing to do here, just iterating to the wanted insertion position. */ } } pxNewListItem->pxNext = pxIterator->pxNext; pxNewListItem->pxNext->pxPrevious = pxNewListItem; pxNewListItem->pxPrevious = pxIterator; pxIterator->pxNext = pxNewListItem; /* Remember which list the item is in. This allows fast removal of the item later. */ pxNewListItem->pvContainer = ( void * ) pxList; ( pxList->uxNumberOfItems )++; }
从链表中删除函数
注意:被删除的节点以下称为节点A
- 获取链表
- 节点A下一个节点的前一个指向节点A的前一个节点
- 节点A前一个节点的下一个指向节点A的下一个节点
- 检查链表索引是否指向了节点A 是:索引更新为指向节点A的前一个节点
- 否:跳过
/** * @brief 从链表中删除节点 * @param * @retval * @author lzm */ uint32_t listRemove( listItem_t * const pxItemToRemove ) { /* The list item knows which list it is in. Obtain the list from the list item. */ list_t * const pxList = ( list_t * ) pxItemToRemove->pvContainer; pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; /* Make sure the index is left pointing to a valid item. */ if( pxList->pxIndex == pxItemToRemove ) { pxList->pxIndex = pxItemToRemove->pxPrevious; } pxItemToRemove->pvContainer = NULL; ( pxList->uxNumberOfItems )--; return pxList->uxNumberOfItems; }
源码集合
- 内含 节点及链表结构体
- 节点初始化函数
- 链表初始化函数
- 节点插入函数
- 删除节点函数
- 配对挂钩与袜子函数
- 获取节点信息函数
- 获取记号值函数
- 获取第一个节点的节点值函数
- 获取链表的入口节点函数
- 获取下一个节点函数
- 获取链表最后一个节点(尾节点)函数
- 判断链表是否为空函数
- 获取链表当前节点总数函数
- 获取链表索引指向的下一个节点函数
相关文章推荐
- NOIP模拟题[双向链表][延时更新][回收空间]EDITOR
- 循环链表与双向链表
- 双向链表 , 添加 , 得到上层结构体地址
- 1.把二元查找树转变成排序的双向链表
- (1)把二元查找树转变成排序的双向链表
- 深入浅出linux内核源代码之双向链表list_head(上)
- 双向循环链表相关操作
- python实现双向链表
- 数据结构(C#)--双向链表
- 双向链表的建立 增删改 打印
- 嵌入式C语言那点事(三)Linux中霸道的双向链表源码与应用
- 双向循环链表的插入和删除
- 二叉排序树转换为有序双向链表
- Linux内核2.6.14源码分析-双向循环链表代码分析
- C++/C++11中std::list双向链表的使用
- 源码之LinkedList 单双向链表介绍
- Linux双向循环链表的实现
- 用只含一个链域的节点实现循环链表的双向遍历
- 剑指Offer:面试题27 二叉搜索树与双向链表
- 面试-双向链表