【高阶数据结构】红黑树的插入

news2024/11/24 13:47:48

🤡博客主页:醉竺

🥰本文专栏:《高阶数据结构》

😻欢迎关注:感谢大家的点赞评论+关注,祝您学有所成!


✨✨💜💛想要学习更多《高阶数据结构》点击专栏链接查看💛💜✨✨ 


目录

1. 什么是红黑树? 

1.1 红黑树存在的意义 

1.2 红黑树的定义和性质

1.3 判断是否为红黑树(练习) 

2. 红黑树的插入

2.1 插入案例详细图解

2.2 案例练习 

3 红黑树完整代码实现

3.1 红黑树结点的定义

3.2 插入代码

3.3 红黑树的验证

3.4 红黑树的查找


1. 什么是红黑树? 

在 本文之前,我写了有关“平衡二叉树”的性质、插入和删除的文章,引入了“平衡性”的概念:

《二叉搜索树的插入、删除和查找(精美图解+完整代码)》icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_43382136/article/details/140280070?spm=1001.2014.3001.5501《平衡二叉树(AVL)的插入(4种旋转方法+精美图解+完整代码)》icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_43382136/article/details/140403187?spm=1001.2014.3001.5501《平衡二叉树(AVL)的删除和调整》icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_43382136/article/details/140577239主要目的是让这棵二叉搜索树左右看起来比较“平衡”,不出现左子树很高、右子树很矮,或者左子树很矮、右子树很高的情形。这样,在进行节点的查找、插入、删除等操作时效率会比较高。

但这同时也带来了缺点——在插入和删除节点时,为了调整平衡,必须对树中的节点进行旋转,从而在一定程度上影响程序的执行效率。

平衡二叉树的一条重要性质是“任一节点的左子树和右子树的高度之差不超过1”。这里引入一个“ 非严格平衡二叉树”的概念。

非严格平衡二叉树指的是 不完全符合 前面平衡二叉树的定义,或者说并不是一种严格意义上的平衡二叉树。但这种二叉树最小高度仍旧在log_{2}^{n}(n代表节点数)附近,或者说,查找操作的时间复杂度仍旧为O(log_{2}^{n})。所以,我们仍旧可以认为这种二叉树是一种平衡二叉树。这就引出了这节课要讲解的“红黑树”。

1.1 红黑树存在的意义 

如果你正在思考为什么已经有AVL树了还需要引入红黑树,那么简单来说可以这样理解。

  • 如果AVL树 很大,那么在 插入和删除 操作时会进行大量的旋转操作以达到AVL树的平衡,尤其是删除节点时,可能要经过若干次的旋转操作,甚至可能需要从最底部的节点一直到根节点都进行平衡性调整。

  • 但在红黑树中,当进行插入和删除操作时,维护红黑树的平衡性成本就比较低——多数情况下只需要旋转两次或者不需要旋转只需要对节点的颜色进行修改,也就是把红色修改为黑色。

所以,虽然AVL树比红黑树更加平衡,但针对 插入和删除 操作,红黑树可以保证平均情况时间复杂度更接近O(log_{2}^{n})。换句话说,如果从单纯 搜索 角度来讲, AVL树更快,但如果 频繁 进行 插入和删除 操作,因为 红黑树 需要更少的旋转,无疑 效率会更高,而且红黑树的查找、插入、删除操作性能较稳定。

下面一张图总结为什么需要存在红黑树:

1.2 红黑树的定义和性质

红黑树(Red-Black Tree),简称R-B树,是一种高效的二叉查找树,由Rudolf Bayer在1972年发明的,当时被称为“对称/平衡二叉B树”,后来在1978年由Leo J. Guibas和Robert Sedgewick 修改为“红黑树”。  

红黑树首先是一棵 二叉搜索树,也是一种典型的非严格平衡二叉树,或者你也可以理解成一种 特殊/特化 的AVL树,甚至在很多资料中, 提到平衡二叉树指的就是红黑树。红黑树在 插入和删除 节点时,会通过特定的操作保持二叉查找树的相对平衡,从而获得 比较高的查找效率。既然是相对平衡,所以任一节点的左子树和右子树的高度之差很可能会超过1。  

