【数据结构】二叉树---AVL树的实现

news2024/11/15 11:07:29

      

目录

一.  什么是AVL树

二.  AVL树的结点结构定义

三.  AVL树的动态平衡法

1. 左单旋转 --- RL(RotateLeft) 型调整操作

2. 右单旋转 --- RR(RotateRight) 型调整操作

3. 先左后右双旋转 --- RLR (RotateLeftRight) 型调整操作

4. 先右后左双旋转 --- RRL (RotateRightLeft) 型调整操作

四.  AVL树的插入操作

五.  AVL树的验证操作

六.  完整源代码



一.  什么是AVL树

AVL树,又称平衡二叉树。   

       可以是一颗空树,或者是具有以下性质的二叉树:即它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度只差的绝对值不超过 1。 把二叉树上结点的平衡因子BF定义为该结点的左子树的高度和右子树的高度之差(即平衡二叉树上结点的平衡因子只可能是 -1、0 和 1

       只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。

   说明:后面所用到的平衡因子的都是右子树的高的 - 左子树的高度

二.  AVL树的结点结构定义

       影响二叉搜索树平衡的操作只能是插入和删除,这里已插入为例,同样一组数据元素插  入的顺序不同,二叉搜索树的形状就不同。也就需要一种动态平衡方法,当插入操作破坏了平衡,便可以用来调整。这种方式需要在原来二叉搜索树结点中增加一个量为平衡因子(BF)

结点结构图

在这里为了方便进行旋转操作对于AVL树的结点定义采用三叉链的结构

//类模板结点的定义
template <class T>
struct AVLTreeNode
{
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;   //指向当前结点的父节点的指针
    AVLTreeNode<T> _data;
	int _bf;                      //平衡因子

    //结点的构造函数
	AVLTreeNode(const T& data)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
		,_data(data)
	{}

};

三.  AVL树的动态平衡法

        如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构, 使之平衡化。根据节点插入位置的不同,AVL树的旋转分为以下四种

1. 左单旋转 --- RL(RotateLeft) 型调整操作

      单向左旋平衡处理: 由于在subR这个结点的右子树上插入结点 ,subR的平衡因子由0变为1,p的平衡因子由1变为2,导致以p为根的子树失去平衡,则需进行一次向左的逆时针旋转操作

    链接操作:b链接到p的右;

                      p链接到subR的左;

                      subR成为当前树的根

注意:1. 链接时subRL为空的情况

           2. p可能是整棵树的子树 (p的上面可能还有结点) 或 整棵树的根 (p的上面无结点)

图示:

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

		//做左旋转(修改结点的指向)
		parent->_right = subRL;
		if (subRL)                  //若subRL不为空,则修改subRL中指向父节点的指针(_parent)
			subRL->_parent = parent;
		subR->_left = parent;

		//修改各结点中父指针(_parent)的指向
		Node* ppnode = parent->_parent;         //保存parent中父指针(_parent)的指向
		parent->_parent = subR;                 //修改parent中指向父节点的指针(_parent)

		if (parent == _root)                    //判断当前结点是否为根节点
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppnode->_right == parent)
			{
				ppnode->_right = subR;
			}
			else
			{
				ppnode->_left = subR;
			}
			subR->_parent = ppnode;
		}
		//更新平衡因子
		parent->_bf = 0;
		subR->_bf = 0;
	}

2. 右单旋转 --- RR(RotateRight) 型调整操作

  单向右旋平衡处理: 由于在subL的左子树上插入结点,subL的平衡因子由 0变成 -1 ,p的平衡因子由 1 变为 -2,导致以p为根的子树失去平衡,则需进行一次向右的顺时针旋转操作

   链接操作:b链接到p的左;

                     p链接到subL的右;

                     subL成为当前树的根

注意:1. 链接时subLR为空的情况

           2. p可能是整棵树的子树 (p的上面可能还有结点) 或 整棵树的根 (p的上面无结点)

