1. 链表与数组

链表 ,数组 ,先看看百度怎么说。

上面的说法太专业,用简单的话来讲就是:链表和数组都是用来存储数据的一种数据结构,各有利弊。
数组:线性数据结构,存放的数据的类型是一样的,而且他们在内存中的排布是有序排列的。因此数组的优势就是数据连续,随机访问速度快,定义好了就不好在改变大小.
单链表:由一个个节点(node)组成,每个节点都有指向下一个节点的指针。节点的连接方向是单向的,节点之间用指针连起来,所有结构体类型可以不一样,链表的大小也可以动态变化。但是不能随机访问数据,只能遍历。

举一个栗子:一个班的学生的信息要存储起来,用数组肯定不太方便,学生的属性很多如:

sturct stu{
char name[10];
uint8_t math;
uint8_t  physics;
float height;
}

这里就可以用链表把一个班的学生整整齐齐的放在一起,考试出成绩的时候,就可以遍历了。

RT-Thread 的内核中就使用到了链表,所以这些 API 我们都是可以直接使用的,而不需要自己再去造轮子。

2. 单链表怎么使用

struct rt_slist_node
{struct rt_slist_node *next;                         /**< point to next node. */
};
typedef struct rt_slist_node rt_slist_t;                /**< Type for single list. */

结构体只有指向下一个节点的指针。

  1. 初始化一个单链表
rt_slist_t list;
rt_slist_init(&list);
  1. 在单链表的末尾插入新的链表
rt_slist_t nlist1;
rt_slist_append(&list, &nlist1)
  1. 在节点 L 之后插入一个新的节点
rt_slist_t nlist2;
rt_slist_insert(&list, &nlist2)
  1. 获取链表的长度
unsigned int len = 0;
len = rt_slist_len(&list);
  1. 移除一个节点
rt_slist_t nlist3;
nlist3 = rt_slist_remove(&list,&nlist2);
  1. 获取第一个节点
rt_slist_t nlist4;
nlist4 = rt_slist_insert(&list);
  1. 获取下一个节点
rt_slist_t nlist5;
nlist5 = rt_slist_insert(&list);
  1. 判断单链表是否为空,为空返回 1
int res;
res = rt_slist_isempty(&list);
  1. 获取节点所在结构体的类型
rt_slist_entry(node(节点), struct (结构体), list(链表所在结构体成员中的名字))
  1. 遍历链表
rt_slist_for_each(node(节点),head(头结点))
  1. 遍历链表中的结构体成员
rt_slist_for_each_entry(node(节点), struct (结构体), list(链表所在结构体成员中的名字))

3. 单链表的实现

  1. 初始化链表
rt_inline void rt_slist_init(rt_slist_t *l)
{l->next = RT_NULL;
}

指针指向下一个节点 RT_NULL

  1. rt_slist_append
rt_inline void rt_slist_append(rt_slist_t *l, rt_slist_t *n)
{struct rt_slist_node *node;node = l;while (node->next) node = node->next;/* append the node to the tail */node->next = n;n->next = RT_NULL;
}

遍历到最后一个节点,然后把插入新的节点,新的节点的 next 指向 RT_NULL

  1. 插入一个新的节点
rt_inline void rt_slist_insert(rt_slist_t *l, rt_slist_t *n)
{n->next = l->next;l->next = n;
}

l 节点后面插入 n 节点

  1. 获取链表的长度
rt_inline unsigned int rt_slist_len(const rt_slist_t *l)
{unsigned int len = 0;const rt_slist_t *list = l->next;while (list != RT_NULL){list = list->next;len ++;}return len;
}

遍历节点求出链表的长度

  1. 移除一个节点
rt_inline rt_slist_t *rt_slist_remove(rt_slist_t *l, rt_slist_t *n)
{/* remove slist head */struct rt_slist_node *node = l;while (node->next && node->next != n) node = node->next;/* remove node */if (node->next != (rt_slist_t *)0) node->next = node->next->next;return l;
}

l 的 next 指向 n 的next

  1. 获取链表头
rt_inline rt_slist_t *rt_slist_first(rt_slist_t *l)
{return l->next;
}

找到 l 的 next

  1. 获取最后一个节点
rt_inline rt_slist_t *rt_slist_tail(rt_slist_t *l)
{while (l->next) l = l->next;return l;
}

遍历链表找到最后一个节点

  1. 获取下一个节点
rt_inline rt_slist_t *rt_slist_next(rt_slist_t *n)
{return n->next;
}

找到 n 的 next

  1. 判断链表是否为空
rt_inline int rt_slist_isempty(rt_slist_t *l)
{return l->next == RT_NULL;
}

直接判断 l -> next 是否为 RT_NULL

  1. 获取链表所在的结构体
#define rt_slist_entry(node, type, member) \rt_container_of(node, type, member)

这是一个宏

#define rt_container_of(ptr, type, member) \((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

这个展开分析一下
(char *) (ptr) - (unsigned long)(&((type *)0)->member)
再分成两部分

  • (char *) (ptr): ptr 本来的类型是rt_slist_t * ,把 ptr 强转成 char*
  • (&((type *)0)->member): (type *)0)假设地址0处存放的是一个type类型的结构体变量,memberlist 所在的位置,这样就知道了 membertype 的偏移量

