【数据结构】Splay详解

news2024/9/23 19:22:08

Splay

      • 引入
  • Splay
      • 旋转操作
      • splay操作
      • 插入操作
      • 查询x排名
      • 查询排名为x
      • 删除操作
      • 查询前驱/后继
      • 模板
      • Splay时间复杂度分析
    • 进阶操作
      • 截取区间
      • 区间加,区间赋值,区间查询,区间最值
      • 区间翻转
      • 原序列整体插入
      • 指定位置插入
      • 整体插入末尾
      • 区间最大子段和
    • 一些好题
    • 参考文献

引入

首先我们要知道一个东西叫二叉搜索树。
其定义如下:

  1. 空树是二叉搜索树。
  2. 若二叉搜索树的左子树不为空,则其左子树上所有点的附加权值均小于其根节点的值。
  3. 若二叉搜索树的右子树不为空,则其右子树上所有点的附加权值均大于其根节点的值。
  4. 二叉搜索树的左右子树均为二叉搜索树。

二叉搜索树上的基本操作所花费的时间与这棵树的高度成正比。对于一个有 n n n 个结点的二叉搜索树中,这些操作的最优时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)

如图就是一颗典型的 BST(二叉查找树)
在这里插入图片描述
可是我们发现,如果树退化成一条链,那么时间复杂度将退化为 O ( n ) O(n) O(n),这是我们不能接受的,于是平衡树孕育而生,其核心就是维护一颗相对平衡的 BST。
本文将介绍Splay,虽然它并不能保证树一直是"平衡"的,但对于Splay的一系列操作,我们可以证明其每步操作的平摊复杂度都是 O ( log ⁡ n ) O(\log n) O(logn)。所以从某种意义上说,Splay也是一种平衡的二叉查找树。

Splay

旋转操作

下面参考 OI-WIKI的介绍。
在这里插入图片描述
注意,左右旋指的是向左或右旋转。
左旋为ZAG,右旋为ZIG
以下是一次标准旋转操作:
在这里插入图片描述
我们可以知道,旋转流程如下:
在这里插入图片描述

于是我们便可以写出 ZIG和ZAG函数,参考下列代码:
在这里插入图片描述
在这里插入图片描述
不过有时候为了方便表示,我们可以把两个旋转操作合并起来。
就成了 rotate(旋转)函数,以下是参考代码:

void rotate(int x){
		int y=fa[x],z=fa[y],id=son_(x);
		ch[y][id]=ch[x][id^1];
		if(ch[x][id^1])
			fa[ch[x][id^1]]=y;
		ch[x][id^1]=y;
		fa[y]=x;
		fa[x]=z;
		if(z)
			ch[z][y==ch[z][1]]=x;
		pushup(y);
		pushup(x);
	}

其中 son_( x x x)是判断 x x x 为父节点的左儿子还是右儿子,pushup为由下往上更新。

splay操作

这个操作可以说是Splay的核心操作之一,可以理解为把某个点通过旋转操作旋转到根节点。
那么如何将一个节点旋转到根节点呢?
首先有 6 6 6 种基本情况,见下图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
那么我们只需要不断重复执行旋转操作,即可旋转到根节点。
以下是参考代码:

void splay(int x) {
  for (int f = fa[x]; f = fa[x], f; rotate(x))
    if (fa[f]) 
    	rotate(get(x) == get(f) ? f : x);
  rt = x;
}

一些进阶
由于后面某些操作需要用到,所以我们对splay函数进行一些修改。
具体而言,我们引入一个参数 y y y,让splay把 x x x 旋转到 y y y 的儿子上。(当 y = 0 y=0 y=0 时将 x x x 旋转到根节点)
其实也没什么改动,见参考代码:

void splay(int x,int y){
	while(fa[x]!=y){
		if(fa[fa[x]]!=y){
			if(son_(fa[x])==son_(x))
				rotate(fa[x]);
			else
				rotate(x);
		}
		rotate(x);
	}
	if(!y)
		rt=x;
}

插入操作

在这里插入图片描述