图示:

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

		//做右旋转(修改结点的指向)
		parent->_left = subLR;
		if (subLR)                  //若subLR不为空,则修改subLR中指向父节点的指针(_parent)
			subLR->_parent = parent;
		subL->_right = parent;

		//修改各结点中父指针(_parent)的指向
		Node* ppnode = parent->_parent;     //保存parent中父指针(_parent)的指向
		parent->_parent = subL;             //修改parent中指向父节点的指针(_parent)

		if (parent == _root)                //判断当前结点是否为根节点
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subL;
			}
			else
			{
				ppnode->_right = subL;
			}
			subL->_parent = ppnode;
		}

		//更改平衡因子
		parent->_bf = 0;
		subL->_bf = 0;
	}

3. 先左后右双旋转 --- RLR (RotateLeftRight) 型调整操作

  双向旋转(先左后右)平衡处理由于在subL的右子树上插入结点,subL的平衡因子由 0 变为 1,p的平衡因子由 -1 变为 -2,导致以p为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作

   链接操作:左单旋:b链接到subL的右;

                                    subL链接到subLR的左;

                                    subLR链接到p的左

                      右单旋:c链接到p的左;

                                     p链接到subLR的右;

                                     subLR成为当前子树的根

图示:

    //先左后右双旋转
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;    //先保存旋转前subLR结点的平衡因子
		RotateL(parent->_left); //左单旋转
		RotateR(parent);        //右单旋转

		//更新旋转后的平衡因子
		if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

4. 先右后左双旋转 --- RRL (RotateRightLeft) 型调整操作

  双向旋转(先右后左)平衡处理由于在subR的左子树上插入结点,subR的平衡因子由 0 变为 -1,p的平衡因子由 1 变为 2,导致以p为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作

   链接操作:右单旋:c链接到subR的左;

                                    subR链接到subRL的右;

                                    subRL链接到p的右

                     左单旋:b链接到p的右;

                                    p链接到subRL的左;

                                    subRL成为当前子树的根

     

图示:

    //先右后左双旋转
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;    //先保存旋转前subRL结点的平衡因子            
		RotateR(subR);          //右单旋转
		RotateL(parent);        //左单旋转

		//更新旋转后的平衡因子
		if (bf == 1)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subRL->_bf = 0;
			subR->_bf = 1;
			parent->_bf = 0;
		}
		else if(bf==0)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

四.  AVL树的插入操作

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

       1. 按照二叉搜索树的方式插入新节点

       2. 调整节点的平衡因子      

        插入一个结点cur(当前要插入的结点)后,Parent的平衡因子一定需要调整,在插入之前,Parent 的平衡因子分为三种情况:-1,0, 1;

分以下两种情况:  

      1. 如果cur插入到Parent的左侧,只需给 Parent 的平衡因子 减1 即可  

      2. 如果cur插入到Parent的右侧,只需给 Parent 的平衡因子 加1 即可  

此时:Parent的平衡因子可能有三种情况:0,1或-1, 2或-2  

      1. 如果Parent的平衡因子为 0说明插入之前Parent的平衡因子为 1或-1插入后被调整成 0,此时满足 AVL树的性质,插入成功  

      2. 如果Parent的平衡因子为 1或-1,说明插入前Parent的平衡因子一定为 0,插入后被更新成 1或-1,此时,以Parent为根的树的高度增加,需要继续向上更新  

      3. 如果Parent的平衡因子为 2或-2,则Parent的平衡因子违反平衡树的性质,需要对其进行旋转处理

