浅谈线段树

文章同步发布于洛谷,建议前往洛谷查看。

前言

蒟蒻终于学会线段树(指【模板】线段树 1 1 1)啦!

线段树思想

我们先来考虑 P3372(基础线段树模板题)给的操作:

  1. 区间修改(增加)
  2. 区间查询(求和)

很重要的一点是,两者是交叉的,要不然可以使用好写时间复杂度又优秀又好吃的前缀和秒了。

O ( 1 ) \mathcal O(1) O(1) 是困难的,所以我们思考可不可以 O ( log ⁡ n ) \bm{\mathcal O(\log n)} O(logn) 实现。

分段

目标

我们可以把一个区间分割成 O ( log ⁡ n ) \bm{\mathcal O(\log n)} O(logn) 个小区间(这一点非常重要),如果每个区间都可以 Θ ( 1 ) \bm{\Theta(1)} Θ(1) 完成(当然也很重要,但是如果是均摊 Θ ( 1 ) \Theta(1) Θ(1) 也行),则总时间复杂度为 O ( log ⁡ n ) \mathcal O(\log n) O(logn)

较为失败的分段

我们的第一反应是把整个序列分割成几块,但是这样显然是不行的,哪里没割我们就可以把区间的一个端点设置为那个点,然后就无法分割了。如果把整个区间分割成每一个长度都是 1 1 1,则和数组无异,分割成的块数是 O ( n ) \bm{\mathcal O(n)} O(n) 的。

看起来不太有希望,但是我们可以换种思路。

线段树思想

一个位置可以被多个区间包含,而分割的方法是类似二进制

这样做的好处:一个数字 k \bm k k Θ ( log ⁡ k ) \bm{\Theta(\log k)} Θ(logk) 个二进制位,完美符合我们“一个序列被分割成 O ( log ⁡ k ) \mathcal O(\log k) O(logk) 个小序列”的要求。

这,就轮到线段树出场了。

这里插一句,为了发扬 STL 的传统美德,我们这里使用 [ a , b ) [a,b) [a,b) 左闭右开区间,同时下标从 0 0 0 开始。

我们假设序列长度是 2 k 2^k 2k

  • 一个线段是 [ 0 , 2 k ) [0,2^k) [0,2k)
  • 一个线段是 [ 0 , 2 k − 1 ) [0,2^{k-1}) [0,2k1)
  • 一个线段是 [ 2 k − 1 , 2 × 2 k − 1 ) [2^{k-1},2 \times 2^{k-1}) [2k1,2×2k1)
  • 一个线段是 [ 0 , 2 k − 2 ) [0,2^{k-2}) [0,2k2)
  • 一个线段是 [ 2 k − 2 , 2 × 2 k − 2 ) [2^{k-2},2 \times 2^{k-2}) [2k2,2×2k2)
  • 一个线段是 [ 2 k − 1 , 3 × 2 k − 2 ) [2^{k-1},3 \times 2^{k-2}) [2k1,3×2k2)
  • 一个线段是 [ 3 × 2 k − 2 , 4 × 2 k − 2 ) [3 \times 2^{k-2}, 4 \times 2^{k-2}) [3×2k2,4×2k2)

我们把它以树的形式组织起来:

  • [ 0 , 2 k ) [0,2^k) [0,2k)
    • [ 0 , 2 k − 1 ) [0,2^{k-1}) [0,2k1)
      • [ 0 , 2 k − 2 ) [0,2^{k-2}) [0,2k2)
      • [ 2 k − 2 , 2 × 2 k − 2 ) [2^{k-2},2 \times 2^{k-2}) [2k2,2×2k2)
    • [ 2 k − 1 , 2 × 2 k − 1 ) [2^{k-1},2 \times 2^{k-1}) [2k1,2×2k1)
      • [ 2 k − 1 , 3 × 2 k − 2 ) [2^{k-1},3 \times 2^{k-2}) [2k1,3×2k2)
      • [ 3 × 2 k − 2 , 4 × 2 k − 2 ) [3 \times 2^{k-2}, 4 \times 2^{k-2}) [3×2k2,4×2k2)

或者,使用 Graph Editor?为了不让文字太长,这里令 k = 3 k = 3 k=3

)

空间复杂度 Θ ( k 2 k ) \Theta(k2^k) Θ(k2k),若 n = 2 k n = 2^k n=2k 则为 Θ ( n log ⁡ n ) \Theta(n \log n) Θ(nlogn),可以完美承受。

