手写一个双向链表是什么体验

节点本身必须包含一个节点指针,用于指向后一个节点。
struct node
{
struct node *next; /* 指向链表的下一个节点 */
char data1; /* 单个的数据 */
unsigned char array[]; /* 数组 */
unsigned long *prt /* 指针数据 */
struct userstruct data2; /* 自定义结构体类型数据 */
/* ...... */
}
上面的数据结构中,除了struct node *next外,其他的都可以理解为节点携带的数据。但是一般不会这么,一般是在节点里面只包含一个用于指向下一个节点的指针。通过链表存储的数据内嵌一个节点,存储的数据通过内嵌的节点挂载到链表中。
理解:就是在一个要存储的数据结构中添加一个节点,然后通过这个节点将数据挂载到链表中
1 /* 节点定义 */
2 struct node
3 {
4 struct node *next; /* 指向链表的下一个节点 */
5 }
6//用户数据结构
7 struct userstruct
8 {
9 /* 在用户数据结构体中,内嵌一个节点指针,通过这个节点将数据挂接到链表 */
10 struct node *next;
11 /* 各种各样......,要存储的数据 */
12 }

双向链表:与单链表的区别是节点还在那个有两个节点指针,分别指向前后两个节点其他的完全一样

FreeRTOS中链表的实现
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; //节点类型重定义

struct xMINI_LIST_ITEM
{
TickType_t xItemValue; //辅助值,用于帮助节点做升序排列
struct xLIST_ITEM *pxNext; //指向下一个节点
struct xLIST_ITEM *pxPrevious; //指向前一个节点
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
//定义根节点数据结构(链表)
typedef struct xLIST
{
UBaseType_t uxNumberOfItems; //链表节点计数器
ListItem_t *pxIndex; //链表节点索引指针,用于遍历节点
MiniListItem_t xListEnd; //最后一个节点(也是第一个节点,生产者)
}List_t;

根节点初始化
void vListInitiallise(List_t * const pxList)
{
//将链表索引指针指向最后一个节点
pxList->pxIndex = (ListItem_t *)&(pxList->xListEnd);
//将链表最后一个节点辅助值排序的值设为最大,确保该节点是链表的最后节点
pxList->xListEnd.xItemValue = portMAX_DELAY;
//将链表最后一个节点的pxNext和pxPrevious指针均指向节点自身,表示链表为空
pxList->xListEnd.pxNext = &(pxList->xListEnd);
pxList->xListEnd.pxPrevious = &(pxList->xListEnd);
//初始化链表节点计数器的值为0,表示链表为空
pxList->uxNumberOfItems = (UBaseType_t)0;
}

将链表插入节点的尾部(是将新的节点插入根节点的前一个节点),就是将一个新的节点插入到一个空的链表
void vListInserEnd(List_t * const pxList, ListItem_t * const pxNewListItem)
{
//根节点,也就是最后一个节点
ListItem_t * const pxIndex = pxList->pxIndex;
//新节点的next node指向根节点
pxNewListItem->pxNext = pxIndex;
//新节点的pre node指向根节点的前一个节点
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
//设置根节点的上一个节点的next node指向新节点
pxIndex->pxPrevious->pxNext = pxNewListItem;
//设置根节点的上一个节点指向新节点
pxIndex->pxPrevious = pxNewListItem;
//记住节点所在的链表
pxNewListItem->pvContainer = (void *)pxList;
//节点计数器加1
pxList->uxNumberOfItems++;
}

插入前:
+----------+ +----------+ +----------+ +----------+
| xListEnd |<---> | 节点A |<---> | 节点B |<---> | xListEnd |
+----------+ +----------+ +----------+ +----------+
插入后:
+----------+ +----------+ +----------+ +----------+ +----------+
| xListEnd |<---> | 节点A |<---> | 节点B |<---> | NewItem |<---> | xListEnd |
+----------+ +----------+ +----------+ +----------+ +----------+
步骤:
//新节点的next node指向根节点
pxNewListItem->pxNext = pxIndex;
//新节点的pre node指向根节点的前一个节点
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
1、设置NewItem的next指向 xListEnd,NewItem的previous指向节点B(也就是根节点的previous)
//设置根节点的上一个节点的next node指向新节点
pxIndex->pxPrevious->pxNext = pxNewListItem;
//设置根节点的上一个节点指向新节点
pxIndex->pxPrevious = pxNewListItem;
2、设置节点B指向NewItem:也就是 xListEnd的上一个节点的next指向新节点,然后在设置根节点的previous指向新节点
3、最后设置节点的所在的链表,以及节点计数器加一
将节点按照升序排列插入链表
//将节点按照升序排列插入到链表
void vListInsert(List_t * const pxList, ListItem_t * const pxNewListItem)
{
ListItem_t *pxIterator;
//获取节点的辅助值
const TickType_t xValueOfInsertion = pxNewListItem.xItemValue;
//寻找节点要插入的位置
if(xValueOfInsertion == portMAX_DELAY)
{
//如果新节点的辅助值是最大,那么新节点要插入的位置就是最后一个节点
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
/*
再次强化,根节点既是尾节点也是头节点,所以这里
pxIterator->pxNext->xItemValue就是第一个节点的辅助值
且这里的判断是pxIterator的下一个节点的辅助值,所以新节点永远在pxIterator后面
依次遍历,找到新节点要插入的位置
*/
for(pxIterator = (ListItem_t *)&(pxList->xListEnd);
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
pxIterator = pxIterator->pxNext)
{/*只是迭代找到节点要插入的位置*/}
}
//根据升序排列,将节点插入
//是将新节点的next插入到当前节点的下一个节点
pxNewListItem->pxNext = pxIterator->pxNext;
//将后一个节点的pre设置为当前节点
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
//设置新节点的pre为当前节点
pxNewListItem->pxPrevious = pxIterator;
//当前节点的next设置为新节点
pxIterator->pxNext = pxNewListItem;
//记住节点所在的链表
pxNewListItem->pvContainer = (void *)pxList;
//节点计数器加1
pxList->uxNumberOfItems++;
}
xListEnd <-> 10 <-> 20 <-> 30 <-> xListEnd
将25节点插入上面列表
xListEnd <-> 10 <-> 20 <-> 25 <-> 30 <-> xListEnd
通过迭代会找到20对应的节点,然后将25插入到20和30之间
//将25节点的next设置为30,因为pxIterator表示的是20
pxNewListItem->pxNext = pxIterator->pxNext;
//将30节点的pre设置为25
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
//设置25节点的pre为20
pxNewListItem->pxPrevious = pxIterator;
//设置20节点的next为25
pxIterator->pxNext = pxNewListItem;

//将节点从链表中删除
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove)
{
//获取节点所在的链表
List_t * const pxList = (List_t *)pxItemToRemove->pvContainer;
//将指定的节点从链表删除
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
//调整链表的节点索引指针
if(pxList->pxIndex == pxItemToRemove)
{
pxList->pxIndex = pxItemToRemove->pxNext;
}
//初始化该节点所在的链表为空,表示该节点没有插入链表
pxItemToRemove->pvContainer = NULL;
pxList->uxNumberOfItems--;
return pxList->uxNumberOfItems;
}


浙公网安备 33010602011771号