bool Insert(const T& data)
{
    //为空树
	if (_root == nullptr)
	{
		_root = new Node(data);
		return true;
	}
    
    //按照二叉搜索树的规则插入结点
	Node* parent = nullptr;  //记录插入结点的父节点
	Node* cur = _root;
	while (cur)
	{
		if (cur->_data < data)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_data > data)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}

    //判断链接到父节点的那边
	cur = new Node(data);
	if (parent->_data > data)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}

	//调整平衡因子 及 旋转调整 
	cur->_parent = parent; //修改当前结点(cur)的父指针(_parent)的指向
	while (parent)
	{
		if (cur == parent->_left) //插入到父结点的左边,父节点的平衡因子--
		{
			parent->_bf--;
		}
		else                 //插入到父结点的左边,父节点的平衡因子--
		{
			parent->_bf++;
		}
		//
		if (parent->_bf == 0)   //若(插入节点后)当前结点的平衡因子为零,则不会影响此节点的父及祖先结点;
		{                       //说明当前这颗子树插入节点后,其高度没有发生变化,也就不需要向上更新平衡因子
			break;
		}
		else if (parent->_bf == 1 || parent->_bf == -1)  //若当前结点的平衡因子为1或-1,则会影响上面的祖先结点的平衡因子
		{											     //需要更新上面祖先的平衡因子
			cur = cur->_parent;
			parent = parent->_parent;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)//若当前结点的平衡因子为2或-2,则需做旋转调整
		{
			//旋转
			if (parent->_bf == 2 && cur->_bf == 1)
			{
				RotateL(parent); //左单旋转
			}
			else if (parent->_bf == -2 && cur->_bf == -1)
			{
				RotateR(parent); //右单旋转
			}
			else if (parent->_bf == -2 && cur->_bf == 1)
			{
				RotateLR(parent); //先左单旋转,在右单旋转
			}
			else
			{
				RotateRL(parent); //先右单旋转,在左单旋转
			}

			break;
		}
		else            //说明插入之前AVL数就有问题
		{
			assert(false);
		}
	}
	return true;
}

五.  AVL树的验证操作

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

     1. 验证其为二叉搜索树 如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

            验证方法:采用中序遍历即可

     2. 验证其为平衡树 每个节点子树高度差的绝对值不超过1

         (注意节点中如果没有平衡因子) 节点的平衡因子是否计算正确

            验证方法:1.验证每颗子树的左右高度差的绝对值是否超过 1(采用递归思想);

                              2.验证结点的平衡因子是否正确

    //中序遍历
	void InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		InOrder(root->_left);
		cout << root->_data << endl;
		InOrder(root->_right);
	}


    //求树的高度
    int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftheight = _Height(root->_left);
		int rightheight = _Height(root->_right);
		return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
	}


    // 验证AVL树的平衡
	bool _IsAVLTree1(Node* root) //前序遍历 
	{
		if (root == nullptr)
			return true;

		int leftheight = _Height(root->_left);  //左子树的高度
		int rightheight = _Height(root->_right); //右子树的高度
        
        //判断左右高度差是否超过1
		if (abs(rightheight - leftheight) >= 2)
		{
			cout << root->_data << "不平衡" << endl;
			return false;
		}
		
        //判断结点的平衡因子是否有异常	
		if (rightheight - leftheight != root->_bf)
		{
			cout << root->_data << "平衡因子异常" << endl;
			return false;
		}

		return _IsAVLTree1(root->_left) && _IsAVLTree1(root->_right);
	}

六.  完整源代码

template <class T>
struct AVLTreeNode
{
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;   //指向当前结点的父节点的
	int _bf;                      //平衡因子
	T _data;

	AVLTreeNode(const T& data)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
		,_data(data)
	{}

};

template<class T>
class AVLTree
{
	typedef AVLTreeNode<T> Node;
public:

	AVLTree()
		: _root(nullptr)
	{}

