【C++】AVL树

news2024/10/6 8:39:21

​🌠 作者:@阿亮joy.
🎆专栏:《吃透西嘎嘎》
🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在这里插入图片描述

目录

    • 👉AVL树👈
      • AVL树的概念
      • AVL树节点的定义
      • AVL树的插入
      • AVL树的旋转
      • AVL树的验证
      • AVL树的删除(了解)
      • AVL树的性能
      • AVL树的完整代码
    • 👉总结👈

👉AVL树👈

AVL树的概念

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

AVL 树也称为高度平衡二叉搜索树,其满足以下性质:

  • 它的左右子树都是 AVL 树(注:空树也是 AVL 树)
  • 左右子树的高度差(简称平衡因子)的绝对值不超过 1(-1 / 0 / 1)(注:无法保证左右子树的高度差永远为 0)
  • 平衡因子等于右子树高度减去左子树高度,平衡因子是非必须的,我们用的话方便实现 AVL 树。

在这里插入图片描述

如果一颗二叉搜索树是高度平衡的,它就是 AVL 树。如果它有 N 个节点,其高度可保持在 O ( l o g 2 N ) O(log_2 N) O(log2N) ,查找和插入的时间复杂度为O( l o g 2 N log_2 N log2N) 。

AVL树节点的定义

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

	pair<K, V> _kv;	// 键值对
	int _bf;	// balance factor(平衡因子)

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

AVL 树的节点是三叉链结构的,因为插入节点可能会影响父节点的平衡因子,有了指向父节点的指针会方便我们进行平衡因子的更新和旋转操作。新插入的节点的平衡因子都是 0。

AVL树的插入

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

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子

调整节点的平衡因子

在这里插入图片描述

在这里插入图片描述

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	// 树中已经存在Key
		{
			return false;
		}
	}

	// 插入节点
	cur = new Node(kv);
	// 判断在父节点的哪一边插入
	if (parent->_kv.first < kv.first)
		parent->_right = cur;
	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 (abs(parent->_bf) == 1)	// 继续往上更新
		{
			parent = parent->_parent;
			cur = cur->_parent;
		}
		else if (abs(parent->_bf) == 2)
		{
			// 说明parent所在的子树已经不平衡了,需要旋转处理
			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;
}

AVL树的旋转

如果在一棵原本是平衡的 AVL 树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL 树的旋转分为四种:左单旋、右单旋、左右双旋和右左双旋。注:旋转操作可能对整棵树做,也可能对子树做。旋转的原则是:旋转成平衡数并且符合二叉搜索树的规则。一次插入,最多一次旋转。

节点插入在较高右子树的右侧时,需要左单旋

在这里插入图片描述
注:当parent->_bf == 2 && cur->_bf == 1时,此时需要对parent所在的子树进行左单旋。parentsubR不可能为空,subRL可能为空。更新subR的父指针是需要需要parent是否是根节点。

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

	parent->_right = subRL;
	if (subRL)	// subRL不为空,其父指针且才能指向parent
		subRL->_parent = parent;
	// 记录parent的父亲
	Node* ppNode = parent->_parent;
	
	subR->_left = parent;
	parent->_parent = subR;
	// parent就是根节点时,则需要将subR更新为根节点,subR的父指针指向nullptr
	if (parent == _root)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		// 判断原父节点在左边还是右边
		if (ppNode->_left == parent)
			ppNode->_left = subR;
		else
			ppNode->_right = subR;

		subR->_parent = ppN
		ode;
	}
	// 更新平衡因子
	parent->_bf = subR->_bf = 0;
}

节点插入在较高左子树的左侧时,需要右单旋。右单旋和左单旋是一致的。

在这里插入图片描述

注:当parent->_bf == -2 && cur->_bf == -1时,此时需要对parent所在的子树进行右单旋。parentsubL不可能为空,subLR可能为空。更新subL的父指针是需要需要parent是否是根节点。

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

	parent->_left = subLR;
	// subLR不为空时,其父指针才能指向parent
	if (subLR)
		subLR->_parent = parent;

	Node* ppNode = parent->_parent;

	subL->_right = parent;
	parent->_parent = subL;
	// parent就是根节点时,则需要将subR更新为根节点,subR的父指针指向nullptr
	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 = subL->_bf = 0;
}

节点插入在较高左子树的右侧时,需要左右双旋。当parent->_bf == -2 && cur->_bf == 1时,需要进行左右双旋:先对subL进行左单旋,再对parent进行右单旋。

在这里插入图片描述
在这里插入图片描述