下面先看一张红黑树的图片,我们比对着图片来讲解红黑树的性质:

性质1每个节点或是红色,或是黑色

性质2根节点必须是黑色的。

性质3叶子节点(外部节点、Nil节点、失败节点)均是黑的。

性质4不存在两个连续的红节点。同时,下面几种说法都是一样的理解一种即可。

  1. 红色节点是被黑色节点分隔开的。

  2. 红色节点的两个孩子节点或者父亲节点必须是黑色的。

  3. 从叶节点到根的所有路径上不可以有两个连续的红色节点。

性质5: 对于每个节点,从该节点到任一叶节点的简单路径上,所含黑节点的数目相同。

这个黑色节点数量叫做“ 黑高度”或者“ 黑高”(bh),用于保证黑色节点的平衡性。  

与“黑高”相关的推论:

根据红黑树需要同时满足 性质4 和 性质5还能得到红黑树有两个特点(为了减少记忆负担了解即可,重要的还是上述性质): 

性质1证明: 

  • 从根到叶的最短路径:这一路径下全是黑色节点没有红色节点。

  • 从根到叶的最长路径:这个路径将包含尽可能多的红色节点。由于红黑树的性质,红色节点后面必然跟随一个黑色节点,因此每增加一个红色节点,就会增加一个黑色节点。当黑色节点数和最短路径上的黑色节点数量相同时,这个最长路径的节点数量(红+黑)就是最短路径黑色节点数量的两倍了。 

性质2证明:

设根节点的黑高为 h, 则 内部节点数最少有2^{h}-1个;
若红黑树的高度为 h, 则 根节点的黑高 ≥ h/2;(最短路径),因此内部节点的个数 n最少有2^{\frac{2}{h}}-1,即n\geqslant 2^{\frac{2}{h}}-1,由此推出h\leqslant 2log_{2}^{(n+1)} 

1.3 判断是否为红黑树(练习) 

为了方便记忆红黑树以上的定义和性质,可以背诵下面的顺口溜:

接下来我们练习一下,来判断下面的树是不是红黑树?

例1: 

例2: 

例3: 

例4: 


2. 红黑树的插入

我们先来想想,红黑树插入节点操作一般分为几种情况呢?

  • 首先,对于没有任何节点的空树,直接创建一个 黑色 节点作为根节点即可。
  • 其次,对于非空的树,查找要插入的位置并插入一个 红色 节点,然后要判断该节点的父节点。

为什么插入的新节点是红色的而不是黑色的呢? 

若我们插入的是黑色结点,那么插入路径上黑色结点的数目就比其他路径上黑色结点的数目多了一个,即破坏了红黑树的性质5(“黑路同”),就会影响红黑树的多条路经。而之所以插入的是红色,是因为红色节点不会增加黑高度,从而尽量减少插入节点后导致的平衡性调整。

  • 插入红色节点之后,如果其父节点为黑色,此时不需要做任何调整。
  • 若其父节点为红色,最多会违背性质红黑树的性质4(“不红红”),此时必须进行平衡性调整。

红黑树的插入看似复杂,其实不难,相比于平衡二叉树而言,红黑树的插入无需更新平衡因子,只是在旋转调整的基础上,再对相关节点进行变色即可!
下面是对红黑树的插入进行的总结,只需要理解下面的内容就能完全理解红黑树的插入,请认真阅读下图。

注意:当叔叔不存在(为叶子节点,Nil,NULL)也属于 “黑叔”。 

LL型: 新插入节点是 爷爷左孩子的左孩子 (右旋)

RR型:新插入节点是 爷爷右孩子的右孩子 (左旋)

LR型:新插入节点是 爷爷左孩子的右孩子 (先左旋,后右旋)

RL型:新插入节点是 爷爷右孩子的左孩子 (先右旋,后左旋)

对上图内容进行分析:
我们知道,插入一个新的红节点时,若父亲也是红色,则最多违背了性质4(“不红红”),此时必须进行调整。从上图中我们了解到红黑树插入后进行调整时:需要看新节点叔叔的颜色