解释一下:
二叉树的性质使得插入操作变得非常简易,具体而言,只要值比当前节点大,就往右子树找,小就往左子树找,一样就让计数器+1,如果找不到匹配的值就直接新建一个节点。
参考代码:

	void add(int k){
		if(!rt){
			rt=++idx;
			cnt[rt]++,val[rt]=k;
			pushup(rt);
			return ;
		}
		int x=rt,y=0;
		while(1){
			if(val[x]==k){
				cnt[x]++;
				pushup(x),pushup(y);
				splay(x,0);
				break;
			}
			y=x;
			x=ch[x][val[x]<k];
			if(!x){
				cnt[++idx]++,val[idx]=k;
				fa[idx]=y;
				ch[y][val[y]<k]=idx;
				pushup(idx);
				pushup(y);
				splay(idx,0);
				break;
			}
		}
	}

查询x排名

这个跟插入差不多,从根节点不断往下找,每次向右子树找时加上左子树的size+1,因为左子树和根的值一定比查询值小(BST的性质)。
具体详见代码:

	int x_rank(int k){
		int rk=0,x=rt;
		while(1){
			if(k<val[x])
				x=ch[x][0];
			else{
				rk+=sz[ch[x][0]];
				if(!x)
					return rk+1;
				if(k==val[x]){
					splay(x,0);
					return rk+1;
				}
				rk+=cnt[x];
				x=ch[x][1];
			}
		}
	}

查询排名为x

这个跟上面两个操作都差不多,不断往下找就行了。
看着代码,画画图也就能理解了。

	int kth(int k){
		int x=rt;
		while(1){
			if(ch[x][0]&&k<=sz[ch[x][0]])
				x=ch[x][0];
			else{
				k-=sz[ch[x][0]];
				if(k<=cnt[x]){
					splay(x,0);
					return val[x];
				}
				k-=cnt[x];
				x=ch[x][1];
			}
		}
	}

删除操作

在这里插入图片描述
这个就感性理解一下。
参考代码:

	void del(int k){
		x_rank(k);
		int x=rt,y=0;
		if(cnt[rt]>1)
			cnt[rt]--,pushup(rt);
		else if(!ch[rt][0]&&!ch[rt][1])
			clean(rt),rt=0;
		else if(!ch[rt][0]){
			rt=ch[rt][1];
			fa[rt]=0;
			clean(x);
		}
		else if(!ch[rt][1]){
			rt=ch[rt][0];
			fa[rt]=0;
			clean(x);
		}
		else{
			pre();
			fa[ch[x][1]]=rt;
			ch[rt][1]=ch[x][1];
			clean(x),pushup(rt);
		}
	}

或者还有一种方式,我们把 x x x 的前驱旋转到根节点,再把 x x x 的后继旋转到根节点的右子树上,这样根节点的右子树的左儿子即为目标节点,直接断开联系即为删除。
参考代码:

void del(int x){
	int l=kth(x-1),r=kth(r+1);
	splay(l,0),splay(r,l);
	fa[ch[r][0]]=0,ch[r][0]=0;
	pushup(r);
	pushup(l);
}

查询前驱/后继

这个可以先将这个节点插入,此时它在根节点,那么前驱就是它左子树中最右的点,后继就是它右子树中最左的点。
查询完我们在删除这个点即可。
参考代码:

	int pre(){
		int z=ch[rt][0];
		while(ch[z][1])
			z=ch[z][1];
		splay(z,0);
		return z;
	}
	int nxt(){
		int z=ch[rt][1];
		while(ch[z][0])
			z=ch[z][0];
		splay(z,0);
		return z;
	}

模板

综合上述操作,我们即可A掉洛谷模版题。
P3369 【模板】普通平衡树

题目概述:
在这里插入图片描述
参考代码:

struct Tr_splay{
	int fa[N],ch[N][2],sz[N],val[N],cnt[N];
	void pushup(int x){
		sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
	}
	void clean(int x){
		fa[x]=sz[x]=cnt[x]=val[x]=ch[x][0]=ch[x][1]=0;
	}
	bool son_(int x){
		return x==ch[fa[x]][1];
	}
	void rotate(int x){
		int y=fa[x],z=fa[y],id=son_(x);
		ch[y][id]=ch[x][id^1];
		if(ch[x][id^1])
			fa[ch[x][id^1]]=y;
		ch[x][id^1]=y;
		fa[y]=x;
		fa[x]=z;
		if(z)
			ch[z][y==ch[z][1]]=x;
		pushup(y);
		pushup(x);
	}
	void splay(int x,int y){
		while(fa[x]!=y){
			if(fa[fa[x]]!=y){
				if(son_(fa[x])==son_(x))
					rotate(fa[x]);
				else
					rotate(x);
			}
			rotate(x);
		}
		if(!y)
			rt=x;
	}
	int pre(){
		int z=ch[rt][0];
		while(ch[z][1])
			z=ch[z][1];
		splay(z,0);
		return z;
	}
	int nxt(){
		int z=ch[rt][1];
		while(ch[z][0])
			z=ch[z][0];
		splay(z,0);
		return z;
	}
	void add(int k){
		if(!rt){
			rt=++idx;
			cnt[rt]++,val[rt]=k;
			pushup(rt);
			return ;
		}
		int x=rt,y=0;
		while(1){
			if(val[x]==k){
				cnt[x]++;
				pushup(x),pushup(y);
				splay(x,0);
				break;
			}
			y=x;
			x=ch[x][val[x]<k];
			if(!x){
				cnt[++idx]++,val[idx]=k;
				fa[idx]=y;
				ch[y][val[y]<k]=idx;
				pushup(idx);
				pushup(y);
				splay(idx,0);
				break;
			}
		}
	}
	int x_rank(int k){
		int rk=0,x=rt;
		while(1){
			if(k<val[x])
				x=ch[x][0];
			else{
				rk+=sz[ch[x][0]];
				if(!x)
					return rk+1;
				if(k==val[x]){
					splay(x,0);
					return rk+1;
				}
				rk+=cnt[x];
				x=ch[x][1];
			}
		}
	}
	int kth(int k){
		int x=rt;
		while(1){
			if(ch[x][0]&&k<=sz[ch[x][0]])
				x=ch[x][0];
			else{
				k-=sz[ch[x][0]];
				if(k<=cnt[x]){
					splay(x,0);
					return val[x];
				}
				k-=cnt[x];
				x=ch[x][1];
			}
		}
	}
	void del(int k){
		x_rank(k);
		int x=rt,y=0;
		if(cnt[rt]>1)
			cnt[rt]--,pushup(rt);
		else if(!ch[rt][0]&&!ch[rt][1])
			clean(rt),rt=0;
		else if(!ch[rt][0]){
			rt=ch[rt][1];
			fa[rt]=0;
			clean(x);
		}
		else if(!ch[rt][1]){
			rt=ch[rt][0];
			fa[rt]=0;
			clean(x);
		}
		else{
			pre();
			fa[ch[x][1]]=rt;
			ch[rt][1]=ch[x][1];
			clean(x),pushup(rt);
		}
	}
}tree;
signed main(){
	IOS;
	cin>>m;
	while(m--){
		int x,y;
		cin>>x>>y;
		if(x==1)tree.add(y);
		if(x==2)tree.del(y);
		if(x==3)tree.add(y),cout<<tree.x_rank(y)<<"\n",tree.del(y);
		if(x==4)cout<<tree.kth(y)<<"\n";
		if(x==5)tree.add(y),cout<<tree.val[tree.pre()]<<"\n",tree.del(y);
		if(x==6)tree.add(y),cout<<tree.val[tree.nxt()]<<"\n",tree.del(y);
	}
	return 0;
}

Splay时间复杂度分析

这个蒟蒻不会,但可以参考 OI-WIKI的证明:
证明

进阶操作

截取区间

Splay还可应用到序列操作中,具体而言,如果我们需要对区间 [ l , r ] [l,r] [l,r]进行操作,我们只需要先将 l − 1 l-1 l1 弄到根节点,再把 r + 1 r+1 r+1 弄到根节点的右儿子上,那么它的左子树就是区间 [ l , r ] [l,r] [l,r]了。
参考代码:

	int split(int l,int r){
		l=kth(l-1),r=kth(r+1);
		splay(l,0);
		splay(r,l);
		return ch[r][0];
	}
	//返回区间[l,r]对应的子树的根节点

区间加,区间赋值,区间查询,区间最值

这个类似线段树,我们相应的维护标记,并写好pushdown即可。
区间加参考:

void pushadd(int x,int k){
	val[x]+=k;
	sum[x]+=k*sz[x];
	add[x]+=k;
}
void modify1(int l,int r,int k){
	int _=split(l,r);
	pushadd(_,0,k);
	pushup(r);
	pushup(l);
}