// 左右双旋
void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	
	// 通过subLR的平衡因子来区分各种情况
	int bf = subLR->_bf;

	// 先左单旋再右单旋
	RotateL(subL);
	RotateR(parent);

	subLR->_bf = 0;
	if (bf == 1)	// 在c插入新节点
	{
		parent->_bf = 0;
		subL->_bf = -1;
	}
	else if (bf == 0)	// 新增节点就是自己
	{
		parent->_bf = 0;
		subL->_bf = 0;
	}
	else if (bf == -1)	// 在b插入新节点
	{
		parent->_bf = 1;
		subL->_bf = 0;
	}
	else	// 理论上不会走到这里
	{
		assert(false);
	}
}

节点插入在较高右子树的左侧时,需要右左双旋。当parent->_bf == 2 && cur->_bf == -1时,需要进行左右双旋:先对subR进行右单旋,再对parent进行左单旋。

在这里插入图片描述

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

	// 通过subRL的平衡因子来区分各种情况
	int bf = subRL->_bf;

	// 先右单旋再左单旋
	RotateR(subR);
	RotateL(parent);

	subRL->_bf = 0;
	if (bf == 1)	// 在c插入新节点
	{
		parent->_bf = -1;
		subR->_bf = 0;
	}
	else if (bf == 0)	// 新增节点是自己
	{
		parent->_bf = 0;
		subR->_bf = 0;
	}
	else if (bf == -1)	// 在b插入新节点
	{
		parent->_bf = 0;
		subR->_bf = 1;
	}
	else	// 理论不会走到这里
	{
		assert(false);
	}
}

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

  1. pParent 的平衡因子为 2,说明 pParent 的右子树高,设 pParent 的右子树的根为 pSubR
    • 当 pSubR 的平衡因子为 1 时,执行左单旋
    • 当 pSubR 的平衡因子为 -1 时,执行右左双旋
  2. pParent 的平衡因子为 -2,说明 pParent 的左子树高,设 pParent 的左子树的根为 pSubL
    • 当 pSubL 的平衡因子为 -1 时,执行右单旋
    • 当 pSubL 的平衡因子为 1 时,执行左右双旋

旋转完成后,原以 pParent 为根的子树个高度降低,已经平衡,不需要再向上更新。

现在已经将 AVL 树的旋转写完了,那么现在就来验证一下写得对不对。

AVL树的验证

AVL 树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证 AVL 树,可以分两步:1、验证其为二叉搜索树:如果中序遍历可得到一个有序的序列,就说明为二叉搜索树。2、验证其为平衡树:节点的平衡因子是否计算正确,每个节点子树高度差的绝对值不超过 1(注意节点中如果没有平衡因子)。

public:
	// 判断是否是平衡树
	bool IsBalance()
	{
		return _IsBalance(_root);
	}
	// 中序遍历判断是否是二叉搜索树
	void InOrder()
	{
		return _InOrder(_root);
	}
	
private:
	// 求二叉树的高度
	int Height(Node* parent)
	{
		if (parent == nullptr)
			return 0;

		return max(Height(parent->_left), Height(parent->_right)) + 1;
	}
	
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;

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

		return abs(diff) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

测试样例

void AVLTreeTest1()
{
	size_t N = 1000;
	srand(time(nullptr));
	AVLTree<int, int> t;

	for (size_t i = 0; i < N; ++i)
	{
		int x = rand();
		t.Insert(make_pair(x, i));
	}

	t.InOrder();
	cout << endl;
	cout << "IsBalance:" << t.IsBalance() << endl;
}

void AVLTreeTest2()
{
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };  // 测试双旋平衡因子调节
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		t1.Insert(make_pair(e, e));
	}

	t1.InOrder();
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

在这里插入图片描述
在这里插入图片描述

AVL树的删除(了解)

因为 AVL 树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子。最差情况下,需要一直要更新到根节点的位置。

具体实现大家可参考《算法导论》或《数据结构-用面向对象方法与C++描述》殷人昆版。

AVL树的性能

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

在这里插入图片描述

AVL树的完整代码

#pragma once

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

	pair<K, V> _kv;
	int _bf;	// balance factor

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

template <class K, class V>
class 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;
		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 (abs(parent->_bf) == 1)
			{
				//cur = parent;
				//parent = parent->_parent;
				parent = parent->_parent;
				cur = cur->_parent;
			}
			else if (abs(parent->_bf) == 2)
			{
				// 说明parent所在的子树已经不平衡了,需要旋转处理
				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;
	}

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

	void InOrder()
	{
		return _InOrder(_root);
	}