因此,当我们插入一个新的红节点并且违背了红黑树的性质需要进行调整时,此时必有下面情况:

  1. 父亲一定是红色(否则插入后怎么会违背性质呢?);同理,此时爷爷一定是黑色(否则在没插入时就已经父爷俩节点就违背了“不红红”)。
  2. 若新插入节点的叔叔为红色,则有 叔父为红,爷为黑。
  3. 若新插入节点的叔叔为黑色(叔叔不存在也为黑色Nil),则有 父为红,爷叔为黑。

上述 3 条无需记忆,只是进行分析理解即可,为后续代码实现以及调整过程中的“染色”进行铺垫。 

上图调整过程中,所谓的“变色”,就是把相关节点的颜色进行 “ 变黑,黑变 ”。

根据上面3条,以及“染色”,我们可以推出:

调整染色过程中,爷爷要变为红,父亲要变为黑,叔叔不变(原来啥颜色就啥颜色)。
有一点需要注意:若爷爷变为红后,但其是整个红黑树的根,还要将爷爷恢复成黑色(性质1)。


2.1 插入案例详细图解

1. 红叔:变色+变新(无需旋转)
调整方法:无论什么型,父叔变色(父、叔变黑,爷变红),再将爷爷看作为新插入的节点(继续向上调整)

  1. 若爷爷是整个二叉树的根节点,那么为了满足红黑树性质1 “根是黑色”,要把爷爷节点变回黑色。此时整个调整才完毕。
  2. 如果爷爷节点不是整个二叉树的根节点,则还需要继续沿着爷爷节点向上调整,调整中如果遇到一个黑色的前辈节点,则整个平衡性调整完毕。 


 2.黑叔(或者叔叔为空):旋转+变色

调整方法:

LL型:右单旋,父换爷并变色

RR型:左单旋,父换爷并变色

LR型:左、右双旋,儿换爷并变色

RL型:右、左双旋,儿换爷并变色

LL型:新插入节点是爷爷左孩子的左孩子,叔叔节点不存在或者存在但为黑色。 以爷爷节点为根向右旋转。接着将原父亲节点变为黑色,原爷爷节点变为红色。


LR型:新插入节点是爷爷右孩子的右孩子,叔叔节点不存在或者存在但为黑色。 首先以父亲节点为根向左旋转,然后再以爷爷节点为根向右旋转。接着将原来的新节点变为黑色,原来的爷爷节点变为红色。


 RR型:新插入节点是爷爷右孩子的右孩子,叔叔节点不存在或者存在但为黑色。 以爷爷节点为根向左旋转。接着将原父亲节点变为黑色,原爷爷节点变为红色。


RL型:新插入节点是爷爷右孩子的左孩子,叔叔节点不存在或者存在但为黑色。首先以父亲节点为根向右旋转,然后再以爷爷节点为根向左旋转。接着将原来的新节点变为黑色,原来的爷爷节点变为红色。 

2.2 案例练习 

​上述2.1节已经涵盖红黑树所有插入和修改的情况,接下来请根据上图的插入调整步骤,看一下下面的实战练习,图比较多! 


3 红黑树完整代码实现

3.1 红黑树结点的定义

我们这里直接实现 KV模型 的红黑树,为了方便后序的旋转操作,将红黑树的结点定义为三叉链结构,除此之外还新加入了一个成员变量,用于表示结点的颜色。 

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

 红黑树类的定义框架

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;

private:
	Node* _root = nullptr;
};

下面红黑树相关成员函数的实现,都是在RBTree这个类中实现的。 

