C++AVL树

news2024/11/27 6:15:43

目录:

  • AVL树的概念
    • AVL树节点的定义
      • 更新平衡因子
  • AVL树的旋转
    • AVL树的验证
      • AVL的整体实现
    • AVL树的删除
    • AVL树的性能
  • 总结

AVL树的概念

AVL树:二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
特性:
1.它的左右子树都是AVL树
2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
在这里插入图片描述
图中我们可以看到平衡因子等于右子树的高度减左子树的高度,比如节点3的平衡因子就是1-2=-1,所以节点3的平衡因子就是-1。

AVL树节点的定义

template<class k , class v>
struct AVLTreeNode
{
	pair<k, v> _kv;
	AVLTreeNode<k, v>* _left;
	AVLTreeNode<k, v>* _right;
	AVLTreeNode<k, v>* _parent;

	int _bf;//balance factor 
	//成员初值列
	AVLTreeNode(const pair<k, v>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		,_bf(0)//上面的平衡因子受到下面影响,所以初始化为0
	{}
};

更新平衡因子

先按照二叉搜索树的规则将节点插入到AVL树中, 新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否破坏了AVL树 的平衡性。调整的话就是左边插入一个节点,平衡因子减减;右边插入,平衡因子就要加加,但新节点插入后,平衡因子可能存在三种情况:

  1. 如果parent的平衡因子为0,说明插入之前parent的平衡因子为正负1,插入后被调整成0,此时满足 AVL树的性质,插入成功。
  2. 如果parent的平衡因子为正负1,说明插入前parent的平衡因子一定为0,插入后被更新成正负1,此时以parent为根的树的高度增加,需要继续向上更新。
  3. 如果parent的平衡因子为正负2,则parent的平衡因子违反平衡树的性质,需要对其进行旋转处理。

AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。
旋转的目的:
一.让这颗子树的高度差不超过1
二.旋转过程在保持它是搜索树
三.更新调整孩子的平衡因子
四.降低这颗子树的高度跟插入前保持一致

根据节点插入位置的不同,AVL树的旋转分为四种:
1.新节点插入较高右子树的右侧—右右:左单旋在这里插入图片描述
通过上面的抽象图我们看到a/b/c是高度为h的AVL树,c插入节点后,导致高度变化为h+1,右边就高了需要往左边压,就会发生左单转。
左单旋的旋转规则是:节点60的左边b调整到节点30的右边,节点30变成节点60的左边,原来节点30是根,现在节点60是根。为什么这样调整呢?这也是根据二叉树的性质来调整的,旋转前,节点30的右子树一定都大于它,节点60的左子树一定都小于节点60,这样调整也合理,旋转完成后,更新节点的平衡因子即可。
代码如下:

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if(subRL)
		subRL->_parent = parent;
		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;
		if (ppNode==nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
		parent->_bf = subR->_bf = 0;//最后更新平衡因子
	}
  1. 新节点插入较高左子树的左侧—左左:右单旋
    在这里插入图片描述
    通过上面的抽象图我们看到a/b/c是高度为h的AVL树,a插入节点后,导致高度变化为h+1,左边就高了需要往右边压,就会发生右单转。
    右单旋的旋转规则是:节点b变成节点60的左边,节点60变成节点30的右边,原来节点60是根,现在节点30是根,为什么这样调整呢?目的二我们要保证这是一颗搜索数,节点b比节点30大比节点60小,节点60又比节点30大,这样调整也合理,不会破坏这棵树,旋转完成后,更新节点的平衡因子即可。
    代码如下:
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		Node* ppNode = _parent;
		subL->_right = parent;
		parent->_parent = subL;
		if (_root==parent)
		{
			_root = subL;//sybL是根
			_root->_parent = nullptr;		
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}
		subL->_bf = parent->_bf = 0;
	}
  1. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
    在这里插入图片描述
    通过上面的抽象图我们看到a/d为高度h的AVL树,b/c为高度h-1的AVL树,假设节点b是新增的一个节点,单旋的时候我们的AVL树结构像是一条直线,比较好控制平衡,现在这棵树更像是一条折现,这种情况就需要进行双旋,也就是通过两次旋转来调整平衡。
    左右旋转的旋转规则是:第一次节点30为轴点进行一个左单旋,节点b变成30的右边,节点30变成节点60的左边,节点60变成子树的根,第二次节点90为轴点进行一个右单旋,节点c变成节点90的左边,节点90变成节点60的右边,节点60变成子树的根,这个根才是我们旋转两次后得到的这颗子树的根。

