AVLTree【c++实现】

news2024/11/17 3:24:25

目录

  • AVL树
    • 1.AVL树的概念
    • 2.AVLTree节点的定义
    • 3.AVLTree的插入
    • 4.AVLTree的旋转
      • 4.1左单旋
      • 4.2右单旋
      • 4.3左右双旋
      • 4.4右左双旋
    • 5.AVLTree的验证
    • 6.AVLTree的性能

AVL树

AVLTree的代码实现连接:

AVLTree 代码链接

1.AVL树的概念

学习了二叉搜索树之后,我们知道二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此AVLTree就出现来解决这个问题了。

AVLTree如何解决的呢?——当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度

AVLTree也可以叫做高度平衡二叉搜索树

AVLTree的特点:

  1. 所有子树的左右子树高度差的绝对值不超过1
  2. 所有子树都是AVLTree
  3. AVLTree如果有n个节点,那么其高度保持在log_2_N,搜索的时间复杂度会保持在O(log_2_N)

image-20240926102000806

注意:

上面-1代表对于3这个节点来说,其左子树高度比右子树大1.

1代表右子树高度比左子树高度大1

0代表左右子树高度平衡

2.AVLTree节点的定义

实现AVLTree有多种办法,这里我们选择引入平衡因子。用于判断该树是否平衡

template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left; // 左孩子
	AVLTreeNode<K, V>* _right; // 右孩子
	AVLTreeNode<K, V>* _parent; // 节点的父亲

	int _bf; // 平衡因子

	pair<K, V> _kv; // 键值对
    	
	AVLTreeNode(pair<K, V>)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
		_kv(pair)
	{}
};

3.AVLTree的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的规则进行插入
  2. 调整平衡因子
  3. 根据平衡因子来判断是否平衡,并进行相应处理。【不平衡要旋转】

下图对平衡因子的调整的三种情况进行了分析:

image-20240926122656350

对第一种情况进行解析:

image-20240926124105840

对第二种情况进行解析:

image-20240926124804620

对第三种情况进行解析:

image-20240926124400594

下面是更新平衡因子的代码实现:

		// 更新平衡因子
		while (parent) // 可能会更新到根节点
		{
			// 如果插入的节点是当前parent节点的右孩子
			if (cur == parent->_right)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}

			// 这个时候判断parent的平衡因子的情况是否还要向上调整
			if (parent->_bf == 0)
			{
				// 这个情况说明高度差不变,不需要变化了。
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				// 这种情况就还要向上调整
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				// 这种情况就代表着该parent子树已经不平衡了,要进行旋转处理

			}
		}

要完成AVLTree的插入,旋转的实现是必不可少的。

4.AVLTree的旋转

旋转就是保持树仍然是二叉搜索树的前提下,让它变成平衡二叉搜索树【旋的过程可不是乱旋的】

首先要知道什么情况需要旋转,上面也说了,这里在通过图片复习一下:

就是当树出现高度的不平衡的时候,就需要旋转。

image-20240926195046604

旋转分多种情况的旋转:

4.1左单旋

当节点插入较高的左子树的左侧的时候——左单旋

image-20240926202507470

因此根据上面两个进行左单旋的例子,我们可以总结出左单旋的两个重要规律

  1. subR的左孩子变成parent的右孩子
  2. parent会成为subR的左孩子

有了这个规律,我们就可以用一个抽象图来表示左单旋:

image-20240926202841941

可以进行左单旋的情况是非常多的。上面我们的例子就是当h为0和1的情况。

注意:

在插入前,AVL树是平衡的,新节点插入到60的右子树(注意:此处不是右孩子)中,60右子树增加了一层,导致以30为根的二叉树不平衡,要让30这课树平衡,只能将30子树的高度减少一层,左子树增加一层