3.2 插入代码

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;
		return true;
	}

	// 1.找到插入位置
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			return false;
		}
	}

	// 2.插入新节点,新增节点给红色
	cur = new Node(kv);
	cur->_col = RED;
	if (kv.first < parent->_kv.first)
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_right = cur;
		cur->_parent = parent;
	}

	// 3.若插入节点的父亲是黑色的,则不需要调整,
	// 若父亲是红色的,则需要修正红黑树的性质进行调整
	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left) // LL型插入+LR型插入
		{
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED) // 红叔 : 父叔爷变色+爷变新节点(继续往上处理)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上更新处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else // 黑叔(Nil)
			{
				if (cur == parent->_left) // 黑叔(Nil)+LL型插入 : 右旋+父爷变色
				{
					// R单旋
					//     g
					//   p   bu(Nil)
					// c
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else //(cur == parent->_right) 黑叔(Nil)+LR型插入 : 左旋+右旋+儿爷变色
				{
					// LR双旋
					//     g
					//   p   bu(Nil)
					//     c
					RotateLR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
		else  // RR型插入+RL型插入(parent == grandfather->_right)
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED) // 红叔 : 父叔爷变色+爷变新节点(继续往上处理)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else // 黑叔(Nil)
			{
				if (cur == parent->_right) // 黑叔(Nil)+RR型插入 : 左旋+父爷变色
				{
					// L单旋
					//     g
					//  bu   p
					//         c
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else //(cur == parent->_left) // 黑叔(Nil)+RL型插入 : 右旋+左旋+儿爷变色
				{
					//     g
					//  bu   p 
					//     c

					RotateRL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
	}

	_root->_col = BLACK; // 根节点必须是黑色(无论中间过程如何调整过)

	return true;
}

对应的4种旋转代码(看不懂的话,请移步我的文另一篇文章每种旋转方式讲解的特别清晰!) 

// LL型插入,右旋
void RotateR(Node* gf)
{
	Node* p = gf->_left;
	Node* pr = p->_right;
	Node* ggf = gf->_parent;

	gf->_left = pr;
	if (pr != nullptr)
		pr->_parent = gf;

	p->_right = gf;
	gf->_parent = p;

	if (_root == gf)
	{
		_root = p;
		p->_parent = nullptr;
	}
	else
	{
		if (ggf->_left == gf)
		{
			ggf->_left = p;
		}
		else
		{
			ggf->_right = p;
		}

		p->_parent = ggf;
	}
}

// RR型插入,左旋
void RotateL(Node* gf)
{
	Node* p = gf->_right;
	Node* pl = p->_left;
	Node* ggf = gf->_parent;

	gf->_right = pl;
	if (pl != nullptr)
		pl->_parent = gf;

	p->_left = gf;
	gf->_parent = p;

	if (_root == gf)
	{
		_root = p;
		p->_parent = nullptr;
	}
	else
	{
		if (ggf->_left == gf)
		{
			ggf->_left = p;
		}
		else
		{
			ggf->_right = p;
		}

		p->_parent = ggf;
	}
}

// LR型插入,左旋+右旋
void RotateLR(Node* gf)
{
	RotateL(gf->_left);
	RotateR(gf);
}

// RL型插入,左旋+右旋
void RotateRL(Node* gf)
{
	RotateR(gf->_right);
	RotateL(gf);
}

3.3 红黑树的验证

bool ISRBTree()
{
	if (_root == nullptr)
	{
		return true;  // 空树是平衡的
	}

	if (_root->_col == RED)
	{
		return false;  // 根节点必须是黑色
	}

	// 计算参考的黑色节点数量(这个值是从根节点到最左侧叶子节点路径上的黑色节点数。该值将作为比较的标准)
	int refVal = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			++refVal;
		}
		cur = cur->_left;
	}

	int blacknum = 0;
	return _ISRBTree(_root, blacknum, refVal);
}

// 根节点->当前节点这条路径的黑色节点的数量
bool _ISRBTree(Node* root, int blacknum, const int refVal)
{
	if (root == nullptr)
	{
		// 到达叶子节点
		if (blacknum != refVal)
		{
			// 黑色节点数量不相等
			cout << "存在黑色节点数量不相等的路径" << endl;
			return false;
		}
		return true;
	}

	if (root->_col == RED && root->_parent->_col == RED)
	{
		// 存在连续的红色节点
		cout << "有连续的红色节点" << endl;
		return false;
	}

	if (root->_col == BLACK)
	{
		// 当前节点是黑色,增加黑色节点计数
		++blacknum;
	}

	// 递归检查左子树和右子树
	return _ISRBTree(root->_left, blacknum, refVal) && _ISRBTree(root->_right, blacknum, refVal);
}

3.4 红黑树的查找

//查找函数
Node* Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (key < cur->_kv.first) //key值小于该结点的值
		{
			cur = cur->_left; //在该结点的左子树当中查找
		}
		else if (key > cur->_kv.first) //key值大于该结点的值
		{
			cur = cur->_right; //在该结点的右子树当中查找
		}
		else //找到了目标结点
		{
			return cur; //返回该结点
		}
	}
	return nullptr; //查找失败
}