这种情况复杂一些,思路就是将双旋变成单旋后再旋转,即:先对节点30进行左单旋,然后再对节点90进行右单旋,旋转完成后再考虑平衡因子的更新。双旋的平衡因子更新比较麻烦,又要分三种情况,注意这里是旋转完后平衡因子的更新,旋转完后的图我就不一一画了,我把旋转之前的图一一画出来供大家参考!
第一种:旋转之前,如果是节点b插入,那么节点60的平衡因子就是-1。
在这里插入图片描述

第二种:如果是节点c插入那么节点60的平衡因子就是1。
在这里插入图片描述

第三种:节点60自己就是新增,它的平衡因子就是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左子树新增
		{
			subL->_bf = 0;
			parent->_bf = 1;
			subLR->_bf = 0;
		}
		else if (bf == 1) // subLR右子树新增
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == 0) // subLR自己就是新增
		{
			parent->_bf = 0;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
  1. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋
    在这里插入图片描述

思路和左右双旋差不多,原理都是一样的,结合抽象图,新增节点后,也是分三种情况:
第一种:旋转之前,如果是节点b插入,那么节点60的平衡因子就是-1。
第二种:旋转之前,如果是节点c插入,那么节点60的平衡因子就是1。
第三种:节点60自己就是新增,它的平衡因子就是0。
代码如下:

  void RotateRL(Node* parent)//右左双旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 1)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

AVL树的验证

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

  1. 验证其为二叉搜索树:
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树。
	void Inorder()
	{
		_Inorder(_root);
	}

	void _Inorder(Node* root)//中序的子函数
	{
		if (root == nullptr)
			return;

		_Inorder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_Inorder(root->_right);
	}
  1. 验证其为平衡树:
    每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)节点的平衡因子是否计算正确。
	bool IsBalance()
	{
		return IsBalance(_root);
	}

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

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		if (rightHeight - leftHeight != root->_bf)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}

		return abs(rightHeight - leftHeight) <=1
			&& IsBalance(root->_left)
			&& IsBalance(root->_right);
	}
  1. 验证用例
    像这种树型结构我们最好自己动手画AVL树的创建过程并验证代码是否有漏洞。
    常规场景1
    {16, 3, 7, 11, 9, 26, 18, 14, 15}
    特殊场景2
    {4, 2, 6, 1, 3, 5, 15, 7, 16, 14}
    我这里写了一个随机种子来验证,我们知道size_t最大可能开辟的数组尺寸是2^64,这里随机给100000个数:
    在这里插入图片描述

AVL的整体实现

这是我AVLTree.h的代码:

#pragma once
#include <assert.h>
#include <time.h>

template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf;  

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};

template<class K, class V>
struct AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = 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 = 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 if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}

				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}
	void RotateL(Node* parent)//左旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;


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

			subR->_parent = ppNode;
		}

		parent->_bf = subR->_bf = 0;
	}

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

		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = ppNode;
		}

		subL->_bf = parent->_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左子树新增
		{
			subL->_bf = 0;
			parent->_bf = 1;
			subLR->_bf = 0;
		}
		else if (bf == 1) // subLR右子树新增
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == 0) // subLR自己就是新增
		{
			parent->_bf = 0;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
       void RotateRL(Node* parent)//右左双旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 1)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
	//中序
	void Inorder()
	{
		_Inorder(_root);
	}
	void _Inorder(Node* root)//中序的子函数
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_Inorder(root->_right);
	}
	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int lh = Height(root->_left);
		int rh = Height(root->_right);
		return lh > rh ? lh + 1 : rh + 1;
	}

	bool IsBalance()
	{
		return IsBalance(_root);
	}

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

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		if (rightHeight - leftHeight != root->_bf)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}

		return abs(rightHeight - leftHeight) <=1
			&& IsBalance(root->_left)
			&& IsBalance(root->_right);
	}
private:
	Node* _root = nullptr;
};

//void TestAVLTree()
//{
//	//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
//	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
//	int a[] = { 8,4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
//	AVLTree<int, int> t;
//	for (auto e : a)
//	{
//		t.Insert(make_pair(e, e));
//	}
//
//	t.Inorder();
//
//	cout << t.IsBalance() << endl;
//}
void TestAVLTree()
{
	srand(time(0));
	const size_t N = 100000;
	AVLTree<int, int> t;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand();
		t.Insert(make_pair(x, x));
		//cout << t.IsBalance() << endl;
	}
	//t.Inorder();
	cout << t.IsBalance() << endl;
}