private:
	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 (parent == _root)
		{
			_root = subR;
			subR->_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 (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subL;
			else
				ppNode->_right = subL;

			subL->_parent = ppNode;
		}

		parent->_bf = subL->_bf = 0;
	}
	// 左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		
		// 通过subLR的平衡因子来区分各种情况
		int bf = subLR->_bf;

		// 先左单旋再右单旋
		RotateL(subL);
		RotateR(parent);

		subLR->_bf = 0;
		if (bf == 1)	// 在c插入新节点
		{
			parent->_bf = 0;
			subL->_bf = -1;
		}
		else if (bf == 0)	// 新增节点就是自己
		{
			parent->_bf = 0;
			subL->_bf = 0;
		}
		else if (bf == -1)	// 在b插入新节点
		{
			parent->_bf = 1;
			subL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

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

		// 通过subRL的平衡因子来区分各种情况
		int bf = subRL->_bf;

		// 先右单旋再左单旋
		RotateR(subR);
		RotateL(parent);

		subRL->_bf = 0;
		if (bf == 1)	// 在c插入新节点
		{
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == 0)	// 新增节点是自己
		{
			parent->_bf = 0;
			subR->_bf = 0;
		}
		else if (bf == -1)	// 在b插入新节点
		{
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else
		{
			assert(false);
		}
	}

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

		return max(Height(parent->_left), Height(parent->_right)) + 1;
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

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

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

		return abs(diff) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

private:
	Node* _root = nullptr;
};

👉总结👈

本篇博客主要讲解了什么是 AVL 树、AVL 树的插入、旋转、验证等等。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️

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

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

相关文章

【Linux】CentOS、CentOS Stream、RedHat 和Fedora 之间的关系

目录 简单说明 详细说明 红帽&#xff08;Red Hat&#xff09;系和德班&#xff08;Debian&#xff09;系 简单说明 在centos8之前&#xff1a; Fedora 》RedHat 》CentOS Fedora 是RedHat的“试验场”&#xff0c;很多新功能和特性先加入Fedora 稳定后再加入RedHat&…

YOLOv5 引入 最新 BiFusion Neck | 附详细结构图

YOLO 社区自前两次发布以来一直情绪高涨&#xff01;随着中国农历新年2023兔年的到来&#xff0c;美团对YOLOv6进行了许多新的网络架构和训练方案改进。此版本标识为 YOLOv6 v3.0。对于性能&#xff0c;YOLOv6-N在COCO数据集上的AP为37.5%&#xff0c;通过NVIDIA Tesla T4 GPU测…

99.恢复二叉搜索树

99.恢复二叉搜索树 1、题目描述 题目的额外要求是: 使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用 O(1) 空间的解决方案吗&#xff1f; 2、解题思路 二叉搜索树中某两个节点被交换了数值&#xff0c;那么对中序序列的影响如下&#xff1a; 假设没有交换之前二叉…

活动星投票千人共读一本书网络评选微信的投票方式线上免费投票

“千人共读一本书”网络评选投票_视频投票评选_投票统计小程序_微信不记名投票用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务&am…

正则表达式和re模块