即将右子树往上提,这样30转下来,因为30比60大,只能将其放在60的左子树,而如果60有左子树,左子树根的值一定大于30,小于60,只能将其放在30的右子树,旋转完成后,更新节点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:

  1. 60节点的左孩子可能存在,也可能不存在

    要注意左孩子不存在时,是否存在对空指针的解引用

    如果左孩子存在,要处理其父亲指针的链接

  2. 30可能是根节点,也可能是子树

    如果是根节点,旋转完成后,要更新根节点

    如果是子树,可能是某个节点的左子树,也可能是右子树要提前保存下parent的parent,这样才能在此时进行连接

左单旋的代码实现如下:

	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		// 处理关系的时候还要注意到父亲指针的变化
		// 1.先让subR的左孩子变成parent的右孩子,再处理subR左孩子的父亲指针
		parent->_right = subRL;
		if(subRL) // 防止当h为0时,也就是subRL的左孩子是空的情况
			subRL->_parent - parent; // 处理父亲指针

		//2.再让parent成为subR的左孩子,并处理parent的父亲指针
		subR->_left = parent;
		Node* ppNode = parent->_parent; // 防止后面subR链接不到parent->_parent,这里要做个保存
		parent->_parent = subR; // 处理父亲指针
		
		//3.subR的父亲指针也需要变化
		// 这里会有两种情况
		// 1.左单旋之后,subR直接是根节点
		if (_root = parent)
		{
			// 原本parent是根节点,左单旋之后subR变成跟节点
			_root = subR;
			subR->_parent = nullptr;
		}
		else //2.parent不是根节点,左单旋完毕之后subR仍然是子树
		{
			// 这个情况说明原本parent是一个子树,左单旋后subR也是一个子树
			// 这个时候就要链接上原本parent的_parent,上面存储起来是ppNode

			// 但是这个时候还得判断左单旋之后的subR应该是ppNode的左子树还是右子树
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
				subR->_parent = ppNode;
			}
			else
			{
				// 原来的parent是ppNode的右子树,也就是subR要变成ppNode的右子树
				ppNode->_right = subR;
				subR->_parent = ppNode;
			}
		}

		// 左单旋之后,还得修改平衡因子,这样才算结束
		subR->_bf = parent->_bf = 0;
	}

4.2右单旋

当节点插入较高的右子树的右侧的时候——右单旋

image-20240926220741382

因此根据上面两个进行右单旋的例子,我们可以总结出右单旋的两个重要规律

  1. subR的右孩子变成parent的左孩子
  2. parent会成为subR右孩子

有了这个规律,我们就可以用一个抽象图来表示右单旋:

image-20240926220909857

上面举的两个例子就是该抽象图中,h为0或者1的情况。

注意:

在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层

即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:

  1. 30节点的右孩子可能存在,也可能不存在

    要注意右孩子不存在时,是否存在对空指针的解引用

    如果右孩子存在,要处理其父亲指针的链接

  2. 60可能是根节点,也可能是子树

    如果是根节点,旋转完成后,要更新根节点

    如果是子树,可能是某个节点的左子树,也可能是右子树要提前保存下parent的parent,这样才能在此时进行连接

右单旋的代码实现:

// 右单旋
void RotateR(Node* parent)
{
	Node* subR = parent->_left;
	Node* subLR = subR->_right;

	// 处理节点关系的时候要注意处理其父亲指针的关系
	// 1.先让subR的右孩子变成parent的左孩子,再处理subR右孩子的父亲指针
	parent->_left = subLR;
	if (subLR)
		subLR->_parent = parent;// 处理subR右孩子的父亲指针

	// 2.再让parent成为subR的右孩子,再处理parent的父亲指针
	subR->_right = parent;
	Node* ppNode = parent->_parent; // 防止原来的parent是子树,不是根节点
	parent->_parent = subR;// 处理parent的父亲指针

	// 3.再处理更新后的subR的父亲指针
	// 这里又分两种情况
	// 1.一种是parent原先是根节点,subR代替之后,也会变成根节点
	if (_root == parent)
	{
		_root = subR; // subR代替之后,变成根节点
		subR->_parent = nullptr;
	}
	else //2.parent是原先的一个子树,左单旋完毕之后subR仍然是子树
	{
		//原来的parent是其父亲的左子树还是右子树
		if (parent == ppNode->_left)
		{
			// subR替代之后,是ppNode的左子树
			ppNode->_left = subR;
			subR->_parent = ppNode; //处理父亲指针
		}
		else
		{
			// sub替代之后,是ppNode的右子树
			ppNode->_right = subR;
			subR->_parent = ppNode; //处理父亲指针
		}
	}

	//4.处理完毕之后,还要更新平衡因子,这样右单旋才是结束了
	subR->_bf == parent->_bf = 0;
}

