链表、列表、列表项

news2024/11/15 7:23:47

链表、列表、列表项

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 列表与列表项

p98q20x.jpg

假如现在的列表项如下:

此时我们要将 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就成功的插入到列表中去了,插入的过程就是将插入点与后面的连接项断开,然后重新连接到新插入项上。在把新插入项和断开的连接项连接起来。

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

    单链表的节点

​ 单链表的表示

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

​ 单链表的创建

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;
    }
}
  1. 单链表基本操作

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

pC2iKIJ.md.png
pC2iua4.md.png
pC2inZF.md.png
pC2ieqU.md.png
pC2iZrT.md.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/736002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AcWing 1497:树的遍历

【题目来源】https://pintia.cn/problem-sets/994805342720868352/exam/problems/994805485033603072https://www.acwing.com/problem/content/description/1499/【题目描述】 一个二叉树&#xff0c;树中每个节点的权值互不相同。 现在给出它的后序遍历和中序遍历&#xff0c;…

Verilog 基础知识(一) Verilog 基础语法与注意事项

基础知识 0.1 模块(Module) Verilog中的module可以看成一个具有输入输出端口的黑盒子&#xff0c;该黑盒子有输入和输出接口(信号)&#xff0c;通过把输入在盒子中执行某些操作来实现某项功能。(类似于C语言中的函数) 图1 模块示意图 0.1.1 模块描述 图1 所示的顶层模块(top…

1.Git使用技巧-常用命令2

1.Git使用技巧-常用命令2 文章目录 1.Git使用技巧-常用命令2一、本地仓库整理二、查看本地仓库查看过滤 二、逆向操作1. 检出覆盖本地文件 workspace -> NULL1.1 删除workspace 文件 2. 本地仓库回滚3. 远程仓库1. 远程仓库未基于代码做修改 逆操作总结 三、删除1. 删除远程…

基于 Jieba 和 Word2vec 的关键词词库构建

最近有空&#xff0c;把论文中用到的技术和大家分享一下&#xff08;以组件化的形式&#xff09;&#xff0c;本篇将讲述如何从大量的语料中获取诸多关键词和构建关键词词库或 xx 关键词词库&#xff08;细分领域&#xff09;。举例以购物网站的在线评论作为语料库&#xff0c;…

