List_t
//List_t 列表
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE //校验值
volatile UBaseType_t uxNumberOfItems; // 列表中的列表项数量:用于记录列表中列表项的个数(不包含 xListEnd)
ListItem_t * configLIST_VOLATILE pxIndex; // 用于遍历列表项的指针:用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项
MiniListItem_t xListEnd; // 末尾列表项:成员变量 xListEnd 是一个迷你列表项,排在最末尾
listSECOND_LIST_INTEGRITY_CHECK_VALUE //< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1.
} List_t;
ListItem_t
//ListItem_t 列表项
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE TickType_t xItemValue; //列表项的值
struct xLIST_ITEM * configLIST_VOLATILE pxNext; //下一个列表项
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; //上一个列表项
void * pvOwner; //列表项的拥有者
struct xLIST * configLIST_VOLATILE pxContainer; //列表项所在列表
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
vListInsert 列表的插入(本质上是双向链表的使用)
按照 Item列表项 中的 xItemValue 排序插入,xItemValue 越大,越靠近 xListEnd
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
/*pxList 列表项要插入的列表 pxNewListItem 要插入的列表项*/
ListItem_t * pxIterator; /* 临时索引变量 */
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; //获取插入的列表项值
listTEST_LIST_INTEGRITY( pxList ); /* 检查参数是否正确 */
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); /* 如果待插入列表项的值为最大值 */
/*将新的列表项插入到列表,根据xItemValue的值升序插入列表。*/
if( xValueOfInsertion == portMAX_DELAY ) //portMAX_DELAY列表项的值为最大值
{
/* 如果列表项的值等于portMAX_DELAY,即列表项为最大值,则插入末尾 */
pxIterator = pxList->xListEnd.pxPrevious; /* 插入的位置为列表 xListEnd 前面 */
}
else /*遍历列表中的列表项,找到插入的位置*/
{
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
pxIterator = pxIterator->pxNext )
/*
pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); //
*/
{
/* 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->pxContainer = pxList; /* 更新待插入列表项所在列表 */
( pxList->uxNumberOfItems )++; /* 更新列表中列表项的数量 */
}
参考文章:FreeRTOS中列表和列表项插入函数分析(https://blog.csdn.net/qq_20222919/article/details/102936067)
假如现在的列表项如下:
此时我们要将 50 插入到列表中去
此时pxNewListItem指向的就是50这一项,pxIterator指向的就是40这一项。
由上面的for循环也可以看出
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
列表中的值小于等于当要插入的值,插入点就继续指向下一个。
此时要插入的值是50,它会依次和列表中的值20、30、40、60比较,当比较到60时,插入点的值是60,大于要插入的值50,此时for循环退出,插入点指向40这一项。
此时列表的状态入下图所示
下来开始执行列表插入操作。
pxNewListItem->pxNext = pxIterator->pxNext;//50->next->60
将插入点指向的下一个列表项赋值给新插入列表项的下一项
此时列表项50的下一项指向了列表项60.
pxNewListItem->pxNext->pxPrevious = pxNewListItem;//pxNewListItem->pxNext就是60 60->pre ->50
将新插入列表项的下一项的前一项设置为新插入列表项。
也就是说此时列表项60的上一个列表项指向了50。
pxNewListItem->pxPrevious = pxIterator;
下来将新插入列表项的前一个列表项设置为插入点。
此时列表项50的前一项指向了40。
pxIterator->pxNext = pxNewListItem;
将新插入列表项指向插入点的下一项
此时列表项40的下一项是列表项50,列表项50的下一项是60。列表项60的前一项是列表项50,列表项50的前一项是列表项40。
pxNewListItem->pvContainer = ( void * ) pxList:
插入后,列表项成员变量pvContainer记录此列表项属于哪个列表。
pxList->uxNumberOfItems:列表成员数量加1。
这样列表项50就成功的插入到列表中去了,插入的过程就是将插入点与后面的连接项断开,然后重新连接到新插入项上。在把新插入项和断开的连接项连接起来。
Blog 参考 https://yufengbiji.com/posts/data-structures-02
链表的创建
定义表示节点的结构体
struct node {
/* 后继节点 */
struct node *next;
/* 值 */
int data;
};
定义结点结构体
typedef struct node
{
/* data:数据域*/
int num;
int score;
char name[20];
/* point:指针域 指针需要指向的地址 后继节点*/
struct node *next;
} Node;
在后面的代码书写中, Node
就等价于struct node
了。 比如我们使用这个结构体创建一个新的节点, Node *new_node
就等价于struct node *new_node
。
单链表的节点
单链表的表示
单链表是线性表的链式表示和实现。把节点链接起来,就形成了单链表。
单链表的创建
list * create_list()
{
/* 创建一个新的节点,由于使用了typedef关键字,此处 node *head与struct node *head等价 */
list *head = (list *)malloc(sizeof(list));
if(head==NULL) return NULL;
/* 初始化节点 */
head->data = 0; // 头结点数据域,我们用来表示链表长度
head->next = NULL;
return head;
}
此函数会创建一个单链表,并返回头指针。 头指针是指向头结点地址的指针,和节点中指向下一个节点的指针是相同类型。
首先,我们用malloc
函数开辟了一块list
大小的内存,并返回了指向该内存块首地址的指针,同时将此指针赋值给头指针变量。
接着,判断此指针是否为空,为空,则说明内存申请失败(一般不会)。
然后,对该节点进行初始化。
最后,函数返回头指针,结束。
为什么设置头节点?
头节点的数据域一般无意义,这里为了方便后面的插入和删除操作而设置,头节点并非链表所必须。
头节点后面的第一个元素节点,称为首元节点
// 链表的创建
void link_creat_head(Node ** p_head,Node *p_new)
{
Node *p_mov = *p_head; //当第一次加入链表为空时,head执行p_new
if (*p_head == NULL)
{
*p_head = p_new;
p_new->next = NULL;
}
else //第二次及以后加入链表
{
while (p_mov->next!=NULL) //找到原有链表的最后一个节点
{
p_mov = p_mov->next;
}
p_mov->next = p_new; //将新申请的节点加入链表
p_new->next = NULL;
}
}
//链表的遍历
void link_print(Node *head)
{
Node *p_mov; //定义新的指针保存链表的首地址,防止使用head改变原本链表
p_mov = head; //当指针保存最后一个结点的指针域为NULL时,循环结束
while(p_mov!=NULL)
{
//先打印当前指针保存结点的指针域
printf("num=%d score=%d name:%s\n",p_mov->num,\
p_mov->score,p_mov->name);
//指针后移,保存下一个结点的地址
p_mov = p_mov->next;
}
}
//链表的释放
void link_free(Node **p_head)
{
//定义一个指针变量保存头结点的地址
Node *pb=*p_head;
while(*p_head!=NULL)
{
//先保存p_head指向的结点的地址
pb=*p_head;
//p_head保存下一个结点地址
*p_head=(*p_head)->next;
//释放结点并防止野指针
free(pb);
pb = NULL;
}
}
单链表基本操作
2.1 单链表的插入
2.2 单链表的删除
2.3 单链表的查找
先对比第一个结点的数据域是否是想要的数据,如果是就直接返回,如果不是则继续查找下 一个结点,如果到达最后一个结点的时候都没有匹配的数据,说明要查找数据不存在
…
//链表的查找
//按照num查找
Node * link_search_num(Node *head,int num)
{
Node *p_mov;
//定义的指针变量保存第一个结点的地址
p_mov=head;
//当没有到达最后一个结点的指针域时循环继续
while(p_mov!=NULL)
{
//如果找到是当前结点的数据,则返回当前结点的地址
if(p_mov->num == num)//找到了
{
return p_mov;
}
//如果没有找到,则继续对比下一个结点的指针域
p_mov=p_mov->next;
}
//当循环结束的时候还没有找到,说明要查找的数据不存在,返回NULL进行标识
return NULL;//没有找到
}
//按照name查找
Node * link_search_name(Node *head,char *name)
{
Node *p_mov;
p_mov=head;
while(p_mov!=NULL)
{
if(strcmp(p_mov->name,name)==0)//找到了
{
return p_mov;
}
p_mov=p_mov->next;
}
return NULL;//没有找到
}
//链表的插入:按照num的顺序插入
void link_insert_num(Node **p_head,Node *p_new)
{
Node *pb,*pf;
pb=pf=*p_head;
if(*p_head ==NULL)// 链表为空链表
{
*p_head = p_new;
p_new->next=NULL;
return ;
}
while((p_new->num >= pb->num) && (pb->next !=NULL) )
{
pf=pb;
pb=pb->next;
}
if(p_new->num < pb->num)//找到一个节点的num比新来的节点num大,插在pb的前面
{
if(pb== *p_head)//找到的节点是头节点,插在最前面
{
p_new->next= *p_head;
*p_head =p_new;
}
else
{
pf->next=p_new;
p_new->next = pb;
}
}
else//没有找到pb的num比p_new->num大的节点,插在最后
{
pb->next =p_new;
p_new->next =NULL;
}
}
//链表的排序
void link_order(Node *head)
{
Node *pb,*pf,temp;
pf=head;
if(head==NULL)
{
printf("The list is empty and does not need to be sorted\n");
return ;
}
if(head->next ==NULL)
{
printf("Only one node, no sorting\n");
return ;
}
while(pf->next !=NULL) //以pf指向的节点为基准节点,
{
pb=pf->next ; //pb从基准元素的下个元素开始
while(pb!=NULL)
{
if(pf->num > pb->num) //判断num的大小去排序
{
temp=*pb;
*pb=*pf;
*pf=temp;
temp.next=pb->next;
pb->next=pf->next;
pf->next=temp.next;
}
pb=pb->next;
}
pf=pf->next;
}
}
//链表结点的删除
void link_delete_num(Node **p_head,int num)
{
Node *pb,*pf;
pb=pf=*p_head;
if(*p_head == NULL)//链表为空,不用删
{
printf("The list is empty and there are no nodes to delete");\
return ;
}
while(pb->num != num && pb->next !=NULL)//循环找,要删除的节点
{
pf=pb;
pb=pb->next;
}
if(pb->num == num)//找到了一个节点的num和num相同
{
if(pb == *p_head)//要删除的节点是头节点
{
*p_head = pb->next;//让保存头结点的指针保存后一个结点的地址
}
else
{
pf->next = pb->next;//前一个结点的指针域保存要删除的后一个结点的地址
}
//释放空间
free(pb);
pb = NULL;
}
else//没有找到
{
printf("There are no nodes to delete\n");
}
}
int main()
{
Node *head = NULL, *p_new =NULL;
int num,i;
printf("Please enter the initial number of linked list:\n");
scanf("%d",&num);
for (i = 0;i<num;i++)
{
p_new = (Node*) malloc(sizeof(Node));
printf("Iint num,socre,name \n");
scanf("%d %d %s",&p_new->num,&p_new->score,&p_new->name);
link_creat_head(&head,p_new);
}
link_print(head);
#if 0
Node *pb;
while(1)
{
printf("Please enter the number you are looking for \n");
scanf("%d",&num);
pb=link_search_num(head,num);
if(pb!=NULL)//找到了
{
printf("Found it. num=%d score=%d name:%s\n",pb->num,pb->score,pb->name);
}
else
{
printf("The node you were looking for was not found \n");
}
}
#endif
#if 0
char name[32] = "";
Node *pb;
while(1)
{
printf("Please enter the name you are looking for\n");
scanf("%s",name);
pb=link_search_name(head,name);
if(pb!=NULL)//找到了
{
printf("Found it. num=%d score=%d name:%s\n",pb->num,pb->score,pb->name);
}
else
{
printf("The node you were looking for was not found\n");
}
}
#endif
#if 0
printf("Enter the number of the node you want to delete\n");
scanf("%d",&num);
link_delete_num(&head,num);
link_print(head);
#endif
#if 0
while(1)
{
printf("Enter the num-score-name of the node you want to insert\n");
p_new=(Node*)malloc(sizeof(Node));//申请一个新节点
scanf("%d %d %s",&p_new->num,&p_new->score,p_new->name);
link_insert_num(&head,p_new);
link_print(head);
}
#endif
#if 0
printf("***************************\n");
link_order(head);
link_print(head);
#endif
link_free(&head);
return 0;
}