区间赋值参考:

void pushcov(int x,int k){
	val[x]=k;
	sum[x]=sz[x]*k;
	add[x]=0;
	cov[x]=1;
}
void modify(int l,int r,int k){
	int _=split(l,r);
	pushcov(_,k);
	pushup(r);
	pushup(l);
}

区间查询参考:

void ask_sum(int l,int r){
	int _=split(l,r);
	cout<<sum[_]<<"\n";
}

区间翻转

这个呢我们还是搞一个懒标记然后下传,注意各个标记之间的先后顺序。
参考代码:

	void change(int x){
		swap(ch[x][0],ch[x][1]);
		lazy[x]^=1;
	}
	void reverse(int l,int r){
		l=kth(l),r=kth(r+2);
		splay(l,0);
		splay(r,l);
		change(ch[ch[l][1]][0]);
	}

原序列整体插入

有时候题目会直接给我们一个初始序列,一个个插入过于麻烦,于是我们可以类似线段树直接建树。
参考代码:

	int create(int k){
		int x=top?rb[top--]:++ID;
		ch[x][0]=ch[x][1]=fa[x]=rev[x]=cov[x]=0;
		sz[x]=1;
		val[x]=mx[x]=sum[x]=k;
		lx[x]=rx[x]=max(0ll,k);
		return x;
	}
	一些毒瘤题卡空间,这样回收可以节省空间。
	int build(int l,int r,int *a){
		if(l>r)
			return 0;
		if(l==r)
			return create(a[l]);
		int mid=(l+r)>>1,x=create(a[mid]);
		ch[x][0]=build(l,mid-1,a);
		ch[x][1]=build(mid+1,r,a);
		fa[ch[x][0]]=fa[ch[x][1]]=x;
		pushup(x);
		return x;
	}
rt=build(1,n,a);

指定位置插入

这个可以参考查询排名为x的操作。
能看到这里说明你已经是大佬了,看着代码画画图即可理解吧。

	void add(int pos,int k){
		kth(pos);
		pushdown(rt);
		fa[ch[rt][0]]=++ID,ch[ID][0]=ch[rt][0];
		ch[rt][0]=ID,fa[ID]=rt;
		sz[ID]=1;
		val[ID]=sum[ID]=k;
		pushup(ID);
		pushup(rt);
	}

整体插入末尾

这个也比较抽象,类似于建一棵新的splay,然后合并。

	void insert(int pos,int len,int *a){
		int _=build(1,len,a);
		int y=kth(pos),x=kth(pos+1);
		splay(y,0);
		splay(x,y);
		ch[x][0]=_,fa[_]=x;
		pushup(x);
		pushup(y);
	}

区间最大子段和

参考线段树,我们维护3个标记:
lx:从左起的最大子段和
mx:整个区间的最大子段和
rx:从右起的最大子段和
参考代码:(由于同时维护区间赋值和区间翻转,代码比较抽象)

	void pushup(int x){
		sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
		sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
		lx[x]=max(lx[ch[x][0]],sum[ch[x][0]]+val[x]+lx[ch[x][1]]);
		rx[x]=max(rx[ch[x][1]],sum[ch[x][1]]+val[x]+rx[ch[x][0]]);
		mx[x]=max(max(mx[ch[x][0]],mx[ch[x][1]]),rx[ch[x][0]]+val[x]+lx[ch[x][1]]);
	}
	void pushdown(int x){
		if(cov[x]){
			if(ch[x][0])val[ch[x][0]]=val[x],cov[ch[x][0]]=1,sum[ch[x][0]]=val[x]*sz[ch[x][0]];
			if(ch[x][1])val[ch[x][1]]=val[x],cov[ch[x][1]]=1,sum[ch[x][1]]=val[x]*sz[ch[x][1]];
			if(val[x]>0){
				if(ch[x][0])lx[ch[x][0]]=rx[ch[x][0]]=mx[ch[x][0]]=sum[ch[x][0]];
				if(ch[x][1])lx[ch[x][1]]=rx[ch[x][1]]=mx[ch[x][1]]=sum[ch[x][1]];
			}
			else{
				if(ch[x][0])lx[ch[x][0]]=rx[ch[x][0]]=0,mx[ch[x][0]]=val[x];
				if(ch[x][1])lx[ch[x][1]]=rx[ch[x][1]]=0,mx[ch[x][1]]=val[x];
			}
			cov[x]=0;
		}
		if(rev[x]){
			if(ch[x][0])
				rev[ch[x][0]]^=1,swap(ch[ch[x][0]][0],ch[ch[x][0]][1]),swap(lx[ch[x][0]],rx[ch[x][0]]);
			if(ch[x][1])
				rev[ch[x][1]]^=1,swap(ch[ch[x][1]][0],ch[ch[x][1]][1]),swap(lx[ch[x][1]],rx[ch[x][1]]);
			rev[x]=0;
		}
	}
	void ask_max_sum(){
		cout<<mx[rt]<<"\n";
	}