这里,我们每一个节点不仅记录 [ l , r ) [l,r) [l,r) 范围,还要记录一个东西:范围里所有数字的和,以下称作 S S S

复杂度假了

假设整个区间为 [ 0 , 8 ) [0,8) [0,8),我们要把 [ 3 , 6 ) [3,6) [3,6) 这个区间的所有数字加一,我们可以:

  • 把区间 [ 0 , 8 ) [0,8) [0,8) S S S 3 3 3
  • 把区间 [ 0 , 4 ) [0,4) [0,4) S S S 1 1 1
  • 把区间 [ 2 , 4 ) [2,4) [2,4) S S S 1 1 1
  • 把区间 [ 3 , 4 ) [3,4) [3,4) S S S 1 1 1
  • 把区间 [ 4 , 8 ) [4,8) [4,8) S S S 2 2 2
  • 把区间 [ 4 , 6 ) [4,6) [4,6) S S S 2 2 2
  • 把区间 [ 4 , 5 ) [4,5) [4,5) S S S 1 1 1
  • 把区间 [ 5 , 6 ) [5,6) [5,6) S S S 1 1 1

看起来加了很多,复杂度真的是正确的吗?

很不幸,把区间内所有数字加上某个数字的时间复杂度为 Θ ( n log ⁡ n ) \bm{\Theta(n \log n)} Θ(nlogn),还不如直接在数组上操作呢。

那么,这个思路就不行了吗?当然不是,我们刚才已经看到过这个思路的一次绝处逢生,为什么这一次就不行?

我们注意到,把 [ 4 , 6 ) [4,6) [4,6) 2 2 2 可以不涉及到原来的区间就推出后面的把 [ 4 , 5 ) [4,5) [4,5) [ 5 , 6 ) [5,6) [5,6) 加一这两件事。

小 trick?

所以,我们有一个优化的小 trick(至于为什么要加双引号,待会儿就知道了):在每次区间加的时候,如果是把整个区间加某个值,则先记录下来要加,并不把下面的整个子树都加上某一个值。

事实上,专门用来记录这个值的变量叫做 lazytag \text{lazytag} lazytag,以下为了节省 KaTeX \KaTeX KATEX 的码量,则把它称作 tag \text{tag} tag KaTeX \KaTeX KATEX\text 内怎么加粗啊?/fn)

那么,加上这个 tag \text{tag} tag 之后,变成了什么样呢?

  • 把区间 [ 0 , 8 ) [0,8) [0,8) S S S 3 3 3
  • 把区间 [ 0 , 4 ) [0,4) [0,4) S S S 1 1 1
  • 把区间 [ 2 , 4 ) [2,4) [2,4) S S S 1 1 1
  • 把区间 [ 3 , 4 ) [3,4) [3,4) S S S 1 1 1
  • 把区间 [ 4 , 8 ) [4,8) [4,8) S S S 2 2 2
  • 把区间 [ 4 , 6 ) [4,6) [4,6) S S S 2 2 2
  • 把区间 [ 4 , 6 ) [4,6) [4,6) tag \text{tag} tag 1 1 1

看起来还是很多,但是我们惊喜的发现,如果在整个区间中都加上 v v v,我们只需要把 [ 0 , 8 ) [0,8) [0,8) tag \text{tag} tag 加上 v v v 即可。

接下来进入 hack 模式。

hack!

hack 1 1 1:左边少一个元素

也就是 [ 1 , n ) [1,n) [1,n)

分成两部分: [ 1 , n 2 ) [1,\frac{n}{2}) [1,2n) [ n 2 , n ) [\frac{n}{2},n) [2n,n),第二部分直接改一下 tag \text{tag} tag Θ ( 1 ) \Theta(1) Θ(1) 处理掉了。

第一部分分成两部分: [ 1 , n 4 ) [1,\frac{n}{4}) [1,4n) [ n 4 , n 2 ) [\frac{n}{4},\frac{n}{2}) [4n,2n),第二部分直接改一下 tag \text{tag} tag Θ ( 1 ) \Theta(1) Θ(1) 处理掉了。

第一部分分成两部分: [ 1 , n 8 ) [1,\frac{n}{8}) [1,8n) [ n 8 , n 4 ) [\frac{n}{8},\frac{n}{4}) [8n,4n),第二部分直接改一下 tag \text{tag} tag Θ ( 1 ) \Theta(1) Θ(1) 处理掉了。