运行一下:
在这里插入图片描述
bool返回值是1,说明我们的测试用例没有问题!

AVL树的删除

AVL树的插入有了,那么删除呢?有的伙伴可能会问,因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不过与删除不同的时,删除节点后要对平衡因子更新,最差情况下一直要调整到根节点的位置。具体实现可参考《算法导论》或《数据结构-用面向对象方法与C++描述》殷人昆版。

AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这
样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操
作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,
有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数
据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

总结

假如以parent为根的子树不平衡,即parent的平衡因子为2或者-2,分以下情况考虑

  1. parent的平衡因子为2,说明parent的右子树高,设parent的右子树的根为subR
    当subR的平衡因子为1时,执行左单旋。
    当subR的平衡因子为-1时,执行右左双旋。
  2. parent的平衡因子为-2,说明parent的左子树高,设parent的左子树的根为subL
    当subL的平衡因子为-1是,执行右单旋。
    当subL的平衡因子为1时,执行左右双旋。
    旋转完成后,原parent为根的子树个高度降低,已经平衡,不需要再向上更新。
    这就是AVL树,下面我们将继续学习红黑树,记得三连哦,小佳会更新更多干货!!!

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

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

相关文章

CAPL硬件控制课程,物理故障注入自动化以及程控电源

&#x1f4d9; CAN/LIN总线物理故障注入&#xff0c;自动化解决方案 车载网络通讯的健壮性和故障恢复能力至关重要&#xff0c;所以我们需要对控制器进行各种物理容错测试&#xff0c;常规情况下我们需要注入如下8种物理故障注入&#xff1a;CANH断路&#xff0c;CANL断路,CANH…

Redux的基础操作和思想

什么是Redux? Redux是JavaScript应用的状态容器&#xff0c;提供可预测的状态管理! Redux除了和React一起用外&#xff0c;还支持其它框架;它体小精悍(只有2kB&#xff0c; 包括依赖)&#xff0c;却有很强大的插件扩展生态! Redux提供的模式和工具使您更容易理解应用程序中的…

管理类联考——英语——趣味篇——完型填空

完型填空解题秘籍 一、答案分配规律 历年完型答案统计 A B C D 2010 DCBAA/BDCBA/CDDAC/BDCAB 5 5 5 5 2011 ACBDD/BACCB/DBACA/ADACD 6 4 5 5 2012 BBAAC/DAACB/DBCDD/ACCBD 5 5 5 5 2013 ADBDC/BBDBA/ADCCC/CABAD 5 5 5 5 2014 BACAD/ACCDB/ABCDB/DADCB 5 5 5 5 2015 CDC…

leetcode199. 二叉树的右视图(java)

二叉树的右视图 leetcode199. 二叉树的右视图题目描述 广度优先遍历二叉树专题 leetcode199. 二叉树的右视图 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/binary-tree-right-side-view 题目描述 给定一个二叉树的 根…

Python之del析构方法_、call__方法、方法没有重载

一、__del__方法(析构函数)和垃圾回收机制 __del__()称为“析构方法”&#xff0c;用于实现对象被销毁时所需的操作。比如&#xff1a;释放对象占用的资源&#xff0c;例如&#xff1a;打开的文件资源、网络连接等。 Python实现自动的垃圾回收&#xff0c;当对象没有被引用时&…

【AUTOSAR】UDS协议的代码分析与解读(四)----UDS 否定应答服务

6 全局信号需求 一些用于诊断功能的全局信号&#xff0c;应通过整车网络广播至所有的电控单元。信号应包含但不限于如下 所有列&#xff1a; 1) 日期和时间&#xff1b; 2) 蓄电池电压&#xff1b; 3) 点火开关信号&#xff0c; 包括OFF、ACC、ON、START&#xff1b; 4) …

【从删库到跑路】MySQL系列——详细讲解SQL的DDL,DML,DQL,DCL语句

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;关系型数据库⭐概念⭐特点 &#x1f354;MySQL数…

今天面了个支付宝拿35K出来的测试,真是砂纸擦屁股,给我露了一手啊

今年的春招已经开始了&#xff0c;很多小伙伴收获不错&#xff0c;有的已经拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0…