这样就可以知道结构体的地址往前移动 membertype 的偏移量 就得到了,type 的首地址
最后强转成 (type *), 这样就得到了结构体类型指针的首地址,这样就得到了节点所在的结构体

  1. 遍历链表
#define rt_slist_for_each(pos, head) \for (pos = (head)->next; pos != RT_NULL; pos = pos->next)

通过 next 不为空来遍历链表

  1. 遍历链表获取结构体
#define rt_slist_for_each_entry(pos, head, member) \for (pos = rt_slist_entry((head)->next, typeof(*pos), member); \&pos->member != (RT_NULL); \pos = rt_slist_entry(pos->member.next, typeof(*pos), member))

上面两个宏的结合。

4. 最后

作为一名合格的程序员一定要熟练的掌握链表。
如果上面的 插入 移除 API 没看明白的建议自己画个图,一下就明白了。

RT-Thread 隐藏的宝藏之单链表相关推荐

  1. 三、单链表增删改查原理和代码实现

    单链表 1.简单介绍 (1)单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素 ...

  2. java 链表逆转_java 实现单链表逆转详解及实例代码

    java 实现单链表逆转详解 实例代码: class Node { Node next; String name; public Node(String name) { this.name = nam ...

  3. c语言单链表就地逆置编程,C++ 单链表的 就地逆置 ,以及基本操作

    #include "stdafx.h" #define sub(a,b) a-b //没用 #include using namespace std; struct node { ...

  4. Go 学习笔记(80)— Go 标准库 container/list(单链表、双链表)

    列表是一种非连续存储的容器,由多个节点组成,节点通过一些变量记录彼此之间的关系.列表有多种实现方法,如单链表.双链表等. ​ 在 Go 语言中,将列表使用 container/list 包来实现,内部 ...

  5. 数据结构(08)— 线性单链表基本操作

    1. 线性单链表数据结构 // 假定每个结点的类型用 SNode 表示 typedef struct SNodeTag {int data; // 所存储的数据元素SNodeTag *next; // ...

  6. 数据结构(05)— 线性单链表实战

    1. 设计思路 本项目的实质是完成对考生信息的建立.查找.插入.修改.删除等功能,可以首先定义项目的数据结构,然后将每个功能写成一个函数来完成对数据的操作,最后完成主函数以验证各个函数功能并得出运行结 ...

  7. 带头节点单链表的增删改查

    单链表有很多结构循环单链表,有头节点的单链表,无头节点的单链表,双节点单链表,以下源码是以有一个头节点的单链表为例写的增删改查的各种功能,就是下图 然后各个注释也在函数后面写着,这玩意确实还挺难,源码 ...

  8. 数据结构--单链表(数组)

    文章目录 单链表 单链表 用数组实现的单链表可以在任意位置插入.删除.替换一个数,相比于用结构体和指针实现的动态链表而言,数组模拟链表运行的速度非常快,动态链表基本会出现超时的现象.但缺点是浪费内存 ...

  9. 数据结构和算法:(3)3.2.2单链表的整表删除

    我们不使用这个单链表时,我们需要把他在内存中释放掉,以便于流出空间给其他程序和软件使用. 单链表整表删除的算法思路如下: 声明结点p和q: 将第一个结点赋值给p,下一个结点(也就是第一个结点的下一个结 ...

最新文章

  1. Pytorch两种模型保存方式
  2. 如何自学Python?这本技术大咖推荐Python书籍,就是你的第一选择
  3. vue封装第三方插件并发布到npm
  4. 用Matplotlib跟踪疫情实时监控2019-nCoV
  5. VTK修炼之道11:基本数据结构_数据对象数据集
  6. JS面向对象(二)——构造函数的继承
  7. 《Web全栈工程师的自我修养》
  8. Hello World!!!
  9. 微信小程序实战 购物车功能
  10. hdfs java api读写
  11. 计算机组成原理实验存储器部件实验,计算机组成原理实验_存储器部件教学实验...
  12. 猜数游戏控制猜测次数c语言,C语言小游戏之猜数字,三子棋游戏
  13. UI框架-JQuery Smart / 淘宝JS库 KISSY UI
  14. c语言抽签系统_抽签小程序(C语言随机数)
  15. 用 Java 实现的八种常用排序算法
  16. EntityRef:expecting“;”
  17. Unity打开Windows虚拟键盘
  18. Ubuntu Linux出现IP inet6 addr: fe80::fe0:9b43:8a0e:2463/64的解决办法
  19. Microsoft Remote Desktop提示「Your session was disconnected」
  20. NT151应用案例:西门子PLC S7-1200与派克ACR9000的通信(Part 4)

热门文章

  1. Android入门学习_Android获取来电号码
  2. 磷酸酶、转录因子、KRAS ——“不可成药”靶点?
  3. 迅雷看看评分js实现
  4. msm android机型适配,英雄联盟手游安卓及iOS适配机型汇总 LOL手游适配机型一览
  5. java被3整除_被3整除
  6. ChatGPT进入百度“弱智吧”后,疯了
  7. [导入]如何学习英语(英语学习中最重要的五点)
  8. 计算机网络技术店面取名,电脑店铺起名
  9. 注意力机制 Attention
  10. 家长会计算机教师致辞,开家长会教师发言稿