	bool Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_data < data)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_data > data)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(data);
		if (parent->_data > data)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}

		//调整平衡因子 及 旋转调整 
		cur->_parent = parent; 
		while (parent)
		{
			if (cur == parent->_left) //插入到父结点的左边,父节点的平衡因子--
			{
				parent->_bf--;
			}
			else                 //插入到父结点的左边,父节点的平衡因子--
			{
				parent->_bf++;
			}
			
			if (parent->_bf == 0)   
			{                       
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1) 
			{											     //需要更新上面祖先的平衡因子
				cur = cur->_parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//旋转
				if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent); //左单旋转
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent); //右单旋转
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent); //先左单旋转,在右单旋转
				}
				else
				{
					RotateRL(parent); //先右单旋转,在左单旋转
				}

				break;
			}
			else            //说明插入之前AVL数就有问题
			{
				assert(false);
			}
		}
		return true;
	}

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

		//做左旋转(修改结点的指向)
		parent->_right = subRL;
		if (subRL)                  //若subRL不为空,则修改subRL中指向父节点的指针(_parent)
			subRL->_parent = parent;
		subR->_left = parent;

		//修改各结点中父指针(_parent)的指向
		Node* ppnode = parent->_parent;         //保存parent中父指针(_parent)的指向
		parent->_parent = subR;                 //修改parent中指向父节点的指针(_parent)

		if (parent == _root)                    //判断当前结点是否为根节点
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppnode->_right == parent)
			{
				ppnode->_right = subR;
			}
			else
			{
				ppnode->_left = subR;
			}
			subR->_parent = ppnode;
		}
		//更新平衡因子
		parent->_bf = 0;
		subR->_bf = 0;
	}

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

		//做右旋转(修改结点的指向)
		parent->_left = subLR;
		if (subLR)                  //若subLR不为空,则修改subLR中指向父节点的指针(_parent)
			subLR->_parent = parent;
		subL->_right = parent;

		Node* ppnode = parent->_parent;   
		parent->_parent = subL;             

		if (parent == _root)              
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subL;
			}
			else
			{
				ppnode->_right = subL;
			}
			subL->_parent = ppnode;
		}

		//更改平衡因子
		parent->_bf = 0;
		subL->_bf = 0;
	}

	//先左后右双旋转
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;    
		RotateL(parent->_left); 
		RotateR(parent);        

		//更新旋转后的平衡因子
		if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	//先右后左双旋转
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;    
		RotateR(subR); 
		RotateL(parent);   
    
		//更新旋转后的平衡因子
		if (bf == 1)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subRL->_bf = 0;
			subR->_bf = 1;
			parent->_bf = 0;
		}
		else if(bf==0)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
    
    //高度
	int Height()
	{
		return _Height(_root);
	}

	// AVL树的验证
	bool IsAVLTree()
	{
		return _IsAVLTree1(_root);
	}

	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
	}

private:
	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftheight = _Height(root->_left);
		int rightheight = _Height(root->_right);
		return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
	}

	// AVL树的验证
	bool _IsAVLTree1(Node* root) //前序遍历 
	{
		if (root == nullptr)
			return true;

		int leftheight = _Height(root->_left);
		int rightheight = _Height(root->_right);

		if (abs(rightheight - leftheight) >= 2)
		{
			cout << root->_data << "不平衡" << endl;
			return false;
		}
			
		if (rightheight - leftheight != root->_bf)
		{
			cout << root->_data << "平衡因子异常" << endl;
			return false;
		}

		return _IsAVLTree1(root->_left) && _IsAVLTree1(root->_right);
	}

	//中序遍历
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_data << endl;
		_InOrder(root->_right);
	}

private:
	Node* _root;
};


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

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

相关文章

python工具方法 47 基于paddleseg将目标检测数据升级为语义分割数据

在进行项目研究时,通常需要搜集开源数据集。但是所能搜集到的数据集通常会存在形式上的差异,比如我想要的是语义分割数据,而搜集到的数据集却是目标检测数据;在这种情况下所搜集的数据就完成没有利用价值了么?不,其还存在价值,我们可以通过模型训练对数据标签的标注粒度…

【学习】python装饰器