创作不易,对你有用的话麻烦点个收藏或者评论吧~❤ 

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

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

相关文章

CCNA对学历有要求吗?看看你是否有资格报考

思科认证网络助理工程师CCNA作为网络工程领域的权威认证之一&#xff0c;备受年轻人的青睐。然而&#xff0c;对于部分文化水平较低的年轻人来说&#xff0c;他们可能会有一个疑问&#xff1a;CCNA认证对学历有要求吗? 一、CCNA对学历有要求吗? 没有! 针对这一问题&#…

别再盲目选购随身WiFi了!一文教你精准挑选最适合自己的随身WiFi!随身wifi哪个牌子的最好用?

市面上随身WiFi种类繁多&#xff0c;4G/5G&#xff0c;单网设备/三网设备&#xff0c;电池款/USB款/充电宝款等难以抉择。本文旨在为你提供一份详尽的随身WiFi选购指南&#xff01; 首先&#xff0c;明确需求&#xff1a;4G还是5G&#xff1f; 日常用网&#xff0c;如浏览视频…

天锐绿盾加密软件与Ping32:信息安全领域的双子星,谁将引领加密新风尚?

在信息安全这片广袤的星空中&#xff0c;有两颗璀璨的明星格外引人注目&#xff0c;它们就是天锐绿盾加密软件和Ping32。这两款加密软件各自以其卓越的性能、全面的功能和深度的安全保障&#xff0c;赢得了众多企业的青睐。那么&#xff0c;在它们之间&#xff0c;谁将引领加密…

《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 构建公共能力层》

上一篇大概说了 《探索 HarmonyOS NEXT(5.0)&#xff1a;开启构建模块化项目架构奇幻之旅 —— 构建基础特性层》&#xff0c;这一篇继续开发 构建公共能力层。 公共能力层 主要针对公共能力层的各子目录将被编译成HAR包&#xff0c;而他们只能被产品定制层和基础特性层所依赖…

开源 AI 智能名片 2+1 链动模式 S2B2C 商城小程序与私域流量圈层

摘要&#xff1a;本文探讨了私域流量圈层的特点以及其在当今时代的重要性&#xff0c;分析了开源 AI 智能名片 21 链动模式 S2B2C 商城小程序源码在私域流量圈层构建中的作用&#xff0c;阐述了产品在圈层时代被标签化的现象&#xff0c;并以实例展示了如何利用该小程序源码打造…

【网络】传输层协议TCP(中)

目录 四次挥手状态变化 流量控制 PSH标记位 URG标记位 四次挥手状态变化 之前我们讲了四次挥手的具体过程以及为什么要进行四次挥手&#xff0c;下面是四次挥手的状态变化 那么我们下面可以来验证一下CLOSE_WAIT这个状态&#xff0c;这个状态出现的条件是后调用close的一方…

11款PDF阅读器深度体验分享!你选哪一款?

不管是哪个行业还是哪个职位&#xff0c;每天处理的文件中&#xff0c;PDF格式的文档占据了相当大的比例。为了提高工作效率&#xff0c;我尝试了市面上几款流行的PDF阅读器&#xff0c;下面来和大家分享我用过的11款PDF阅读软件怎么样吧。 一、福昕PDF编辑器 直达通道&#…

Gradle篇(入门到精通)

目录 一、前言 1. 项目构建历史 1.1. 传统方式 1.2. 构建工具 1.3. Gradle 2. 初始 groovy 2.1. 什么是 Groovy 2.2. Groovy 安装环境 2.3. groovy 与 Java 对比 3. groovy 特性 3.1. 基础语法 3.2. 闭包 二、gradle 实战 1. Gradle 环境搭建 1.1. 安装 gradle …

8.FreeRTOS之软件定时器