4.3左右双旋

左右双旋:其实就是先左单旋,在右单旋。

当给较高的左子树插入一个右节点,就要先左单旋,在右单旋

但是这里会有两种情况

image-20240927145625985

image-20240927151925105

这里虽然有两种情况,但是都是进行左右双旋,即先对30进行左单旋,在对90进行右单旋,规律如下:

  1. 左右双旋完毕后,两种情况下,60都为根。
  2. 并且60的b给了30的右子树。60的c给了90的左子树 【因为b肯定比60小,30大】【c肯定比60大,比90小】
  3. 旋转的情况是相同的。两种情况的旋转情况都是一样的

因此左右双旋难得不是旋转,难得是平衡因子的调节。两种情况的平衡因子是不一样的。

image-20240927155540410

并且除了上述两种情况下,还有一种情况也需要对平衡因子特殊处理:

也就是a b c d四个区域都是空的,60节点的插入本身

image-20240927170247360

一共有三种情况,这三种情况的平衡因子都需要进行不同的处理。

因此判断是那种情况并做出相应的平衡因子调节成为了一个重点

代码实现如下:

这里对parent、subL、subLR的位置给个图片,方便代码理解

image-20240927170440609

	// 左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf; // 先记载下来subLR节点的平衡因子,防止被后面的双旋进行干扰

		// 左右双旋 【双旋无关节点的插入情况,旋转都是一样的】
		RotateL(subL);
		Rotatel(parent);

		// 旋转之后需要对三种不同情况的平衡因子进行处理
		// 这里我们通过前面保存的subRL的平衡因子bf来区分节点是插入b还是c,还是abcd四个区域都为空。
		// 这里的a b c d要结合图片理解【博客或者笔记】

		if (bf == -1)
		{
			// 节点插入b区域
			// 此时subLR成为了根节点或者是整棵树的其中一颗子树
			// parent旋转后的bf == 1, subL的bf == 0
			parent->_bf = 1;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else if (bf == 1)
		{
			// 节点插入区域c
			// 此时subLR成为了根节点或者是整棵树的其中一颗子树
			// parent旋转后bf == 0, subL的bf == 1
			parent->_bf = 0;
			subL->_bf = 1;
			subLR->_bf = 0;
		}
		else // bf == 0
		{
			// 此时abcd四个区域都为空。
			// 旋转之后三个节点的bf都为0
			parent->_bf = 0;
			subL->_bf = 0;
			subLR->_bf = 0;
		}

	}

4.4右左双旋

左右双旋:其实就是先左单旋,在右单旋。

当给较高的右子树插入一个左节点,就要先右单旋,在左单旋

image-20240927163346290

image-20240927164341218

这里虽然有两种情况,但是都是进行右左双旋,即先对90进行右单旋,在30进行左单旋,规律如下:

  1. 右左双旋完毕后,两种情况下,60都为根。
  2. 并且60的b给了30的右子树。60的c给了90的左子树 【因为b肯定比60小,30大】【c肯定比60大,比90小】
  3. 旋转的情况是相同的。两种情况的旋转情况都是一样的

因此双旋难得不是旋转,难得是平衡因子的调节。两种情况的平衡因子是不一样的。

image-20240927164739238

并且除了上述两种情况下,还有一种情况也需要对平衡因子特殊处理:

image-20240927163144848

一共有三种情况,这三种情况的平衡因子都需要进行不同的处理。

因此判断是那种情况并做出相应的平衡因子调节成为了一个重点