第一部分分成两部分: [ 1 , n 16 ) [1,\frac{n}{16}) [1,16n) [ n 16 , n 8 ) [\frac{n}{16},\frac{n}{8}) [16n,8n),第二部分直接改一下 tag \text{tag} tag Θ ( 1 ) \Theta(1) Θ(1) 处理掉了。

以此类推,时间复杂度: T ( n ) = T ( n 2 ) + Θ ( 1 ) T(n)=T(\frac{n}{2})+\Theta(1) T(n)=T(2n)+Θ(1),显然 T ( n ) = Θ ( log ⁡ n ) T(n)=\Theta(\log n) T(n)=Θ(logn),也就是时间复杂度是 Θ ( log ⁡ n ) \bm{\Theta(\log n)} Θ(logn)

完美接招。

hack 2 2 2:右边少一个元素

完美接招,把左边的稍微改改,变成每次第一部分直接 Θ ( 1 ) \Theta(1) Θ(1) 处理掉了,时间复杂度 Θ ( log ⁡ n ) \bm{\Theta(\log n)} Θ(logn)

hack 3 3 3:左右边都少一个元素

本来以为可以成功 hack 的,但是分成两半之后我们发现:

左边:相当于 hack 1 1 1

右边:相当于 hack 2 2 2

然后 2 × Θ ( log ⁡ n ) = Θ ( log ⁡ n ) 2 \times \Theta(\log n) = \bm{\Theta(\log n)} 2×Θ(logn)=Θ(logn),又是一次完美地应对。

如何查询

显然,只能修改还不行,还得查询。

查询也就不难了,按照相似的方式,在树上递归求解,遇到和原本序列完全一样的直接加。

时间复杂度?我们不乱 hack 了,直接求时间复杂度吧。

时间复杂度

首先感性理解一下,层数是 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn) 的,所以时间复杂度是 O ( log ⁡ n ) \mathcal O(\log n) O(logn) 的。下面是严谨的证明,不想看可以跳过。

有几种情况:

  1. 序列和当前节点负责区间完全重合,直接返回,时间复杂度 Θ ( 1 ) \Theta(1) Θ(1),不会继续递归。
  2. 序列左端点就是目前节点负责的区间左端点,右端点不到区间中点。此时递归可能会变成情况 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4
  3. 序列左端点就是目前节点负责的区间左端点,右端点恰好为区间中点。此时递归可能会变成情况 1 1 1,也就是时间复杂度还是 Θ ( 1 ) \Theta(1) Θ(1)
  4. 序列左端点就是目前节点负责的区间左端点,右端点超过区间中点。此时左边递归可能会变成情况 1 1 1,右边递归可能变成情况 2 , 3 , 4 2,3,4 2,3,4
  5. 序列右端点就是目前节点负责的区间右端点,左端点不到区间中点。此时递归可能会变成情况 1 , 5 , 6 , 7 1,5,6,7 1,5,6,7
  6. 序列右端点就是目前节点负责的区间右端点,左端点恰好为区间中点。此时递归可能会变成情况 1 1 1,也就是时间复杂度还是 Θ ( 1 ) \Theta(1) Θ(1)
  7. 序列右端点就是目前节点负责的区间右端点,左端点超过区间中点。此时右边递归可能会变成情况 1 1 1,左边递归可能变成情况 5 , 6 , 7 5,6,7 5,6,7
  8. 区间左端点和右端点都不是目前节点负责的左右端点,但是范围符合,左边可能递归成 5 , 6 , 7 5,6,7 5,6,7,右边可能是 2 , 3 , 4 2,3,4 2,3,4
  9. 区间左端点和右端点都在左半部分,可能递归为 8 , 9 , 10 8,9,10 8,9,10
  10. 区间左端点和右端点都在右半部分,可能递归为 8 , 9 , 10 8,9,10 8,9,10

是不是看的有点晕?配合图片理解一下,黑色代表节点负责的区间,黑色中间的线代表中点,蓝色代表查询的点。

第一种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第三种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第四种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第五种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第六种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第七种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第八种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第九种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第十种:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看个文章怎么跟个打音游一样

我们发现:只有第八种需要担心——有两个递归,可能导致时间复杂度变为 Θ ( n ) \Theta(n) Θ(n)。当然,第 9 9 9 和第 10 10 10 也需要担心,但是如果第 8 8 8 种解决了,这两种也就迎刃而解了。