Python实现PSO粒子群优化算法优化Catboost回归模型(CatBoostRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 PSO是粒子群优化算法&#xff08;Particle Swarm Optimization&#xff09;的英文缩写&#xff0c;是一…

Git--多人协作开发

文章目录 前言一、多人协作一开发目标将dev分支内容合并至master分支 二、多人协作二开发者1操作开发者2操作突发情况内容合并至master分支 三、解决git branch -a打印已被删除的远程分支的方法总结 前言 目前,我们所完成的工作如下 : 基本完成Git的所有本地库的相关操作,git…

学无止境·MySQL(4-2)(多表查询加强版)

多表练习试题 试题2&#xff08;加强版&#xff09;1、创建表2、表中添加数据3、找出销售部门中年纪最大的员工的姓名4、求财务部门最低工资的员工姓名5、列出每个部门收入总和高于9000的部门名称6、求工资在7500到8500元之间&#xff0c;年龄最大的人的姓名及部门7、找出销售部…

【Python】面向对象 - 封装 ② ( 访问私有成员 | 对象无法访问私有变量 / 方法 | 类内部访问私有成员 )

文章目录 一、访问私有成员1、对象无法访问私有变量2、对象无法访问私有方法3、类内部访问私有成员 一、访问私有成员 1、对象无法访问私有变量 在下面的 Python 类 Student 中 , 定义了私有的成员变量 , # 定义私有成员__address None该私有成员变量 , 只能在类内部进行访问 …

分析图中常见的这种“箭头”,如何用GIS绘制?

小伙伴们, 在各种分析图、空间结构图中, 是不是经常看到这种“扇形”的箭头, 同时伴随着一些“引绿入城”等分析语言。 于是,你有没有好奇, 这种箭头是怎么做出来的? 强大的GIS是否可以绘制出这种箭头? 还是那句话—— 别问,问就是能! 其实ArcMap 和 ArcGIS P…

【大数据实战电商推荐系统】概述版

文章目录 第1章 项目体系框架设计&#xff08;说明书&#xff09;第2章 工具环境搭建&#xff08;说明书&#xff09;第3章 项目创建并初始化业务数据3.1 IDEA创建Maven项目&#xff08;略&#xff09;3.2 数据加载准备&#xff08;说明书&#xff09;3.3 数据初始化到MongoDB …

41. 同时在线人数问题

文章目录 题目需求思路一实现一学习链接题目来源 题目需求 现有各直播间的用户访问记录表&#xff08;live_events&#xff09;如下。 表中每行数据表达的信息为&#xff1a;一个用户何时进入了一个直播间&#xff0c;又在何时离开了该直播间。 现要求统计各直播间最大同时在…

Stable Diffusion系列课程上:安装、提示词入门、常用模型(checkpoint、embedding、LORA)、放大算法、局部重绘、常用插件

文章目录 一、Stable Diffusion简介与安装二、文生图&#xff08;提示词解析&#xff09;2.1 提示词入门2.2 权重2.3 负面提示词&#xff08; Negative prompt&#xff09;2.4 出图参数设置2.5 新手念咒方法 三、图生图3.1 图生图入门3.2 随机种子解析3.3 图生图拓展 四、模型4…

MySQL基础篇第3章(运算符)

文章目录 1、算术运算符1.1 加法与减法运算符1.2 乘法与除法运算符1.3 求模&#xff08;求余&#xff09;运算符 2、比较运算符2.1 等号运算符2.2 安全等于<>2.3 不等于运算符2.4 空运算符2.5 非空运算符2.6 最小值运算符2.7 最大值运算符2.8 BETWEEN AND运算符2.9 IN运算…

cmake操作目录

目录 cmake如何使用子目录 demo cmake生成build目录结构 如果指定子目录编译文件名字(binaryDir) 如果指定子目录编译的路径(binaryDir) 子目录相关的作用域 demo 子目录中定义project cmake如何使用子目录 如果项目比较小的话,我们将所有源码文件放到一个目录里面是没…

wpf border控件和Effect学习

Border&#xff08;边框&#xff09;控件绘制一个边框、一个背景。 常用的属性&#xff0c; Background&#xff1a;填充 Border 边界之间的区域或者说是绘制该区域的背景&#xff0c;是一个Brush对象。 BorderBrush&#xff1a;用于绘制外部边框颜色&#xff0c;是Bru…

Vue 和 React 前端框架的比较

Vue 和 React 前端框架的比较 本文研究了流行的前端框架 Vue 和 React 之间的区别。通过对它们的学习曲线、视图层处理方式、组件化开发、响应式数据处理方式和生态系统及社区支持进行比较分析&#xff0c;得出了它们在不同方面的优劣和特点。该研究对于开发者在选择合适的前端…

QC51XX---I2c使用

QCC51XX---系统学习目录_嵌入式学习_force的博客-CSDN博客 今天我们聊聊I2C的使用。在开发过程中多多少少会加入一些外围传感器可以给用户带来更好的使用体验。例如,利用接近传感器识别戴上耳机和取下耳机,从而去自动暂停播放音乐,或接听转移通话。又例如,用触摸或敲击替代…

【vant】打开vant表单的正确形式(基于vant表单的二次封装)

前言 最近在用vant做关于移动端的项目,由于表单字段太多,不想写直接写到template中,这样太繁琐了,所以我们以把表单弄成schema配置形式: // data.ts import type { ItemSchema } from /typing/helper; import { StudentField } from /components; import { getDictTextByCode…

在tplink路由器xdr6088中运行Docker潘多拉(pengzhile/pandora)遇到无法访问的问题

在xdr6088中搜索安装pengzhile/pandora一切正常&#xff0c;但是按照常规运行docker后&#xff0c;直接访问8899端口无法打开页面&#xff0c;进入终端 运行如下命令 /usr/local/bin/python /usr/local/bin/pandora-cloud -s 0.0.0.0:8899 即可成功运行&#xff0c;然后客户端…

RabbitMQ在SpringBoot中的高级应用(2)

过期时间 1.单独的设置队列的存活时间,队列中的所有消息的过期时间一样 Bean//创建交换机public DirectExchange ttlQueueExchange(){// 交换机名称 是否持久化 是否自动删除return new DirectExchange("ttl_queue_log",true,false);}Bean//创建队列publ…