一些好题

P2042
P4008
P6707

参考文献

  1. OI-WIKI
  2. 伸展树的基本操作和应用——杨思雨
  3. 各位大佬的博客和题解

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

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

相关文章

自动驾驶系列—智能巡航辅助功能中的横向避让功能介绍

文章目录 1. 背景介绍2. 功能定义3. 功能原理4. 传感器架构5. 实际应用案例5.1 典型场景1&#xff1a;前方车辆压线5.2 典型场景2&#xff1a;相邻车道有大型车辆5.3 典型场景3&#xff1a;它车近距离cut in 6. 总结与展望 1. 背景介绍 随着汽车技术的发展&#xff0c;智能巡航…

一些常见的网络故障

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

关于我在vue3中使用swiper的使用碰到swiper-slide的width特别大的这件事儿

一. 环境 "vue": "^3.3.8","swiper": "^10.0.4",二. 问题描述 原代码: <template><swiperclass"wq-swiper":space-betwee"spaceBetween":pagination"{ clickable: true }":modules"mod…

Unity发微信小游戏记录

Unity2Wechat 流程1.小程序AppID2.Unity 插件3.微信开发者工具4.CDN资源服 参考文档 流程 1.小程序AppID 已有账号 登陆公众平台获取小程序AppID https://mp.weixin.qq.com/ 无账号 注册小程序 https://developers.weixin.qq.com/minigame/dev/guide/ 经营类目需要是游戏大类…

msvcr120.dll丢失的原因分析,msvcr120.dll丢失的解决方法分享

在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示或程序无法正常运行的问题。其中&#xff0c;msvcr120.dll丢失是一种常见的错误&#xff0c;它会导致某些应用程序无法启动或运行。本文将分析msvcr120.dll丢失的原因&#xff0c;并介绍5种解决方法以及修复过程中…

postman双击打不开的解决方案

postman双击打不开的解决方案 深入再深入 于 2022-05-09 15:45:56 发布 阅读量3.1k 收藏 2 点赞数 4 文章标签&#xff1a; postman 版权 右键属性 安装路径 更新版本 回滚 问题排查 关键词由CSDN通过智能技术生成 解决方案&#xff1a; 右键-属性&#xff0c;复制安装路…

App Inventor 2 天气预报App开发 - 第三方API接入的通用方法(2)

本文来自AppInventor2中文网&#xff08;www.fun123.cn&#xff09;参考文档&#xff0c;调用第三方天气接口获取天气JSON数据&#xff0c;解析并展示在App上。 App效果图&#xff0c;展示未来7日的天气预报&#xff0c;包括日期、天气图示和温度&#xff1a; App原理介绍 通…

RT-DETR+Flask实现目标检测推理案例

今天&#xff0c;带大家利用RT-DETR&#xff08;我们可以换成任意一个模型&#xff09;Flask来实现一个目标检测平台小案例&#xff0c;其实现效果如下&#xff1a; 目标检测案例 这个案例很简单&#xff0c;就是让我们上传一张图像&#xff0c;随后选择一下置信度&#xff0c;…

【博士每天一篇文献-算法】连续学习算法之HNet:Continual learning with hypernetworks

阅读时间&#xff1a;2023-12-26 1 介绍 年份&#xff1a;2019 作者&#xff1a;Johannes von Oswald&#xff0c;Google Research&#xff1b;Christian Henning&#xff0c;EthonAI AG&#xff1b;Benjamin F. Grewe&#xff0c;苏黎世联邦理工学院神经信息学研究所 期刊&a…