代码实现如下:

这里对parent、subR、subRL的位置给个图片,方便代码理解

image-20240927162222997

	// 右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf; // 防止后面的双旋对subRL的平衡因子进行干扰

		// 右左双选的两种情况都是,先右单旋,再左单旋
		RotateR(subR);
		RotateL(parent);

		// 重点是要控制三种情况的平衡因子
		// 这里我们通过前面保存的subRL的平衡因子bf来区分节点是插入b还是c,还是abcd四个区域都为空。
		// 这里的a b c d要结合图片理解【博客或者笔记】

		if (bf == 1)
		{
			// 节点插入c区域
			// 这种情况下,最后subRL成为了根节点或者是其中一个子树
			// 此时parent在旋转之后的平衡因子是-1,subR是0
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else if (bf == -1)
		{
			// 节点插入b区域
			// 这个情况下,最后subRL成为了根节点或者是其中一个子树
			// 此时subR的平衡因子是1,parent是0
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else // 此时bf == 0
		{
			//这种情况是a、b、c、d四个区域都为空。
			// 刚好插入的节点构成了引发双旋的条件
			parent->_bf = 0;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
	}

5.AVLTree的验证

要验证AVLTree,需要验证两个方面:

  1. 首先是一个二叉搜索树【通过中序遍历,查看是否有序】
  2. 高度必须要平衡
  • 验证是否为一个二叉搜索树——通过中序遍历即可:
	// 中序遍历
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << ": " << root->_kv.second << " " << endl;
		_InOrder(root->_right);
	}

	//cpp中一般实现递归都要通过子函数
	// 因为外边调用这个中序遍历接口的时候没办法直接传一个_root进来,_root是私有的
	void InOrder()
	{
		if (_root == nullptr)
		{
			cout << "该树为空" << endl;
			return;
		}

		_InOrder(_root);
		//_InOrder(this->_root); // 等价于上面的
		cout << endl;
	}
  • 验证高度是否平衡:

这里两个方案:

  1. 树的左右子树的高度差的绝对值要<2,就意味着当前树平衡。然后每个左右子树的高度差都 < 2就意味是AVLTree。

  2. 计算当前根节点的平衡因子,将其与根节点存的平衡因子对比。如果相同,就意味着当前树平衡,如果每个子树的平衡因子都不出错,就意味着是AVLTree

	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		// 记载左右子树的高度
		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		//对于当前层数的根节点来说,左右子树大的那个高度+1,才是这棵树的高度
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	bool _IsBanlance(Node* root)
	{
		if (root == nullptr)
			return true;

		// 计算出左右子树的高度
		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		// 如果左右子树高度差的绝对值 < 2 并且 每个左右子树都是满足其左右子树高度差的绝对值<2的话,就是平衡的
		return abs(rightHeight - leftHeight) < 2 && _IsBanlance(root->_left) && _IsBanlance(root->_right);

		// 也可以通过判断平衡因子是否相同,rightHeight - leftHeight计算出当前根节点的平衡因子bf,将其与root->_bf对比。
		// 将每个子树的平衡因子都进行一个对比,如果都是对的,意味着这棵树平衡
		//int bf = rightHeight - leftHeight; // 自己计算的平衡因子
		//return bf == root->_bf && _IsBanlance(root->_left) && _IsBanlance(root->_right);
	}

	bool IsBanlance()
	{
		if (_root == nullptr)
		{
			cout << "该树为空\n";
			return false;
		}

		return _IsBanlance(_root);
	}
  • 测试用例:
void test_AVLTree()
{
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int b[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> avl1;
	AVLTree<int, int> avl2;

	for (auto e : a)
	{
		avl1.insert(pair<int, int>(e, e));
	}
	avl1.InOrder(); // 验证此树为二叉搜索树
	
	// 还得验证高度是否为AVLTree————是否平衡
	cout << avl1.IsBanlance() << endl; // 1
	cout << endl;

	for (auto e : b)
	{
		avl2.insert(pair<int, int>(e, e));
	}
	avl2.InOrder(); // 验证此树为二叉搜索树

    // 是否平衡
	cout << avl2.IsBanlance() << endl; // 1
}

image-20240928170023282

6.AVLTree的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log_2 (N)。

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

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

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

相关文章

18年408数据结构

第一题&#xff1a; 解析&#xff1a;这道题很简单&#xff0c;按部就班的做就可以了。 画出S1&#xff0c;S2两个栈的情况&#xff1a; S1: S2: 2 3 - 8 * 5 从S1中依次弹出两个操作数2和3&a…

甄选范文“论企业应用系统的数据持久层架构设计”,软考高级论文,系统架构设计师论文

论文真题 数据持久层(Data Persistence Layer)通常位于企业应用系统的业务逻辑层和数据源层之间,为整个项目提供一个高层、统一、安全、并发的数据持久机制,完成对各种数据进行持久化的编程工作,并为系统业务逻辑层提供服务。它能够使程序员避免手工编写访问数据源的方法…

IDEA 高版本创建 Spring Boot 项目选不到 java 8

一、场景分析 现在高版本的 IDEA&#xff0c;创建 Spring Boot 项目时常常会选不到 Java 8&#xff1a; 直接使用 Java 17 新建项目&#xff0c;又会报错&#xff1a; Selected version of Java 17 is not supported by the project SDK 1.8. Either choose a lower version o…

Linux增加一个回收站功能(实用功能)

在linux中,默认是没有回收站的概念的,文件被删除之后,就没有了,很难进行恢复,本章教程,教你如何在linux中安装一个回收站功能,让你的文件即使删掉了,也有即使找回来。 一、安装插件 需要注意的是, python版本需要大于3.8,否则安装之后可能无法使用。 1、下载插件 ht…

基于Qt的多功能串口通信工具分享:实时数据收发与波形绘制

需要工程源码请私信 基于 Qt 框架开发的多功能串口通信工具&#xff0c;旨在为用户提供稳定、流畅的串口数据收发体验。该工具不仅支持基本的串口通信功能&#xff0c;还集成了定时发送、多线程数据处理、粘包问题解决、实时波形绘制等多种高级功能。通过使用 QSerialPort 进行…

录屏小白福音!三款神器助你轻松上手

生活工作中&#xff0c;需要借助录屏功能越来越家常便饭了&#xff0c;选择录屏软件时&#xff0c;主要考虑的是软件的易用性、功能以及用户评价等因素。以下是如何进行录屏的步骤&#xff0c;以及推荐的四个录屏软件的使用说明&#xff1a;关于如何录屏的步骤操作&#xff0c;…

使用 PowerShell 命令更改 RDP 远程桌面端口(无需修改防火墙设置)

节选自原文&#xff1a;Windows远程桌面一站式指南 | BOBO Blog 原文目录 什么是RDP&#xfffc;开启远程桌面 检查系统版本启用远程桌面连接Windows 在Windows电脑上在MAC电脑上在Android或iOS移动设备上主机名连接 自定义电脑名通过主机名远程桌面使用Hosts文件自定义远程主…

(undone) MIT6.824 Lecture1 笔记

参考1MIT课程视频&#xff1a;https://www.bilibili.com/video/BV16f4y1z7kn/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 参考2某大佬笔记&#xff1a;https://ashiamd.github.io/docsify-notes/#/study/%E5%88%86%E5%B8%83%…

TDSQL-C电商可视化,重塑电商决策新纪元

前言&#xff1a; 在数字化浪潮席卷全球的今天&#xff0c;电子商务行业以其独特的魅力和无限潜力&#xff0c;成为了推动全球经济增长的重要引擎。然而&#xff0c;随着业务规模的急剧扩张&#xff0c;海量数据的涌现给电商企业带来了前所未有的挑战与机遇。如何高效地处理、…

如何从飞机、电报中提取数据

电报&#xff0c;通常简称TG&#xff0c;是一个跨平台的即时通讯软件。客户端是开源的&#xff0c;而服务器是专有的。用户可以交换加密的、自毁的信息&#xff08;类似于“阅读后烧伤”&#xff09;&#xff0c;并共享各种文件&#xff0c;包括照片和视频。它的安全性很高&…

软件设计之SSM(1)

软件设计之SSM(1) 路线图推荐&#xff1a; 【Java学习路线-极速版】【Java架构师技术图谱】 尚硅谷新版SSM框架全套视频教程&#xff0c;Spring6SpringBoot3最新SSM企业级开发 资料可以去尚硅谷官网免费领取 学习内容&#xff1a; Spring框架结构SpringIoC容器SpringIoC实践…

SD2.0 Specification之功能切换

文章目录 简述命令参数含义状态数据结构及含义功能切换流程Mode0(查询功能)步骤Mode1(切换功能)步骤示例 本文章主要讲解关于SD2.0功能切换(CMD6)的内容&#xff0c;基础概念和其它内容请参考以下文章。 SD2.0 Specification简述 简述 SD卡将一些功能进行分组&#xff0c;归属…

Python爬虫之requests(二)

Python爬虫之requests&#xff08;二&#xff09; 前面演示了requests模块的四种请求方式。接下来再来演示下综合的练习。 一、requests模块综合练习 需求&#xff1a;爬取搜狗知乎某一个词条对应的某个范围页码表示的页面数据。 点开搜狗首页&#xff0c;有一个知乎的版块…

基于小波变换与稀疏表示优化的RIE数据深度学习预测模型

加入深度实战社区:www.zzgcz.com&#xff0c;免费学习所有深度学习实战项目。 1. 项目简介 本项目旨在通过深度学习模型进行RSOP&#xff08;Remote Sensing Observation Prediction&#xff09;的数据预测。RSOP数据是基于远程传感技术采集的多维信息&#xff0c;广泛应用于…

volatile关键字最全原理剖析

介绍 volatile是轻量级的同步机制&#xff0c;volatile可以用来解决可见性和有序性问题&#xff0c;但不保证原子性。 volatile的作用&#xff1a; 保证了不同线程对共享变量进行操作时的可见性&#xff0c;即一个线程修改了某个变量的值&#xff0c;这新值对其他线程来说是…

Android开发中的ViewModel

在Android应用开发中&#xff0c;ViewModel作为架构组件之一&#xff0c;扮演着管理UI数据和生命周期的关键角色。本文将深入探讨ViewModel如何感知View的生命周期&#xff0c;并分析其内核原理&#xff0c;帮助开发者更好地利用ViewModel优化应用架构。 一、ViewModel简介 在…

isilon存储node节点更换你必须知道的知识

最近一直想要写一篇文章是关于EMC Isilon 存储控制器方面的&#xff0c;是什么力量促使我要写这个文章呢&#xff1f;作为一个卖存储备件的资深搬运工&#xff0c;最近遇到了一些关于控制器方面的备件询价、备件更换方面的问题&#xff0c;每次都要花大量的时间给客户解释。解释…

分库分表常见算法,每个高级开发必知必会?

目录标题 分库分表常见算法哈希取模算法容量&#xff08;时间&#xff09;范围算法范围 取模算法 总结 分库分表是一种数据库设计技术&#xff0c;其目的是为了提高数据库的性能和扩展性。它通过将数据库的表拆分到多个数据库中来实现这一目的。 分库分表常见算法 分库分表分…

鸿蒙媒体开发系列12——音频输入设备管理(AudioRoutingManager)

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 有时设备同时连接多个音频输入设备&#xff0c;需要指定音频输入设备进行音频录制&a…

HarmonyOs 学会查看官方文档实现菜单框

1. 学会查看官方文档 HarmonyOS跟上网上的视频学习一段时间后&#xff0c;基本也就入门了&#xff0c;但是有一些操作网上没有找到合适教学的视频&#xff0c;这时&#xff0c;大家就需要养成参考官方文档的习惯了&#xff0c;因为官方的开发文档是我们学习深度任何一门语言或…