算法之迷宫解法

系列文章目录 文章目录 系列文章目录前言一、迷宫是什么&#xff1f;二、迷宫的生成迷宫的数据结构二维数组图 Prim算法生成地图什么是Prim算法&#xff1f;使用Prim对迷宫生成的实现 三、迷宫的解法深度优先遍历&#xff08;DFS&#xff09; 前言 前几天刷抖音刷到一个迷宫解…

鲸落送书第一期清华出版社系列丛书

1.《Rust项目开发实战》 《Rust项目开发实战》详细阐述了与Rust语言开发相关的基本解决方案&#xff0c;主要包括Rust语言简介、存储和检索数据、创建REST Web服务、创建完整的服务器端Web应用程序、利用Yew创建客户端WebAssembly应用程序、利用quicksilver创建WebAssembly游戏…

【Logback技术专题】「入门到精通系列教程」深入探索Logback日志框架的原理分析和开发实战技术指南(上篇)

深入探索Logback日志框架的原理分析和开发实战指南系列 Logback日志框架Logback基本模块logback-corelogback-classiclogback-accessLogback的核心类LoggerAppenderLayoutLayout和Appender filterlogback模块和核心所属关系 Logbackj日志级别日志输出级别日志级别介绍 Logback的…

MM32F3273G8P火龙果开发板MindSDK开发教程19 - littlefs文件系统的移植

MM32F3273G8P火龙果开发板MindSDK开发教程19 - littlefs文件系统的移植 1、littlefs简介 LittleFS 由ARM官方发布&#xff0c;ARM mbedOS的官方推荐文件系统&#xff0c;具有轻量级&#xff0c;掉电安全的特性。主要用在微控制器和flash上&#xff0c;特点如下&#xff1a; 掉…

java中的多线程、同步代码块、同步方法、锁

一、java中实现多线程的三种方式 &#xff08;1&#xff09;继承Thread类的方式进行实现&#xff1b; &#xff08;2&#xff09;实现Runnable接口的方式进行实现&#xff1b; &#xff08;3&#xff09;利用Callable接口和Future接口方式实现。 1.继承Thread类的方式进行实现 …

【性能优化】性能优化

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录 性能优化运行效率![在这里插入图片描述](https://img-blog.csdnimg.cn/557680b0ca51484c9c2c6c2…

金升阳|三极管的开关速度如何提高?

​三极管是一种常见的电子器件&#xff0c;广泛应用于电路中。它的开关速度是指从关断到导通或从导通到关断的转换速度。提高三极管的开关速度可以提高电路的响应速度&#xff0c;从而提高系统的性能。本文将介绍一些常见的方法来提高三极管的开关速度。 一、选择合适的三极管 …

华为组播实验pim-dm

组播源配置&#xff1a; R1: [r1]dis current-configuration [V200R003C00] sysname r1 snmp-agent local-engineid 800007DB03000000000000 snmp-agent clock timezone China-Standard-Time minus 08:00:00 portal local-server load flash:/portalpage.zip drop illegal-mac…

2023年软件测试有前途吗?技术变革,测试人的进阶发展路线...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 软件测试行业是否…

瑞吉项目优化

Redis缓存菜品 动态构造key。客户端传来的分类id作为key。从redis中获取缓存的数据。根据分类Id&#xff0c;使用redisTemplate.opsForValue().get 方法获取缓存的数据。如果缓存数据存在&#xff0c;直接返回&#xff0c;无需访问数据库。如果不存在&#xff0c;需要查询数据…

Redis从入门到精通【高阶篇】之底层数据结构整数集(IntSet)详解

文章目录 0.前言1.IntSet基本详解1.1 整数集的压缩算法原理1.2 整数集编码方式选择原理1.2.1 判断逻辑1.2.2 举例说明 2. 源码解析2.1. intsetNew2.2. intsetAdd2.3. intsetRemove2.4. intsetFind2.5. intsetUpgradeAndAdd2.6 收获 3.总结4.思考题5. Redis从入门到精通系列文章…

【MySQL 数据库的命令操作】

目录 一、数据库的基本概念二、数据库的发展三、主流的数据库介绍五、关系数据库1、数据库的解释2、数据库的管理3、常用的数据类型4、常见的数据库结构5、SQL语句 四、MySQL 安装方法1、安装Mysql环境依赖包2、创建运行用户3、编译安装4.创建mysql用户5.修改mysql 配置文件6、…