目录 一.基础匹配 1.什么是正则表达式 re模块三个基础方法 re.match(匹配规则&#xff0c;被匹配字符串) search(匹配规则&#xff0c;被匹配字符串) findall(匹配规则&#xff0c;被匹配字符串) 小结 二.元字符匹配 单字符匹配: 示例: 数量匹配 边界匹配 分组匹配…

【Java】【系列篇】【Spring源码解析】【三】【体系】【BeanFactory体系】

BeanFactory体系BeanFactory整体结构体系图顶层接口-BeanFactory1.1、描述1.2、方法解析&#xff08;15个&#xff09;1.2.1、属性1.2.2、获取bean实例1.2.3、获取bean的提供者&#xff08;对象工厂&#xff09;1.2.4、判断是否包含bean1.2.5、单例,原型,bean类型的判断1.2.6、…

SAP ABAP——SAP包(二)【CTS | 传输请求】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计专业大二本科在读&#xff0c;阿里云社区专家博主&#xff0c;华为云社区云享专家&#xff0c;CSDN SAP应用技术领域新兴创作者。   在学习工…

CentOS7.9配置Nginx反向代理+NodeJS部署上线

Nginx配置 Nginx是一个高性能的HTTP和反向代理服务&#xff0c;许多的大型网站都会采用Nginx来进行HTTP服务器托管 安装编译环境gcc g 进入到root目录&#xff1a; yum -y install make zlib zlib-devel gcc-c libtool openssl openssl-devel 安装PCRE PCRE功能时让Ngi…

ue4c++日记3(碰撞报告碰撞位置|力和扭矩)

目录 代码速查 根据世界方向/局部方向移动 根据世界方向/局部方向旋转 加力加扭矩 1碰撞并报告碰撞位置 两个设为阻挡才会阻挡&#xff0c;其中一个是重叠就会重叠 2例子&#xff1a;旋转前进&#xff01;/原地踏步&#xff1f; 3增加力和扭矩 1.头文件 2.cpp文件 …

(14)go-micro微服务服务层Handle开发

文章目录一 Handle层开发功能说明需要完成的服务开发功能&#xff1a;从哪找需要开发的功能二 代码编写三 最后一 Handle层开发功能说明 需要完成的服务开发功能&#xff1a; 登录注册查询用户信息修改信息发送注册邮件发送重置密码邮件重置密码获取权限修改权限退出账号删除…

【计算机视觉】梯度消失和爆炸以及解决方法

问题 梯度消失无论是笔试还是面试都是常客了,其实对应于梯度消失,还有一个梯度爆炸的概念,这又是什么导致的呢?下面我们将根据公式推导来解释何为梯度消失与梯度爆炸。 梯度消失和梯度爆炸的表现 网络层数越多,模型训练的时候便越容易出现 梯度消失(gradient vanish) 和…

史上最全| 14种自动化分拣系统合集

导语大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;你的老朋友&#xff0c;老K。新书上市《智能物流系统构成与技术实践》2023年度-厂商宣传合作位--->点击详情本文为研习社原创&#xff0c;违规转载必究01移栽式02偏转轮式03扫臂式04滑靴式05侧向翻转06推…

C++设计模式(3)——抽象工厂模式

抽象工厂模式 亦称&#xff1a; Abstract Factory 意图 抽象工厂模式是一种创建型设计模式&#xff0c; 它能创建一系列相关的对象&#xff0c; 而无需指定其具体类。 问题 假设你正在开发一款家具商店模拟器。 你的代码中包括一些类&#xff0c; 用于表示&#xff1a; …

Vue3系列二:如何实现对响应式数据的代理

上一篇文章中&#xff0c;我们讲解了 Vue3 中对响应式系统的实现&#xff0c;本章节会更进一步的从数据层面分享 Vue3 中对响应式数据是如何进行代理的&#xff0c;本文主要从引用类型数据和基本类型数据两个方面进行讲解。 实现数据代理的基础 理解 Proxy 和 Reflect 首先&…

26.Isaac教程--导航算法

导航算法 本节详细介绍导航算法。 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录导航算法全局路径规划器规划器模型可见性图算法优化器轨迹规划器全局路径规划器 Isaac 框架中的全局规划器问题被分解为三类&#xff1a;规划器模型、…

SpringBoot使用Swagger2

SpringBoot使用Swagger21.引入swagger依赖2.添加swagger配置类3.测试Controller4.测试5.swagger的注解Api注解ApiOperation注解ApiImplicitParam、ApiImplicitParams注解ApiParam注解ApiResponse、ApiResponses注解ResponseHeader注解ApiModel、ApiModelProperty注解6.更多1.引…

Redis 分布式锁实现文章集锦

前言近两年来微服务变得越来越热门&#xff0c;越来越多的应用部署在分布式环境中&#xff0c;在分布式环境中&#xff0c;数据一致性是一直以来需要关注并且去解决的问题&#xff0c;分布式锁也就成为了一种广泛使用的技术&#xff0c;常用的分布式实现方式为Redis&#xff0c…

PDF压缩在线怎么操作?这几个操作谁还不知道

我们在工作里经常处理非常多的文件&#xff0c;如果每个文件都要储存到设备上是非常困难的&#xff0c;因为这需要占用大量的内存&#xff0c;所以我们需要将PDF文件进行压缩&#xff0c;这样就可以释放我们设备的储存空间&#xff0c;不过对于很多人来说&#xff0c;压缩文件并…

自学Java篇之JFrame创建《石头迷阵小游戏》

自学Java篇之JFrame创建《石头迷阵小游戏》 根据黑马程序员java教程自学完java基础&#xff0c;觉得石头迷阵小游戏案例具有一定的编程练习价值&#xff0c;记录之。 最终效果&#xff1a; 案例主要思想流程&#xff1a; ​ 主要是思想是创建一个4*4的二维数组data&#xff…

【openGauss实战5】表管理及CURD

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…