PyTorch中的符号索引和函数索引用法

news2024/9/29 15:27:30

Pytorch中很多函数都采用的是函数式索引的思路,而且使用函数式索引对代码可读性会有很大提升。

张量的符号索引

张量也是有序序列,我们可以根据每个元素在系统内的顺序位置,来找出特定的元素,也就是索引。

一维张量的索引

一维张量索引与Python中的索引一样是是从左到右,从0开始的,遵循格式为[start: end: step]

>>> data = torch.arange(1, 11)
>>> print(data)
tensor([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
>>> data[0]
tensor(1)

张量索引出的结果是零维张量,而不是单独的数。要转化成数,需要使用item()方法:

>>> data[0].item()
1

批注:构成一维张量的是零维张量,而不是单独的数。

>>> data[3:9:2] # 隔2个数取一个,左闭右开
tensor([4, 6, 8])

在Python中,step可以为负数,例如:

>>> num_list = [1, 2, 3]
>>> num_list[::-1]
[3, 2, 1]

但在张量中,step必须大于0,否则就会报错。

>>> num_tensor = torch.arange(1, 11)
>>> num_tensor[::-1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: step must be greater than zero

二维张量的索引

二维张量的索引逻辑和一维张量的索引逻辑相同,二维张量可以视为两个一维张量组合而成。

>>> data2 = torch.arange(1, 21).reshape(4, 5)
>>> data2
tensor([[ 1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10],
        [11, 12, 13, 14, 15],
        [16, 17, 18, 19, 20]])
>>> data2[0, 1], data2[0][1]  # 这两种索引方式都可以
(tensor(2), tensor(2))

但是data2[::2, ::2]data2[::2][ ::2]的索引结果就不同:

>>> data2[::2, ::2]
tensor([[ 1,  3,  5],
        [11, 13, 15]])
>>> data2[::2][::2]
tensor([[1, 2, 3, 4, 5]])

解释:

  • t2[::2, ::2]二维索引使用逗号隔开时,可以理解为全局索引,取第一行和第三行的第一列和第三列的元素。
  • t2[::2][::2]二维索引在两个中括号中时,可以理解为先取了第一行和第三行,构成一个新的二维张量,然后在此基础上又间隔2并对所有张量进行索引。
>>> d = data2[::2]
>>> d
tensor([[ 1,  2,  3,  4,  5],
        [11, 12, 13, 14, 15]])
>>> d[::2]
tensor([[1, 2, 3, 4, 5]])

三维张量的索引

设三维张量的shapexyz,则可理解为它是由x个二维张量构成,每个二维张量由y个一维张量构成,每个一维张量由z个元素构成。

>>> data3 = torch.arange(1, 25).reshape(2, 3, 4)
>>> data3
tensor([[[ 1,  2,  3,  4],
         [ 5,  6,  7,  8],
         [ 9, 10, 11, 12]],

        [[13, 14, 15, 16],
         [17, 18, 19, 20],
         [21, 22, 23, 24]]])
>>> data3[1, 1, 1]
tensor(18)
>>> data3[1, ::2, ::2]
tensor([[13, 15],
        [21, 23]])

高维张量的思路与低维一样,就是围绕张量的“形状”进行索引。

张量的函数索引

除了常⽤的索引选择数据之外,PyTorch还提供了⼀些⾼级的选择函数:

  • index_select(input, dim, index):在指定维度dim上选取,⽐如选取某些⾏、某些列
  • masked_select(input, mask):例⼦如上,a[a>0],使⽤ByteTensor进⾏选取
  • non_zero(input):⾮0元素的下标
  • gather(input, dim, index):根据index,在dim维度上选取数据,输出的sizeindex⼀样

index_select()

index_select(dim, index)表示在张量的哪个维度进行索引,索引的位置是多少。

torch.index_select()函数返回的是沿着输入张量的指定维度指定索引号进行索引的张量子集

torch.index_select(input, dim, index, out=None)

其函数参数有:

  • input(Tensor) - 需要进行索引操作的输入张量;
  • dim(int) - 需要对输入张量进行索引的维度;
  • index(LongTensor) - 包含索引号的 1D 张量;

index_select函数指定index来对张量进行索引,index的类型必须为Tensor

由于 index_select 函数只能针对输入张量的其中一个维度的一个或者多个索引号进行索引,因此可以通过 PyTorch 中的高级索引来实现。下面列举三个例子来说明这个函数的用法:

  1. 获取1D张量的第1个维度且索引号为2和3的张量子集
>>> data = torch.arange(9)
>>> sub_data1 = torch.index_select(data, dim=0, index=torch.tensor([2, 3]))
>>> sub_data2 = data[[2, 3]]
>>> data
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> sub_data1
tensor([2, 3])
>>> sub_data2
tensor([2, 3])
  1. 获取2D张量的第2个维度且索引号为0和1的张量子集(第一列和第二列)
>>> data2 = torch.arange(9).view(3, 3)
>>> sub_data1 = torch.index_select(data2, dim=1, index=torch.tensor([0, 1]))
>>> sub_data2 = data2[:, [0, 1]]
>>> data2
tensor([[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]])
>>> sub_data1
tensor([[0, 1],
        [3, 4],
        [6, 7]])
>>> sub_data2
tensor([[0, 1],
        [3, 4],
        [6, 7]])
  1. 获取3D张量的第1个维度且索引号为0的张量子集
>>> data3 = torch.arange(18).view(2, 3, 3)
>>> sub_data1 = torch.index_select(data3, dim=0, index=torch.tensor([0]))
>>> sub_data2 = data3[0]
>>> print(data3)
tensor([[[ 0,  1,  2],
         [ 3,  4,  5],
         [ 6,  7,  8]],

        [[ 9, 10, 11],
         [12, 13, 14],
         [15, 16, 17]]])
>>> print(sub_data1)
tensor([[[0, 1, 2],
         [3, 4, 5],
         [6, 7, 8]]])
>>> print(sub_data2)
tensor([[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]])

index_select 函数虽然简单,但是有几点需要注意:

  • index 参数必须是1D长整型张量 (1D-LongTensor);
  • 使用 index_select 函数输出的张量维度和原始的输入张量维度相同。(即是说,原来是三维的张量,就会输出三维的张量)
>>> data_rand3 = torch.rand(3, 4)
>>> sub_data1 = torch.index_select(data_rand3, dim=0, index=torch.tensor([0]))
>>> sub_data2 = data_rand3[[0]]
>>> sub_data3 = data_rand3[0]
>>> print(sub_data1)
tensor([[0.1926, 0.6743, 0.9063, 0.0857]])
>>> print(sub_data2)
tensor([[0.1926, 0.6743, 0.9063, 0.0857]])
>>> print(sub_data3)
tensor([0.1926, 0.6743, 0.9063, 0.0857])
>>> print(sub_data1.size(), sub_data2.size(), sub_data3.size())
torch.Size([1, 4]) torch.Size([1, 4]) torch.Size([4])

上面的代码示例可以说明,三种方式索引出来的张量子集中的元素是一样的,不同的是索引出来的张量子集的形状。所以,前文才说 index_select 函数对输入张量进行索引可以使用高级索引实现。

masked_select()

masked_select()函数返回一个根据布尔掩码 (boolean mask) 索引输入张量的 1D 张量。其用法如下:

torch.masked_select(input, mask, out=None) → Tensor

具体地:

  • input (Tensor) : 输入张量
  • mask (ByteTensor) : 掩码张量,包含了二元索引值
  • out (Tensor, optional) : 目标张量
>>> data = torch.randn(5, 4)
>>> mask_index = data.ge(0)  # 筛选大于0的结果
>>> res = torch.masked_select(data, mask_index)
>>> print(data)
tensor([[ 2.5874, -1.5814, -0.6473, -0.1795],
        [-0.4612,  0.2462,  0.5025,  0.9862],
        [ 1.2485,  0.6655,  1.5536,  0.7446],
        [-1.2433,  1.8842, -0.6330, -0.8245],
        [-0.5634, -1.1724,  1.3369,  0.5930]])
>>> print(res)
tensor([2.5874, 0.2462, 0.5025, 0.9862, 1.2485, 0.6655, 1.5536, 0.7446, 1.8842,
        1.3369, 0.5930])

masked_select 函数最关键的参数就是布尔掩码 mask参数。其通过布尔张量maskTrue或者False来决定输入张量对应位置的元素是否保留,最后返回一维张量。

很明显,这种操作是一一对应的关系(True就保留,False就舍去),这就需要maskinput的形状相同。

  • 两者的形状可以完全相同,也即是input.shape = mask.shape
  • 广播机制,两者的形状可以不完全相同,但是必须要能够通过 PyTorch 中的广播机制广播成相同形状的张量。

广播机制 (Broadcast) 是在科学运算中经常使用的小技巧,它是一种轻量级的张量复制手段,只在逻辑层面扩展和复制张量,并不进行实际的存储复制操作,从而大大的减少了计算代价。但并不是所有形状不一致的张量都能进行广播,需要满足一定的规则。比如对于两个张量来说:

  • 如果两个张量的维度不同,则将维度小的张量进行扩展,直到两个张量的维度一样;
  • 如果两个张量在对应维度上的长度相同或者其中一个张量的长度为 1,那么就说这两个张量在该维度上是相容的;
  • 如果两个张量在所有维度上都是相容的,表示这两个张量能够进行广播,否则会出错;
  • 在任何一个维度上,如果一个张量的长度为 1,另一个张量的长度大于 1,那么在该维度上,就好像是对第一个张量进行了复制;

masked_select 函数中的广播机制比较简单,只需要保证输入张量不变,对布尔张量进行广播,而广播后的形状和输入张量的形状一致就可以了。

>>> data = torch.arange(8).view(2, 4)
>>> mask2 = torch.tensor([True, True, False, True])  # 能广播
>>> print(data)
tensor([[0, 1, 2, 3],
        [4, 5, 6, 7]])
>>> print(torch.masked_select(data, mask2))
tensor([0, 1, 3, 4, 5, 7])

需要注意两点:

  • 使用 masked_select 函数返回的结果都是 1D 张量,张量中的元素就是被筛选出来的元素值;
  • 传入 input 参数中的输入张量和传入 mask 参数中的布尔张量形状可以不一致,但是布尔张量必须要能够通过广播机制扩展成和输入张量相同的形状;

问题来了,注意看下面的代码示例:

>>> data = torch.randn(3, 4)
>>> mask = data.ge(0)
>>> print(data[mask])
>>> print(torch.masked_select(data, mask))
tensor([[False,  True,  True, False],
        [ True, False,  True,  True],
        [ True, False,  True,  True]])
tensor([0.5393, 0.2735, 0.9606, 0.0107, 0.0654, 0.8304, 0.8467, 0.0034])
tensor([0.5393, 0.2735, 0.9606, 0.0107, 0.0654, 0.8304, 0.8467, 0.0034])

可以发现,masked_select函数其实没太大必要,直接通过data[mask]就可以达到效果了。那这个函数存在的意义在哪呢?这个问题留待后续…

non_zero()

non_zero()函数用于输出数组的非零值的索引,即用来定位数组中非零的元素。其用法如下:

torch.nonzero(input, *, out=None, as_tuple=False) → LongTensor or tuple of LongTensors

参数为:

  • input:输入的数组
  • as_tuple:函数返回方式,默认为False

如果设为False,则返回一个二维张量,其中每一行都是非零值的索引,如果输入的数组有 n n n维,则输出的张量维度大小为 z × n z\times n z×n,其中 z z zinput非零元素的总数。

>>> a = torch.randn(3, 5)
>>> a = torch.where(x < 0, x, 0)  # 将非负元素置0
>>> print(a)
tensor([[ 0.0000, -1.0246, -0.2621,  0.0000],
        [ 0.0000, -0.7053, -0.8949, -0.3949],
        [ 0.0000, -0.1732, -0.4669,  0.0000],
        [ 0.0000, -1.0170, -1.1945,  0.0000],
        [ 0.0000,  0.0000,  0.0000, -0.4522]])
>>> print(torch.nonzero(a))
tensor([[0, 1],
        [0, 2],
        [1, 1],
        [1, 2],
        [1, 3],
        [2, 1],
        [2, 2],
        [3, 1],
        [3, 2],
        [4, 3]])

这里nonzero函数输出的结果,就是非零元素的索引。比如第一行[0, 1]代表这源Tensor里面第0行第1列的元素非零。

如果as_tuple设为True,则返回一个由一维张量组成的元组。看下面的输出:

>>> a = torch.randn(3, 5)
>>> a = torch.where(x < 0, x, 0)  # 将非负元素置0
>>> print(a)
tensor([[ 0.0000, -1.0246, -0.2621,  0.0000],
        [ 0.0000, -0.7053, -0.8949, -0.3949],
        [ 0.0000, -0.1732, -0.4669,  0.0000],
        [ 0.0000, -1.0170, -1.1945,  0.0000],
        [ 0.0000,  0.0000,  0.0000, -0.4522]])
>>> print(torch.nonzero(a, as_tuple=True))
(tensor([0, 0, 1, 1, 1, 2, 2, 3, 3, 4]), tensor([1, 2, 1, 2, 3, 1, 2, 1, 2, 3]))

如果输入数组为 n n n维,则有 n n n个一维张量,每个一维张量对应非零元素特定维度的索引(第一个张量数组储存的是所有非零元素第一维度的索引),并且每个张量里面有 z z z个数,其中 z z z为输入数组非零元素的个数。

这个函数除了找出非零元素外,还可用于特定元素定位,比如:

>>> a = torch.arange(12).view(3, 4)
>>> print(a)
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])
>>> torch.nonzero(a == 6)  # 输入元素为6的位置
tensor([[1, 2]])

gather()

gather()函数作用:沿给定轴dim,将输入索引张量index指定位置的值进行聚合。(沿着给定的维度dim收集值)

其用法为:

torch.gather(input, dim, index, out=None)

参数解释为:

  • input(Tensor):源张量
  • dim(int):索引的轴
  • index(LongTensor):聚合元素的下标
  • out:目标张量

注意:index的维度要和inputdim所指的维度相同

torch.gather()常用索引多分类中标签所对应的概率。

例子1:按照dim = 0, 取一个二维张量对角线上的数值

>>> a = torch.tensor([[2, 3, 5], [4, 9, 7]])
>>> index = torch.LongTensor([[0, 1, 0]])
>>> b = torch.gather(a, dim=0, index=index)
>>> print(a)
tensor([[2, 3, 5],
        [4, 9, 7]])
>>> print(b)
tensor([[2, 9, 5]])

可以看到dim=0,即行方向的维度和index的维度是匹配的,就是说aindex由行方向从左往右看,有2列,即有2个样本,行方向是匹配的。另外,函数输出的tensorindex大小相同。

上面代码的操作逻辑可以用下图来表示。
在这里插入图片描述
具体地:在a中,由行从左往右看,有两个样本,索引分别为0和1;每个样本有两个特征,每个特征中从上往下索引分别为0和1;依据index中的索引值,取第0样本的第0个特征2,再取第1个样本的第1个特征7。

例子2:按照dim = 1, 取一个二维张量的对角线上的数值

>>> a = torch.tensor([[2, 3], [4, 9], [6, 10]])
>>> index = torch.LongTensor([[0], [1], [0]])
>>> b = torch.gather(a, dim=1, index=index)
>>> print(a)
tensor([[ 2,  3],
        [ 4,  9],
        [ 6, 10]])
>>> print(b)
tensor([[2],
        [9],
        [6]])

可以看到dim=1,即列方向的维度和index的维度是匹配的,就是说aindex由列方向从上往下看,有3行,即有3个样本,列方向是匹配的。另外,函数输出的tensorindex大小相同。

上面代码的操作逻辑可以用下图来表示。
在这里插入图片描述

具体地:在a中,由列从上往下看,有三个样本,索引分别为0、1和0;每个样本有两个特征,每个特征中从左往右索引分别为0和1;依据index中的索引值,取第0样本的第0个特征2,取第1个样本的第1个特征7,再取第2个样本的第1个特征6。

例子3:模拟多分类问题中的概率取值(跟上面的两个例子一样)

>>> a = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
>>> index = torch.LongTensor([[0], [2]])
>>> a
tensor([[0.1000, 0.3000, 0.6000],
        [0.3000, 0.2000, 0.5000]])
>>> a.gather(dim=1, index=index)
tensor([[0.1000],
        [0.5000]])

总结:根据维度按行或者列根据索引取值

  • dim=0:在列上按索引取值
  • dim=1:在行上按索引取值

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

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

相关文章

离线安装JumpServer

官网操作手册&#xff1a; https://docs.jumpserver.org/zh/v3/installation/setup_linux_standalone/offline_install/ 环境要求&#xff1a;&#xff08;内存最小需要4G&#xff09; 架构图 安装部署 1、下载 JumpServer官网下载&#xff1a; https://community.fit2cloud…

定点数的二进制表示形式

定点数的二进制表示形式 文章目录定点数的二进制表示形式什么是定点数表示格式数值范围与分辨率转换python 转换定点数C 双精度浮点数转换为8位和16位定点数C 将定点数转回浮点数测试什么是定点数 在嵌入式系统中&#xff0c;为了降低运算复杂度&#xff0c;通常还会使用定点数…

有趣的数学之回文数

“回文”是指正读反读都能读通的句子&#xff0c;它是古今中外都有的一种修辞方式和文字游戏&#xff0c;如“我为人人&#xff0c;人人为我”等&#xff0c;最有名的莫过于“上海自来水来自海上&#xff0c;人过大佛寺佛大过人 ”。你们知道吗&#xff0c;在数学中也有这样一类…

30多份软件测试报告模板,如何写一份优秀测试报告模板流程

相信很多做软件测试的小伙伴在软件测试后期&#xff0c;都为软件测试报告总结花费了很多的精力&#xff0c;那么如何做好软件测试报告呢&#xff1f;一份优秀的测试报告又包含哪些内容呢&#xff1f; 测试报告的核心要素 一、测试结论 从测试工程师的专业角度分析&#xff0…

pytorch进阶学习(四):使用不同分类模型进行数据训练(alexnet、resnet、vgg等)

课程资源&#xff1a;5、帮各位写好了十多个分类模型&#xff0c;直接运行即可【小学生都会的Pytorch】_哔哩哔哩_bilibili 目录 一、项目介绍 1. 数据集准备 2. 运行CreateDataset.py 3. 运行TrainModal.py 4. 如何切换显卡型号 二、代码 1. CreateDataset.py 2.Train…

如何基于ChatGPT+Avatar搭建24小时无人直播间

0 前言 最近朋友圈以及身边很多朋友都在研究GPT开发&#xff0c;做了各种各样的小工具小Demo&#xff0c;AI工具用起来是真的香&#xff01;在他们的影响下&#xff0c;我也继续捣鼓GPT Demo&#xff0c;希望更多的开发者加入一起多多交流。 上一篇结合即时通 IM SDK捣鼓了一个…

因为这三个面试题,我与字节offer失之交臂

我一个朋友挑战3个月入职字节&#xff0c;一路过关斩将直到终面&#xff0c;着实把我惊了一把&#xff0c;可惜的是&#xff0c;他倒在了最后三个面试题上。 我很讶异&#xff0c;前面不是打得很好吗&#xff1f;怎么会在最后几题上犯错误呢&#xff1f; 朋友说&#xff1a;别…

电瓶隔离器工作原理与发展简史

电瓶隔离器(Battery Isolators)工作原理与发展简史 电池隔离器(英文&#xff1a;Battery Isolators)&#xff0c;又叫双电池隔离器、双电瓶隔离器、双电瓶保护器&#xff0c;还有叫双电池分离器的。 电瓶隔离器其实并没有真正的隔离&#xff0c;负极是始终连在一起的。房车、…

拓展系统命令

文章目录拓展系统命令使用方式拓展系统命令快速运行方法命令 - ZFASTRUN安全运行方法命令 - ZFASTSAFERUN快速运行Query方法命令 - ZFASTQUERY安全运行Query方法 命令 - ZSAFEQUARY防止调试时误将数据提交命令 - ZTRN在Terminal执行SQL语句命令 - ZSQL安全Global命令 - ZSAFEKI…

动态内存管理【上篇】

文章目录⚙️1.为什么存在动态内存分配⚙️2.动态内存函数的介绍&#x1f4ec;2.1. malloc函数&#x1f4ec;2.2. free函数&#x1f4ec;2.3. calloc函数&#x1f4ec;2.4. realloc函数⚙️3.常见的动态内存错误&#x1f512;3.1.对NULL指针的解引用操作&#x1f512;3.2.对动态…

二叉树(OJ)

单值二叉树&#xff08;力扣&#xff09; ---------------------------------------------------哆啦A梦的任意门------------------------------------------------------- 我们来看一下题目的具体要求&#xff1a; 既然我们都学了二叉树了&#xff0c;我们就应该学会如何去…

笔记:Java关于轻量级锁与重量级锁之间的问答

问题&#xff1a;如果在轻量级锁状态下出现锁竞争&#xff0c;不一定会直接升级为重量级锁&#xff0c;而是会先尝试自旋获取锁&#xff0c;那么有a b两个线程竞争锁&#xff0c;a成功获取锁了&#xff0c;b就一定失败&#xff0c;那么轻量级锁就一定升级为重量级锁&#xff0c…

基于Bazel + SQLFluff实现SQL lint

背景SQL进行版本化控制后&#xff0c;我们希望为SQL加入lint步骤。这样做的好处是我们可以在真正执行SQL前发现问题。本文中&#xff0c;我们通过Bazel执行SQLFluff[1]以实现SQL的lint。SQLFluff是一款使用Python语言使用的&#xff0c;支持SQL多方言的SQL lint工具。它的特点是…

设计模式-创建型模式之单例模式

6.单例模式6.1. 模式动机对于系统中的某些类来说&#xff0c;只有一个实例很重要&#xff0c;例如&#xff0c;一个系统中可以存在多个打印任务&#xff0c;但是只能有一个正在工作的任务&#xff1b;一个系统只能有一个窗口管理器或文件系统&#xff1b;一个系统只能有一个计时…

360安全卫士退出企业安全云模式

360安全卫士退出企业安全云模式前言360企业安全云关闭企业安全云提醒退出企业安全云模式前言 360安全卫士推出了企业安全云&#xff0c;并会给个人版用户进行推送&#xff0c;虽然可以关闭&#xff0c;但有可能会不小心升级为企业安全云&#xff0c;用户可能并不不习惯&#x…

2023铜鼓半马5月14日开跑,4月18日启动报名!

长寿铜鼓&#xff0c;康养胜地&#xff01;众翼电气2023铜鼓半程马拉松暨英雄马系列赛&#xff08;铜鼓站&#xff09;新闻发布会今日召开&#xff0c;铜鼓县委常委、宣传部部长熊涛&#xff0c;铜鼓县教育体育局党委书记、局长孙桃基&#xff0c;铜鼓县文广新旅局党组书记、局…

SpringBoot API 接口防刷

SpringBoot API 接口防刷接口防刷接口防刷原理代码实现RequestLimit 注解RequestLimitIntercept 拦截器WebMvcConfig配置类Controller控制层验证接口防刷 接口防刷: 顾名思义&#xff0c;想让某个接口某个人在某段时间内只能请求N次。 在项目中比较常见的问题也有&#xff0c;…

【Python】Python程序中使用request库连接外国网站的方法

确认你的socks端口&#xff1a; 然后程序可以这么写&#xff1a; import requests import socks import socket# 创建 SOCKS5 代理连接 socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 10808) socket.socket socks.socksocket# 发送请求 response request…

Java高级特性 - 多线程基础(2)常用函数【第1关:线程的状态与调度 第2关:常用函数(一)第3关:常用函数(二)】

目录 第1关&#xff1a;线程的状态与调度 第2关&#xff1a;常用函数&#xff08;一&#xff09; 第3关&#xff1a;常用函数&#xff08;二&#xff09; 第1关&#xff1a;线程的状态与调度 相关知识 为了完成本关你需要掌握&#xff1a; 1.线程的状态与调度&#xff1b…

Linux内核中常用的数据结构和算法

文章目录链表红黑树无锁环形缓冲区Linux内核代码中广泛使用了数据结构和算法&#xff0c;其中最常用的两个是链表和红黑树。 链表 Linux内核代码大量使用了链表这种数据结构。链表是在解决数组不能动态扩展这个缺陷而产生的一种数据结构。链表所包含的元素可以动态创建并插入和…