解决虚拟机与主机ping不通,解决主机没有vmware网络

由于注册表文件缺失导致&#xff0c;使用这个工具 下载cclean 白嫖就行 https://www.ccleaner.com/ 是 点击修复就可以了

《TF2.x》强化学习手册-P47-P59-TD时序差分-Monte_carlo蒙特卡洛预测与控制算法

文章目录 实现时序差分学习前期准备实现步骤工作原理 构建强化学习中的蒙特卡洛预测和控制算法前期准备实现步骤工作原理 实现时序差分学习 时序差分&#xff08;Temporal Difference &#xff0c;TD&#xff09;算法。TD算法是一种预测值或目标值校正的方法&#xff0c;用于强…

JRT实体视图查询

JRT的设计目标就是多数据库支持&#xff0c;对于爬行周边数据提供DolerGet解决爬取多维数据问题。但是对于通过父表字段筛选子表数据就不能通过DolerGet取数据了&#xff0c;因为查询到的父表数据没有子表数据的ID。 比如下面表&#xff1a; 我需要按登记号查询这个登记号的报…

tree组件实现折叠与展开功能(方式2 - visible计算属性)

本示例节选自vue3最新开源组件实战教程大纲&#xff08;持续更新中&#xff09;的tree组件开发部分。考察Vue3 Composition API形式的计算属性的用法&#xff0c;computed可以单独用在ts文件中&#xff0c;实现ts的计算属性类型的定义。 父节点属性 在IFlatTreeNode中定义父节…

Blackbox AI:你的智能编程伙伴

目录 Blackbox AI 产品介绍 Blackbox AI 产品使用教程 Blackbox AI体验 AI问答 代码验证 实时搜索 探索&代理 拓展集成 总结 Blackbox AI 产品介绍 Blackbox是专门为程序员量身定制的语言大模型&#xff0c;它针对20多种编程语言进行了特别训练和深度优化&#xff0c;在AI代…

MySQL JDBC

JDBC&#xff1a;Java的数据库编程 JDBC&#xff0c;即Java Database Connectivity&#xff0c;java数据库连接。是一种用于执行SQL语句的Java API&#xff0c;它是 Java中的数据库连接规范。这个API由 java.sql.*,javax.sql.* 包中的一些类和接口组成&#xff0c;它为Java 开…

MySQL:基础操作(增删查改)

目录 一、库的操作 创建数据库 查看数据库 显示创建语句 修改数据库 删除数据库 备份和恢复 二、表的操作 创建表 查看表结构 修改表 删除表 三、表的增删查改 新增数据 插入否则更新 插入查询的结果 查找数据 为查询结果指定别名 结果去重 where 条件 结…

tree组件实现折叠与展开功能(方式1 - expandedTree计算属性)

本示例节选自vue3最新开源组件实战教程大纲&#xff08;持续更新中&#xff09;的tree组件开发部分。考察响应式对象列表封装和computed计算属性的使用&#xff0c;以及数组reduce方法实现结构化树拍平处理的核心逻辑。 实现思路 第一种方式&#xff1a;每次折叠或展开后触发…

经纬恒润全新第二代行泊一体域控制器成功量产

随着L2自动驾驶功能的普及&#xff0c;整车架构的升级&#xff0c;传统分布式控制器已不能适应市场的发展&#xff0c;如何以低成本高性能实现高阶自动驾驶功能的落地, 成为了众多整车厂的迫切需求&#xff0c;行泊一体域控制器应运而生。据高工数据显示&#xff0c;2023年仅1-…

NVIDIA GPU 监控观测最佳实践

1、DCGM 介绍 DCGM&#xff08;Data Center GPU Manager&#xff09;即数据中心 GPU 管理器&#xff0c;是一套用于在集群环境中管理和监视 Tesla™GPU 的工具。它包括主动健康监控&#xff0c;全面诊断&#xff0c;系统警报以及包括电源和时钟管理在内的治理策略。它可以由系…

TypeScript 基础类型(一)

简介 它是 JavaScript 的超集&#xff0c;具有静态类型检查和面向对象编程的特性。TypeScript 的出现&#xff0c;为开发者提供了一种更加严谨和高效的开发方式。 主要特点&#xff1a; 、静态类型检查。 通过静态类型检查&#xff0c;开发者可以在编译时发现错误&#xff0…