我们发现,一旦经过一次第 8 8 8 中,就必然不会再经过第 8 , 9 8,9 8,9 10 10 10 种了。所以,如果我们把第 1 ∼ 7 1 \sim 7 17 种和第 9 , 10 9,10 9,10 中当做两个组的话,那么 8 8 8 就是连接这两个组的桥梁。其中,第一个组花费的时间是 O ( log ⁡ n ) \mathcal O(\log n) O(logn) 的,第二个组也是,而“桥梁”最多被调用一次,所以最终时间复杂度是 O ( log ⁡ n ) + O ( log ⁡ n ) + 2 O ( log ⁡ n ) + O ( 1 ) = O ( log ⁡ n ) \mathcal O(\log n) + \mathcal O(\log n) + 2\mathcal O(\log n) + \mathcal O(1) = \bm{\mathcal O(\log n)} O(logn)+O(logn)+2O(logn)+O(1)=O(logn)

查询时间复杂度也是 O ( log ⁡ n ) \bm{\mathcal O(\log n)} O(logn) 的。

修改时间复杂度

我们发现和查询几乎没有区别,原本时间复杂度假掉的原因只是第一种情况会变成和第八种一样的 T ( n ) = 2 T ( n 2 ) + Θ ( 1 ) T(n) = 2T(\frac{n}{2}) + \Theta(1) T(n)=2T(2n)+Θ(1),并且会变成两个第一种,时间复杂度直接废掉。加入 tag 之后就好啦!

时间复杂度证明和上述相同,是 O ( log ⁡ n ) \mathcal O(\log n) O(logn) 的。

这真的只是一个小 trick 吗?当然不是,这就是线段树的精髓,可以说,上面讲的思想就是线段树的灵魂,而这个 lazytag 就是线段树的骨架,缺一不可。

tag 的细节

在查询和修改时,如果一个标记已经有了 tag,那么这个 tag 可能会被使用,则需要把子树的标记加上本身的标记,然后修改 S S S,然后把标记清空,这一过程称作把标记下放(pushdown)。

代码

动态分配内存,然而静态开点。

// 需要 C++20 的 <bits> 头文件中的 bit_ceil 函数,作用是找到最小的 >= x 的 2 的幂,如果没有 C++20 也可以手写。

// 线段/区间
template<typename T>
class segment
{
public:
	T l, r; // [l, r)
	size_t size() { return r - l; } // 长度
	bool fail() { return l >= r; } // 是否不合规
	template<typename Han_Si_Ying>
	friend bool operator==(const segment<Han_Si_Ying>& x, const segment<Han_Si_Ying>& y) { return x.l == y.l && x.r == y.r; } // Han_Si_Ying:?
};

// 区间交
template<typename T>
segment<T> seg_and(segment<T> l1, segment<T> l2)
{
	return { max(l1.l, l2.l), min(l1.r, l2.r) };
}