这篇文章讲的太清楚了&#xff01;非常好好好&#xff01;&#xff01;&#xff01;Python】一文弄懂python装饰器&#xff08;附源码例子&#xff09; 在他的基础上加了一些自己的话&#xff0c;便于自己理解。 注意的点&#xff1a; func 表示函数对象&#xff0c;可以将其…

智慧公厕建设,助力打造宜居、韧性、可持续的智慧城市

公共厕所作为智慧城市的重要组成部分&#xff0c;对于城市的高质量发展起着至关重要的作用。智慧公厕建设旨在通过全面监测、控制和管理公共厕所&#xff0c;实现多方面功能&#xff0c;包括公共厕所环境监测与调控、厕位占用监测与引导、消耗品监测与缺失提示、安全防范与管理…

使用OCC进行旋转扫掠

旋转扫掠是将物体以某一个坐标轴为参照&#xff0c;按照指定的角度旋转生成新的图形的过程 这里使用面的案例&#xff0c;使用线的逻辑处理其实是一样的 //构造旋转轴 gp_Ax1 anAxis; //设置轴的原点 anAxis.SetLocation(0,0,0); //设置轴的方向 anAxis.SetDirection(gp_Dir(0…

N3-Chitosan N3 叠氮修饰壳聚糖 改性叠氮 CS-Azide

碳水科技&#xff08;Tanshtech&#xff09;可以提供壳聚糖衍生物 1.壳聚糖的各种改性(NH2/COOH/SH/N3/MAL-Chitosan等) 2.各种靶向小分子修饰壳聚糖&#xff08;Biotin/FA/cRGD-Chitosan等&#xff09; 3.各种荧光标记壳聚糖(FITC/RB/CY-Chitosan等) 4.壳聚糖和各种聚合物…

基于springboot实现数据资产管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现数据资产管理系统演示 摘要 固定资产管理系统主要是完成对系统用户管理、资产信息管理、资产变更管理、资产用途管理、资产类别管理和资产增减管理。因为利用本系统管理员可以直接录入信息&#xff0c;修改信息&#xff0c;删除信息&#xff0c;并且若在录入…

【学一点RISC-V】RISC-V IMSIC

IMSIC RISC-V AIA 文档 第三章 Incoming MSI Controller (IMSIC) 传入 MSI 控制器&#xff08;IMSIC&#xff09;是一个可选的 RISC-V 硬件组件&#xff0c;与 hart 紧密相连&#xff0c;每个 hart 有一个 IMSIC。IMSIC 接收并记录 Hart 的传入消息信号中断 (MSI)&#xff0c;并…

Ubuntu下txt中文显示乱码问题常规解决方法

在正常使用ubuntu 文档时&#xff0c;突然发现txt文档出现&#xff0c;如下情况 无法正常观看&#xff0c;后来搜了一下发现是gedit 没有对应打开文件的编码格式&#xff0c;Ubuntu用的是utf-8,所以打开会有乱码&#xff01;初始没有GBK和GB2312&#xff0c;把GBK和GB2312添加…

Python:函数的形参与实参

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 函数基本概念 在Python中&#xff0c;函数是一种将代码封装以进行重复使用的机制。它们允许你定义一段代码&#xff0c;以便在程序的多个位置调…

微信小程序的手工艺品定制商城溯源交易系统

管理员通过点击后台管理&#xff0c;进入页面可以获取首页、个人中心、用户管理、商品分类管理、商品信息管理、我的定制管理、我的预约管理、系统管理、订单管理等功能模块&#xff0c;进行相对应操作 用户登录到小程序首页可以查看首页、商品信息、工艺品资讯、购物车、我的等…

OpenHarmony教程指南-性能示例

介绍 本示例集成了条件渲染、动态加载以及HiDumper等场景来介绍如何提升应用性能。 效果预览 HiDumper使用说明&#xff1a; 1.点击性能示例主页的HiDumper按钮&#xff0c;进入HiDumper查看组件信息场景页。 1.点击HiDumper查看组件信息场景页的查看应用组件树进入场景页。…

