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

      

目录

一.  什么是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.mfbz.cn/a/455825.html

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

相关文章

【SpringBoot篇】再战 登录注册操作

文章目录 ⭐model.dto.user层&#x1f388;UserRegisterRequest.java&#x1f388;UserLoginRequest.java ⭐model.dto.entity层&#x1f388;User.java ⭐service层&#x1f388;UserService.java&#x1f388;UserServiceimpl.java ⭐controller层⭐mapper层&#x1f388;Use…

Parade Series - WebRTC ( < 300 ms Low Latency ) T.B.D

Parade Series - FFMPEG (Stable X64) 延时测试秒表计时器 ini/config.ini [system] homeserver storestore\nvr.db versionV20240312001 verbosefalse [monitor] listrtsp00,rtsp01,rtsp02 timeout30000 [rtsp00] typelocal deviceSurface Camera Front schemartsp ip127…

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

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

OpenMP 编程模型

OpenMP 内存模型 共享内存模型&#xff1a; OpenMP 专为多处理器/核心、共享内存机器设计&#xff0c;底层架构可以是共享内存UMA或NUM OpenMP 执行模型 基于线程的并行&#xff1a; OpenMP 程序基于多线程来实现并行&#xff0c; 线程是操作系统可以调度的最小执行单元。 …

全氟烷氧基树脂PFA容量瓶在实验室金属杂质测定实验中应用

PFA&#xff08;全氟烷氧基树脂&#xff09;容量瓶是一种高性能的实验室器皿&#xff0c;因其出色的化学稳定性和低吸附性而被广泛应用于各种高精度化学分析。在硅材料表面金属杂质含量的测定中&#xff0c;PFA容量瓶发挥了关键作用。 首先&#xff0c;要了解硅材料表面金属杂质…

高级JAVA工程师解决生产环境JVM宕机Java进程挡掉操作系统内存异常实例讲解

高级JAVA工程师解决生产环境JVM宕机Java进程挡掉内存溢出实例讲解 一、事故描述 生产环境Java进程莫名挡掉&#xff0c;JVM宕机。监控平台报警。生产停了&#xff0c;老板急了&#xff0c;客户爆了&#xff0c;怎么迅速解决事故&#xff1f;每次出现生产事故&#xff0c;都是…

mediapipe 实现姿态分析——举手检测

目录 人体姿态检测 效果展示 举手检测 行业应用 代码实现 代码分析 效果展示 代码修改&#xff0c;一只手举起即可 总结 啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦^_^啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦♪(^∇^*)啦啦啦…

《深入理解springCloud与微服务》笔记

第一章 微服务介绍 1.3 微服务的不足 1.3.2 分布式事务 CAP 理论&#xff0c;即同时满足“一致性”“可用性”和“分区容错”是 件不可能的事。 Consistency &#xff1a;指数据的强一致性。如果写入某个数据成功&#xff0c;之后读取&#xff0c;读到的都是新写入的数据&a…

百度云加速即将下线

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 松松商城作为多年百度云加速代理商&#xff0c;上周接到通知&#xff1a;百度云加速产品计划于2024年4月30日下线&#xff0c;目前也无法做实名了。 同时&#xff0c;百度云加速也开始逐步迁移到百度云&#xff0…

【Sql】MVCC有关问题,以及锁,日志和主从复制原理

目录 MVCC 解决什么问题? 实现原理 隐式字段 undo log Read View(读视图) InnoDB 对 MVCC 的实现 锁 分类 锁升级&#xff1f; InnoDB 的行锁&#xff1f; 死锁避免&#xff1f; 乐观锁和悲观锁 日志 主从复制原理 主从复制的作用 MySQL主从复制解决的问题 涉…

Java基于微信小程序的校园生活互助小助手

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

vue3 el-form中嵌套el-tabale 对输入动态校验

简单案例 <el-form :model"Form" :rules"rules" ref"FormRef" class"formDiv"><el-table :data"Form.copyWriters" style"width: 100%"><el-table-column label"文案链接"><temp…

Day33:安全开发-JavaEE应用SQL预编译Filter过滤器Listener监听器访问控制

目录 JavaEE-预编译-SQL JavaEE-过滤器-Filter JavaEE-监听器-Listen 思维导图 Java知识点 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;序列化数据&#xff0c;身份验证&#xff0c;框架开发&#xff0c;第三方库使用等. 框架库&#xff1a;MyBatis&#…

Mysql锁与MVCC

文章目录 Mysql锁的类型锁使用MVCC快照读和当前读读视图【Read View】串行化的解决 exlpain字段解析ACID的原理日志引擎整合SpringBoot博客记录 Mysql锁的类型 MySQL中有哪些锁&#xff1a; 乐观锁&#xff08;Optimistic Locking&#xff09;&#xff1a;假设并发操作时不会发…

C语言程序环境和预处理Pt.1 - 预处理指令|预处理操作符

电脑所能识别的语言为二进制指令&#xff0c;而我们所写的C语言代码是文本信息。为了能使计算机识别并执行C语言代码&#xff0c;就需要翻译环境&#xff0c;使C语言代码翻译为二进制的指令。 1.按下编译按钮的幕后 - 程序的翻译环境 从C语言源代码到计算机可识别的二进制文件…

MTK安卓开发板_联发科开发板评估套件_安卓主板硬件开发

在介绍开发板之前&#xff0c;让我们先来区分一下核心板和开发板的区别。核心板是一种集成度高、功能完整的计算模块&#xff0c;搭载系统&#xff0c;简化了外围接口&#xff0c;体积尺寸相对较小&#xff0c;主要适用于嵌入式系统。而开发板由核心板底板组成&#xff0c;提供…

spring中事务失效的场景有哪些?

异常捕获处理 在方法中已经将异常捕获处理掉并没有抛出。 事务只有捕捉到了抛出的异常才可以进行处理&#xff0c;如果有异常业务中直接捕获处理掉没有抛出&#xff0c;事务是无法感知到的。 解决&#xff1a;在catch块throw抛出异常。 抛出检查异常 spring默认只会回滚非检…

EI级 | Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多特征分类预测

EI级 | Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多特征分类预测 目录 EI级 | Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多特征分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多…

c/c++ | 求叶子结点个数 |构建B树 | 动态规划--找叶子结点个数

是这样的&#xff0c;一道代码题&#xff0c;根据输入数据&#xff0c;计算运行结果 #include<bits/stdc.h> using namespace std; vector<int>g[10]; int ans 0; void dfs(int x){if(g[x].size() 0){ans;return;}for(int i 0; i < g[x].size(); i){dfs(g[x]…

windows 安装 gitlab-runner CICD

点击搜索图标 手动输入PowerShell, 右键点击管理员权限打开&#xff0c; 一、安装 安装 gitlab runner 文档参考地址 1、下载exe执行文件 我这里是 win64 https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe 2、创建 gitla…