template<typename _Valt>
class segtree
{
	class segnode
	{
	public:
		segment<size_t> seg; // 负责区间
		_Valt s, tag; // S, lazytag
		segnode* lp = nullptr, * rp = nullptr; // 左右子树
		void pushdown() // 下放标记
		{
			s += tag * seg.size();
			if (lp) lp->tag += tag;
			if (rp) rp->tag += tag;
			tag = 0;
		}
	};
	segnode* rt;
	void init(segnode* root, size_t l, size_t r) // 初始化
	{
		root->seg = { l,r };
		root->s = root->tag = _Valt();
		if (l + 1 == r) return;
		else
		{
			init(root->lp = new segnode, l, (l + r) >> 1);
			init(root->rp = new segnode, (l + r) >> 1, r);
		}
	}
	_Valt inn_query(segnode* root, segment<size_t> seg) // 区间查询
	{
		root->pushdown(); // 下放懒标记
		if (root->seg == seg) return root->s;
		segment<size_t> segl = seg_and(root->lp->seg, seg), 
			segr = seg_and(root->rp->seg, seg); // 区间交
		_Valt sum = 0; // 和
		if (!segl.fail()) sum += inn_query(root->lp, segl);
		if (!segr.fail()) sum += inn_query(root->rp, segr);
		return sum;
	}
	void inn_add(segnode* root, segment<size_t> seg, _Valt val) // 区间修改
	{
		root->pushdown();
		if (root->seg == seg)
		{
			root->tag += val;
			return;
		}
		root->s += val * seg.size();
		segment<size_t> segl = seg_and(root->lp->seg, seg),
			segr = seg_and(root->rp->seg, seg);
		if (!segr.fail()) inn_add(root->rp, segr, val);
		if (!segl.fail()) inn_add(root->lp, segl, val);
	}
public:
	segtree(size_t sz)
	{
		sz = bit_ceil(sz); // 需要 2 的幂
		rt = new segnode{ 0, sz };
		init(rt, 0, sz);
	}
	_Valt query(size_t l, size_t r)
	{
		return inn_query(rt, { l, r + 1 });
	}
	void add(size_t l, size_t r, _Valt x)
	{
		inn_add(rt, { l, r + 1 }, x);
	}
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux运行级别

运行级别&#xff1a;指linux系统在启动和运行过程中所处的不同的状态。 运行级别之间的切换&#xff1a;init (级别数) 示例&#xff1a; linux的运行级别一共有7种&#xff0c;分别是&#xff1a; 运行级别0&#xff1a;停机状态 运行级别1&#xff1a;单用户模式/救援模式…

【自开发工具介绍】SQLSERVER的ImpDp和ExpDp工具03

SQLSERVER的ImpDp和ExpDp工具 1、全部的表导出&#xff08;仅表结构导出&#xff09; 2、导出的表结构&#xff0c;导入到新的数据库 导入前&#xff0c;test3数据没有任何表 导入 导入结果确认&#xff1a;表都被做成&#xff0c;但是没有数据 3、全部的表导出&#x…

商品列表及商品详情展示

前言 本文将展示一段结合 HTML、CSS 和 JavaScript 的代码&#xff0c;实现了一个简单的商品展示页面及商品详情&#xff0c;涵盖数据获取、渲染、搜索及排序等功能。 效果展示 点击不同的商品会展示对应的商品详情。 代码部分 代码总体实现 <!DOCTYPE html> <htm…

c++提取矩形区域图像的梯度并拟合直线

c提取旋转矩形区域的边缘最强梯度点&#xff0c;并拟合直线 #include <opencv2/opencv.hpp> #include <iostream> #include <vector>using namespace cv; using namespace std;int main() {// 加载图像Mat img imread("image.jpg", IMREAD_GRAYS…

独立开发浏览器插件:案例与启示

浏览器插件&#xff08;Browser Extension&#xff09;作为提升用户浏览体验的重要工具&#xff0c;近年来吸引了许多独立开发者的关注。从广告拦截到生产力工具&#xff0c;再到个性化定制功能&#xff0c;浏览器插件的开发为个人开发者提供了一个低成本、高潜力的创业机会。本…

Linux系统 环境变量

环境变量 写在前面概念查看环境变量main函数的参数argc & argvenv bash环境变量 写在前面 对于环境变量&#xff0c;本篇主要介绍基本概念及三四个环境变量 —— PATH、HOME、PWD。其中 PATH 作为 “ 敲门砖 ”&#xff0c;我们会更详细讲解&#xff1b;理解环境变量的全局…

BFS(广度优先搜索)——搜索算法

BFS&#xff0c;也就是广度&#xff08;宽度&#xff09;优先搜索&#xff0c;二叉树的层序遍历就是一个BFS的过程。而前、中、后序遍历则是DFS&#xff08;深度优先搜索&#xff09;。从字面意思也很好理解&#xff0c;DFS就是一条路走到黑&#xff0c;BFS则是一层一层地展开。…

SpringCloud基础二(完结)

HTTP客户端Feign 在SpringCloud基础一中&#xff0c;我们利用RestTemplate结合服务注册与发现来发起远程调用的代码如下&#xff1a; String url "http://userservice/user/" order.getUserId(); User user restTemplate.getForObject(url, User.class);以上代码就…

Spring Bean 容器

技术成长&#xff0c;是对场景设计细节不断的雕刻&#xff01; 你觉得自己的技术什么时候得到了快速的提高&#xff0c;是CRUD写的多了以后吗&#xff1f;想都不要想&#xff0c;绝对不可能&#xff01;CRUD写的再多也只是能满足你作为一个搬砖工具人&#xff0c;敲击少逻辑流…

【react+redux】 react使用redux相关内容

首先说一下&#xff0c;文章中所提及的内容都是我自己的个人理解&#xff0c;是我理逻辑的时候&#xff0c;自我说服的方式&#xff0c;如果有问题有补充欢迎在评论区指出。 一、场景描述 为什么在react里面要使用redux&#xff0c;我的理解是因为想要使组件之间的通信更便捷…

利用腾讯云cloud studio云端免费部署deepseek-R1

1. cloud studio 1.1 cloud studio介绍 Cloud Studio&#xff08;云端 IDE&#xff09;是基于浏览器的集成式开发环境&#xff0c;为开发者提供了一个稳定的云端工作站。支持CPU与GPU的访问。用户在使用 Cloud Studio 时无需安装&#xff0c;随时随地打开浏览器即可使用。Clo…

基于VMware的ubuntu与vscode建立ssh连接

1.首先安装openssh服务 sudo apt update sudo apt install openssh-server -y 2.启动并检查ssh服务状态 到这里可以按q退出 之后输入命令 &#xff1a; ip a 红色挡住的部分就是我们要的地址&#xff0c;这里就不展示了哈 3.配置vscode 打开vscode 搜索并安装&#xff1a;…

四川正熠法律咨询有限公司正规吗可信吗?

在纷繁复杂的法律环境中&#xff0c;寻找一家值得信赖的法律服务机构是每一个企业和个人不可或缺的需求。四川正熠法律咨询有限公司&#xff0c;作为西南地区备受瞩目的法律服务提供者&#xff0c;以其专注、专业和高效的法律服务&#xff0c;成为众多客户心中的首选。 正熠法…

【优先算法】专题——位运算

在讲解位运算之前我们来总结一下常见的位运算 一、常见的位运算 1.基础为运算 << &&#xff1a;有0就是0 >> |&#xff1a;有1就是1 ~ ^&#xff1a;相同为0&#xff0c;相异位1 /无进位相加 2.给一个数 n&#xff0c;确定它的二进制表示…

Android --- handler详解

handler 理解 handler 是一套Android 消息传递机制&#xff0c;主要用于线程间通信。 tips&#xff1a; binder/socket 用于进程间通信。 参考&#xff1a; Android 进程间通信-CSDN博客 handler 就是主线程在起了一个子线程&#xff0c;子线程运行并生成message &#xff0c;l…

【线程】基于阻塞队列的生产者消费者模型

文章目录 1 生产者消费者模型2 阻塞队列2.1 成员变量2.2 消费者操作2.3 生产者生产 3 总结 1 生产者消费者模型 在多线程环境中&#xff0c;生产者消费者模型是一种经典的线程同步模型&#xff0c;用于处理生产者线程与消费者线程之间的工作调度和资源共享问题。在这个模型中&a…

解决PyG安装中torch-sparse安装失败问题:详细指南

1 问题描述 最近在学习GNN&#xff0c;需要使用PyTorch Geometric&#xff08;PyG&#xff09;库。在安装PyG的过程中&#xff0c;遇到了torch-sparse安装失败的问题&#xff0c;错误提示为&#xff1a; ERROR: Failed building wheel for torch-sparse本文将详细记录问题的解…

4 [危机13小时追踪一场GitHub投毒事件]

事件概要 自北京时间 2024.12.4 晚间6点起&#xff0c; GitHub 上不断出现“幽灵仓库”&#xff0c;仓库中没有任何代码&#xff0c;只有诱导性的病毒文件。当天&#xff0c;他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒&#xff0c;等待不…

【B站保姆级视频教程:Jetson配置YOLOv11环境(六)PyTorchTorchvision安装】

Jetson配置YOLOv11环境&#xff08;6&#xff09;PyTorch&Torchvision安装 文章目录 1. 安装PyTorch1.1安装依赖项1.2 下载torch wheel 安装包1.3 安装 2. 安装torchvisiion2.1 安装依赖2.2 编译安装torchvision2.2.1 Torchvisiion版本选择2.2.2 下载torchvisiion到Downloa…

自动化软件测试的基本流程

一、自动化测试的准备 1.1 了解测试系统 首先对于需要测试的系统我们需要按照软件需求说明书明确软件功能。这里以智慧养老系统作为案例进行测试&#xff0c;先让我们看看该系统的登录界面和用户管理界面。 登录界面&#xff1a; 登录成功默认界面&#xff1a; 用户管理界面…