链表、列表、列表项

链表、列表、列表项

FreeRTOS 列表与列表项

  1. 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;
    
    
    
  2. 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      
    
    };
    
  3. 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)

FreeRTOS 列表与列表项

链表、列表、列表项_第1张图片

假如现在的列表项如下:

链表、列表、列表项_第2张图片

此时我们要将 50 插入到列表中去

链表、列表、列表项_第3张图片

此时pxNewListItem指向的就是50这一项,pxIterator指向的就是40这一项。

由上面的for循环也可以看出
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
列表中的值小于等于当要插入的值,插入点就继续指向下一个。

此时要插入的值是50,它会依次和列表中的值20、30、40、60比较,当比较到60时,插入点的值是60,大于要插入的值50,此时for循环退出,插入点指向40这一项。
此时列表的状态入下图所示

链表、列表、列表项_第4张图片

下来开始执行列表插入操作。

   pxNewListItem->pxNext = pxIterator->pxNext;//50->next->60

将插入点指向的下一个列表项赋值给新插入列表项的下一项

链表、列表、列表项_第5张图片

此时列表项50的下一项指向了列表项60.

pxNewListItem->pxNext->pxPrevious = pxNewListItem;//pxNewListItem->pxNext就是60 60->pre ->50

将新插入列表项的下一项的前一项设置为新插入列表项。

链表、列表、列表项_第6张图片

也就是说此时列表项60的上一个列表项指向了50。

pxNewListItem->pxPrevious = pxIterator;

下来将新插入列表项的前一个列表项设置为插入点。

链表、列表、列表项_第7张图片

此时列表项50的前一项指向了40。

 pxIterator->pxNext = pxNewListItem;

将新插入列表项指向插入点的下一项

链表、列表、列表项_第8张图片

此时列表项40的下一项是列表项50,列表项50的下一项是60。列表项60的前一项是列表项50,列表项50的前一项是列表项40。
pxNewListItem->pvContainer = ( void * ) pxList
插入后,列表项成员变量pvContainer记录此列表项属于哪个列表。
pxList->uxNumberOfItems:列表成员数量加1。
这样列表项50就成功的插入到列表中去了,插入的过程就是将插入点与后面的连接项断开,然后重新连接到新插入项上。在把新插入项和断开的连接项连接起来。

C语言链表基础

Blog 参考 https://yufengbiji.com/posts/data-structures-02

  1. 链表的创建

    定义表示节点的结构体

    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

    单链表的节点

链表、列表、列表项_第9张图片

​ 单链表的表示

​ 单链表是线性表的链式表示和实现。把节点链接起来,就形成了单链表。

链表、列表、列表项_第10张图片

​ 单链表的创建

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;
}

链表、列表、列表项_第11张图片

此函数会创建一个单链表,并返回头指针。 头指针是指向头结点地址的指针,和节点中指向下一个节点的指针是相同类型。
首先,我们用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;
    }
}
  1. 单链表基本操作

    2.1 单链表的插入

    链表、列表、列表项_第12张图片

    2.2 单链表的删除

    链表、列表、列表项_第13张图片

    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");
    }
}
  1. 代码测试
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;
}

链表、列表、列表项_第14张图片
链表、列表、列表项_第15张图片
链表、列表、列表项_第16张图片
链表、列表、列表项_第17张图片
链表、列表、列表项_第18张图片

你可能感兴趣的:(C/C++,链表,数据结构)