什么是定时器&#xff1f; 简单可以理解为闹钟&#xff0c;到达指定一段时间后&#xff0c;就会响铃。 STM32 芯片自带硬件定时器&#xff0c;精度较高&#xff0c;达到定时时间后会触发中断&#xff0c;也可以生成 PWM 、输入 捕获、输出比较&#xff0c;等等&#xff0c;功…

MySQL 9从入门到性能优化-系统信息函数

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

人脸识别算法 - 专利1分析

专利CN117576758A中说明了一种人脸识别算法的整体识别流程&#xff0c;本文将对这篇文章进行详细讲解&#xff0c;并说明有创造性的算法部分。 前置知识&#xff1a;人脸识别 人脸识别技术是一种通过计算机技术和模式识别算法来识别和验证人脸的技术。它能够用于识别人脸的身份…

Linux之nfs服务器和dns服务器

NFS服务器 NFS&#xff08;Network File System&#xff0c;网络文件系统)&#xff0c;NFS服务器可以让PC将网络中的NFS服务器共享的目录挂载到本地端的文件系统中&#xff0c;而在本地端的系统 中看来&#xff0c;那个远程主机的目录就好像是自己的一个磁盘分区一样。 注&am…

第十八章 用于大型程序的工具

18.1 异常处理 异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并做出相应的处理。异常使得问题的检测与解决过程分离开来。 18.1.1 抛出异常 当执行一个throw时&#xff0c;跟在throw后面的语句将不再被执行。相反&#xff0c;程序的控制权从throw转…

红米K80入网,骁龙8至尊版加持主打极致性价比

小米年度旗舰小米 15、15Pro 已经于本周二正式发布&#xff0c;不过 4499 元与 5299 元起售价想必劝退了不少追求极致性价比的小伙伴儿。 于是有同学表示&#xff1a;接下来人民的希望就全看旗舰捍门员红米了。 好消息是&#xff0c;目前新款红米 K80 系已获入网许可&#xff0…

ARM base instruction -- bfi

Bitfield Insert copies a bitfield of <width> bits from the least significant bits of the source register to bit position <lsb> of the destination register, leaving the other destination bits unchanged. 位域插入将<width>位的位域从源寄存器的…

uicc.hci.service的理解

一、uicc.hci.framework的java类 (1) HCIDevice i : getHCIservice 判断获取的service能否实现&#xff0c;若可以则调用并实现serviceimp&#xff0c;并记录appid。 ii : isHCIServiceAvaliable 用于获取service可用性的信息&#xff0c;返回0代表可用。 二、uicc.hci.servic…

更安全高效的文件传输工具,Ftrans国产FTP替代方案可以了解

文件传输协议&#xff08;FTP&#xff09;&#xff0c;诞生于1971年&#xff0c;自20世纪70年代发明以来&#xff0c;FTP已成为传输大文件的不二之选。内置有操作系统的 FTP 可提供一个相对简便、看似免费的文件交换方法&#xff0c;因此得到广泛使用。 随着企业发展过程中新增…

在元神操作系统启动时自动执行任务脚本

1. 背景 本文主要介绍让元神操作系统启动时自动执行任务脚本的方法&#xff0c;适用于无人化任务执行目的。将任务脚本及相关的应用程序准备好之后&#xff0c;把装有元神操作系统的U盘插入目标电脑&#xff0c;然后打开电脑电源就会自动完成所设置的任务。 2. 方法 &#x…

优雅的LUA数据记录方法-serpent序列化+LUA Table

目录 简述如何集成&#xff1f;如何使用序列化 反序列化 参考 简述 项目里需要使用LUA脚本将数据记录到文件&#xff0c;要方便的增加、查找、删除&#xff0c;要方便的加载与存回。 使用序列化/反序列化 lua table可以很容易实现这些功能。 序列化将table转换为字符串 反序列…

jmeter脚本-请求体设置变量and请求体太长的处理

目录 1、查询接口 1.1 准备组织列表的TXT文件&#xff0c;如下&#xff1a; 1.2 添加 CSV数据文件设置 &#xff0c;如下&#xff1a; 1.3 接口请求体设置变量&#xff0c;如下&#xff1a; 2、创建接口 2.1 见1.1 2.2 见1.2 2.3 准备创建接口的请求体TXT文件&#xff…