msf端口转发达到远程连接

一般云服务器上搭建msf没办法远程连接这样很苦恼 这里可以使用端口转发流量来实现 1.首先确认目标机开启了远程连接 然后使用命令将对方3389的端口转发到我们的任意监听端口 portfwd add -l 6666 -p 3389 -r 192.168.78.129 转发成功后ifconfig一下发现已经变化了 然后直接…

Python使用openpyxl库或pandas库创建.xlsx格式的Excel文件,并向文件不同的sheet按行或按列写入内容

import openpyxl# 创建-一个Workbook对象 wb openpyxl.Workbook()# 创建多个工作表 sheet1 wb.active sheet1.title "s1"sheet2 wb.create_sheet("s2")# 在不同的工作表中写入数据 sheet1["A1"] Data for Sheet1 sheet1["A2"] D…

STM32 利用FlashDB库实现在线扇区数据管理不丢失

STM32 利用FlashDB库实现在线扇区数据管理不丢失 &#x1f4cd;FalshDB地址:https://gitee.com/Armink/FlashDB ✨STM32没有片内EEPROM这样的存储区&#xff0c;虽然有备份寄存器&#xff0c;仅可以实现对少量数据的频繁存储&#xff0c;但是依赖备份电源&#xff08;BAT引脚&a…

xcode15,个推推送SDK闪退问题处理办法

个推iOS推送SDK最新版本 优化了xcode15部分场景下崩溃问题&#xff0c;以及回执上传问题&#xff0c;近期您的应用有发版计划&#xff0c;建议更新SDK&#xff1a; 1&#xff09;GTSDK更新到3.0.5.0以及以上版本&#xff1b; 2&#xff09;GTCommonSDK更新到3.1.0.0及以上版本…

【PCL】(二十八)点云超体素分割

&#xff08;二十九&#xff09;点云超体素分割 论文&#xff1a;Voxel Cloud Connectivity Segmentation - Supervoxels for Point Clouds supervoxel_clustering.cpp #include <pcl/console/parse.h> #include <pcl/point_cloud.h> #include <pcl/point_ty…

Linux:kubernetes(k8s)prestop事件的使用(11)

他的作用是在结束pod容器之后进行的操作 apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型 metadata: # pod相关的元数据&#xff0c;用于描述pod的数据name: nginx-po # pod名称labels: # pod的标签type: app #这个是随便写的 自定义的标签version: 1.0.0 #这个…

AI日报:一个新的“科技超级周期”正在出现

文章目录 技术周期预测可连接设备 技术周期 未来学家艾米韦伯表示&#xff0c;人工智能和其他两种通用技术将迎来一个新的“技术超级周期”&#xff0c;预计将在经济中创造“实质性和持续性”的变化。 她在SXSW 2024上表示&#xff0c;过去的科技超级周期是由通用技术引发的&…

二、HarmonyOS 操作系统以及相关生态

前言 2019年8月9日&#xff0c;华为技术有限公司在华为开发者大会上正式发布了HarmonyOS 1.0&#xff0c;同时宣布该操作系统源代码开源。 2020年9月10日&#xff0c;HarmonyOs 2.0正式发布。与HarmonyOs 1.0版本相比&#xff0c;HarmonyOs 2.0在分布式软总线、分布式数据管理、…

德人合科技 | 公司办公终端、电脑文件资料 \ 数据透明加密防泄密管理软件系统

天锐绿盾是一款全面的企业级数据安全解决方案&#xff0c;它专注于为企业办公终端、电脑文件资料提供数据透明加密防泄密管理。 首页 德人合科技——www.drhchina.com 这款软件系统的主要功能特点包括&#xff1a; 1. **透明加密技术**&#xff1a; 天锐绿盾采用了透明加密技…