C++ 实现AVL树

news2024/11/24 16:44:52

目录​​​​​​​

0.二叉搜索树

1.AVL树的概念

 2.AVL树节点的定义

 3.AVL树的插入

4.AVL树的旋转逻辑

5.判断是否符合AVL树

6.完整代码

7.数据测试 


0.二叉搜索树

C++ 搜索二叉树-CSDN博客


1.AVL树的概念

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

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树。
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
O(logn),搜索时间复杂度O(logn)


 2.AVL树节点的定义

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

	int _bf;  // 平衡因子

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

以下是该类的详细解释:

  1. 模板参数KV是两个类型参数,分别表示节点中键(Key)和值(Value)的类型。

  2. 成员变量

    • _left:指向左子节点的指针。
    • _right:指向右子节点的指针。
    • _parent:指向父节点的指针。注意,有些AVL树实现可能不包括此成员,但在某些情况下,如需要快速向上遍历树时,它是有用的。
    • _kv:一个pair,包含键和值。
    • _bf(平衡因子):用于表示该节点左子树和右子树的高度差。平衡因子的值可以是-1、0或1。如果AVL树中的任何节点的平衡因子超过这个范围,树就会失去平衡,需要进行旋转操作来恢复平衡。

 3.AVL树的插入

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) // 1 -1 -> 0
			{
				// 更新结束
				break;
			}
			else if(parent->_bf == 1 || parent->_bf == -1)  // 0 -> 1 -1
			{
				// 继续往上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2) // 1 -1 -> 2 -2
			{
				// 当前子树出问题了,需要旋转平衡一下
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);//右单旋转
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);//、左单旋转
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);//右左二次旋转
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);//左右二次旋转
				}
				
				break;
			}
			else
			{
				// 理论而言不可能出现这个情况
				assert(false);
			}
		}
private:
    Node* _root =nullptr;
};

代码解释:

  1. 检查根节点是否为空:如果根节点为空,则创建一个新节点作为根节点并返回true。
  2. 遍历树以找到插入位置:使用parent和cur指针来遍历树,直到找到一个空位置(即cur为nullptr)或找到与key相等的节点。
  3. 处理重复键:如果找到与key相等的节点,函数返回false,表示插入失败(因为BST中通常不允许重复的键)。
  4. 插入新节点:在遍历结束后,根据parent节点的键值来决定新节点是应该作为左子节点还是右子节点。
  5. 更新平衡因子
    • 从插入节点的父节点开始,向上遍历树,更新每个遍历到的节点的平衡因子。
    • 如果新节点是父节点的左子节点,则父节点的平衡因子减1;如果是右子节点,则加1。
  6. 检查是否需要旋转
    • 在更新平衡因子的过程中,检查每个节点的平衡因子是否超出了允许的范围(-1, 0, 1)。
    • 如果某个节点的平衡因子为2或-2,则需要进行旋转操作来恢复树的平衡。
  7. 执行旋转操作
    • 根据不平衡节点的子节点的平衡因子,确定是进行左旋转还是右旋转。
    • 执行旋转操作,可能需要同时考虑子树的旋转。
    • 旋转后,重新计算并设置相关节点的平衡因子。
  8. 继续向上更新
    • 在旋转后,继续向上遍历树,检查并更新平衡因子,直到根节点或遇到一个平衡因子为0或±1的节点为止。
  9. 结束插入
    • 如果在遍历过程中没有遇到需要旋转的情况,或者已经通过旋转恢复了树的平衡,那么插入操作就完成了。

4.AVL树的旋转逻辑

1.不用旋转的理想状况:

每个节点的平衡因子都在允许的范围(-1, 0, 1)。

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

1. 新节点插入较高左子树的左侧---左左:右单旋

        上图在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左子树增加
        了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,
        即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:

  1. 30节点的右孩子可能存在,也可能不存在
  2. 60可能是根节点,也可能是子树

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

                如果是子树,可能是某个节点的左子树,也可能是右子树

代码:

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

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

		subL->_right = parent;

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

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

			subL->_parent = ppNode;
		}

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

代码讲解:

  1. 准备工作
    • Node* subL = parent->_left;:获取当前需要旋转的节点parent的左子节点subL
    • Node* subLR = subL->_right;:获取subL的右子节点subLR,因为右单旋会将subL提升到parent的位置,而subLR将成为新的subL的左子节点。
  2. 处理parent的左子节点
    • parent->_left = subLR;:将parent的左子节点指针指向subLR
    • if(subLR) subLR->_parent = parent;:如果subLR不为空,则将其父节点指针指向parent
  3. 处理subL的右子节点
    • subL->_right = parent;:将subL的右子节点指针指向parent,完成右旋操作的核心步骤。
  4. 更新父节点指针
    • Node* ppNode = parent->_parent;:获取parent的父节点ppNode
    • parent->_parent = subL;:将parent的父节点指针指向subL
  5. 处理根节点情况
    • 如果parent是根节点_root,则执行以下操作:
      • _root = subL;:将根节点更新为subL
      • _root->_parent = nullptr;:将新的根节点的父节点指针设置为nullptr
  6. 处理非根节点情况
    • 如果parent不是根节点,则根据parentppNode的左子节点还是右子节点,更新ppNode的相应子节点指针。
      • 如果ppNode->_left == parent,则ppNode->_left = subL;
      • 否则,ppNode->_right = subL;
    • subL->_parent = ppNode;:将subL的父节点指针指向ppNode
  7. 重置平衡因子(这里可能不完全正确):
    • parent->_bf = subL->_bf = 0;:在实际应用中,这里只是简单地重置了parentsubL的平衡因子为0。但在AVL树中,平衡因子应该是根据子树的高度差来计算的。因此,在右单旋后,需要递归地更新这两个节点及其所有祖先节点的平衡因子。
  8. 更新高度和平衡因子
    • 在完成旋转后,需要遍历并更新所有受影响的节点的高度(如果树中存储了高度信息)。
    • 更新所有相关节点的平衡因子。平衡因子通常是左子树高度减去右子树高度。在右旋之后,ZY的平衡因子会发生变化。

2. 新节点插入较高右子树的右侧---右右:左单旋

实现及情况考虑可参考右单旋。

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

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

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

		parent->_parent = subR;

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

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

3. 新节点插入较高左子树的右侧---左右:先左单旋再右单旋

将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再
考虑平衡因子的更新。

代码;

	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);
		}
	}

代码讲解:

  1. 准备工作
    • Node* subL = parent->_left;:获取当前需要旋转的节点parent的左子节点subL
    • Node* subLR = subL->_right;:获取subL的右子节点subLR,因为这个节点的高度过高,导致整棵树失去平衡。
    • int bf = subLR->_bf;:保存subLR的平衡因子,因为后面的更新会基于这个值。
  2. 执行左旋
    • RotateL(parent->_left);:对parent的左子树(即subL)执行左旋操作。左旋的目的是将subLR提升到subL的位置或更高层级,使得subL的右子树高度降低。
  3. 执行右旋
    • RotateR(parent);:在左旋之后,对parent执行右旋操作。右旋的目的是将subL(经过左旋后可能已经变化)提升到parent的位置或更高层级,进一步平衡树。
  4. 更新平衡因子
    • 根据subLR的原始平衡因子bf的值,更新subLRsubLparent的平衡因子。由于左旋和右旋后,这些节点的子树结构已经发生变化,因此需要重新计算或设置平衡因子。
    • if (bf == -1):如果subLR的原始平衡因子为-1,说明subLR的左子树比右子树高一个层级。经过双旋后,subLR的左右子树高度平衡,因此subLR->_bf = 0subL的右子树高度没有变化(因为subLRsubL的右子节点),所以subL->_bf = 0;而parent的左子树高度降低了一个层级(因为subL现在是parent的右子节点),所以parent->_bf = 1
    • else if (bf == 1):如果subLR的原始平衡因子为1,说明subLR的右子树比左子树高一个层级。经过双旋后,subLR的左右子树高度平衡,因此subLR->_bf = 0subL的右子树高度增加了一个层级(因为subLR的右子树现在成为了subL的左子树),所以subL->_bf = -1;而parent的左右子树高度没有变化,所以parent->_bf = 0
    • else if (bf == 0):如果subLR的原始平衡因子为0,说明subLR的左右子树高度平衡。经过双旋后,各节点的平衡因子均保持不变,均为0。
    • else:由于bf只可能是-1、0或1,所以这个else分支实际上是不会执行的。这里使用assert(false);是为了在开发阶段确保bf的值是合法的。

4. 新节点插入较高右子树的左侧---右左:先右单旋再左单旋

实现及情况考虑可参考左右双旋。

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(subR);
		RotateL(parent);

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


5.判断是否符合AVL树

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

		return max(_Height(root->_left), _Height(root->_right)) + 1;
	}

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

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		// 不平衡
		if (abs(leftHeight - rightHeight) >= 2)
		{
			cout << root->_kv.first << endl;
			return false;
		}
		
		// 顺便检查一下平衡因子是否正确
		if (rightHeight - leftHeight != root->_bf)
		{
			cout << root->_kv.first << endl;
			return false;
		}

		return _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}
  1. 基础情况

    • 如果rootnullptr(即树为空),那么它自然是平衡的,所以返回true
  2. 计算左右子树的高度

    • 使用_Height函数(这个函数在提供的代码段中没有给出,但我们可以假设它返回给定节点为根的子树的高度)计算左子树和右子树的高度。
  3. 检查高度差

    • 计算左右子树的高度差(abs(leftHeight - rightHeight))。
    • 如果这个高度差大于或等于2,说明树不是AVL树,所以打印出当前根节点的值(可能是为了调试目的),并返回false
  4. 检查平衡因子

    • AVL树的每个节点通常都有一个平衡因子(BF),它是右子树高度减去左子树高度的结果。
    • 接下来,代码检查右子树高度减去左子树高度得到的差值是否等于当前节点的平衡因子(root->_bf)。
    • 如果不等,说明平衡因子不正确,可能是在之前的旋转或插入/删除操作中出现了错误,所以打印出当前根节点的值(为了调试)并返回false
  5. 递归检查子树

    • 最后,代码递归地检查左子树和右子树是否都是AVL树。只有当两个子树都是AVL树时,当前树才是AVL树。

6.完整代码

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

	int _bf;  // balance factor

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

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	// logN
	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) // 1 -1 -> 0
			{
				// 更新结束
				break;
			}
			else if(parent->_bf == 1 || parent->_bf == -1)  // 0 -> 1 -1
			{
				// 继续往上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2) // 1 -1 -> 2 -2
			{
				// 当前子树出问题了,需要旋转平衡一下
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				
				break;
			}
			else
			{
				// 理论而言不可能出现这个情况
				assert(false);
			}
		}


		return true;
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}


	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

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

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

		subL->_right = parent;

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

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

			subL->_parent = ppNode;
		}

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

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

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

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

		parent->_parent = subR;

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

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

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(subR);
		RotateL(parent);

		subRL->_bf = 0;
		if (bf == 1)
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else
		{
			parent->_bf = 0;
			subR->_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);
		}
	}

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

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}

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

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

		return max(_Height(root->_left), _Height(root->_right)) + 1;
	}

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

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		// 不平衡
		if (abs(leftHeight - rightHeight) >= 2)
		{
			cout << root->_kv.first << endl;
			return false;
		}
		
		// 顺便检查一下平衡因子是否正确
		if (rightHeight - leftHeight != root->_bf)
		{
			cout << root->_kv.first << endl;
			return false;
		}

		return _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);
	}
private:
	Node* _root = nullptr;
};


7.数据测试 

void TestAVLTree1()
{
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		t1.Insert({ e,e });

		cout << "Insert:" << e << "->" << t1.IsBalance() << endl;
	}

	t1.InOrder();

	cout << t1.IsBalance() << endl;
}

符合预期:

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

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

相关文章

基于地理坐标的高阶几何编辑工具算法(2)——相交面裁剪

文章目录 工具步骤应用场景算法输入算法输出算法示意图算法原理后处理 工具步骤 选中一个需要裁剪的面&#xff0c;点击“相交面裁剪”工具&#xff0c;多选裁剪模板面&#xff0c;空格执行。 应用场景 常用于基于遥感影像的建筑物几何面编辑。 算法输入 一个待裁剪的面&a…

国家自然博物馆“云端自然”线上虚拟展厅是如何搭建的?

国家级综合性自然博物馆国家自然博物馆&#xff0c;联手积木易搭打造“云端自然”线上虚拟展览&#xff0c;形成一个集参观游览、科普教育为一体的线上虚拟数字博物馆平台&#xff0c;让数千以至数万年的古生物&#xff0c;栩栩如生地呈现在我们面前。 通过数字化的展示手段&am…

C# 文件清理

/// <summary>/// 定期清除文件/// </summary>/// <param name"fileDirect">文件夹</param>/// <param name"postFix">文件后缀</param>/// <param name"saveDay">保存天数</param>private voi…

blender 烘焙渲染图片,已经导出fbx,导出贴图。插件生成图片

1.新建一个模型。选择资产浏览器的材质&#xff0c;并拖动到模型身上&#xff0c;如下图。资产浏览器的材质可以网上找。 2.打开着色器面板。正下方着色器窗口中&#xff0c;点击空白取消选择&#xff0c;然后右击-添加-着色器-原理化BSDF&#xff0c;右击-添加-纹理-图像纹理。…

oracle怎么处理json格式

向数据库导入json相关jar包 loadjava -r -f -u bsuser/XXXX192.168.10.31/bsorcl json.jar 要删除的话&#xff0c;删除指定jar dropjava -u bsuser/XXXX192.168.10.31/bsorcl json.jar select * from user_java_classes 然后我们就可以取到json串中任意节点的值

几个原则

&#xff08;1&#xff09; 成功是成功之母&#xff0c;失败不是成功之母。100分的试卷一模一样&#xff0c;不及格的试卷千奇百怪。向成功者学习。 不要研究失败&#xff0c;因为研究了一万个失败的原因&#xff0c;也不能找到一把成功的钥匙。 &#xff08;2&#xff09; 要定…

Controlnet作者放出新的大招 IC-Light,可以操控图像生成时的光照,对内容主体重新打光生成符合新背景环境光照的图片

IC-Light代表Impose Constant Light,是一个控制图像照明的项目。可以操控图像生成时的光照&#xff0c;对内容主体重新打光生成符合新背景环境光照的图片。这下商品图合成这种需要最大程度保持原有主体 ID 需求的最大的问题解决了。 目前&#xff0c;已经发布了两种类型的模型…

如何将手机中的音乐转移到 SD 卡上?轻松传输音乐

概括 如何将音乐从手机转移到 SD 卡&#xff1f;我们的智能手机可以充当个人点唱机&#xff0c;因此有效管理我们的音乐库变得至关重要。无论您是存储空间不足还是只是想整理您的音乐收藏&#xff0c;将音乐从手机传输到 SD 卡都是一个实用的解决方案。 在本指南中&#xff0…

Python数据可视化(七)

绘制 3D 图形 到目前为止&#xff0c;我们一直在讨论有关 2D 图形的绘制方法和绘制技术。3D 图形也是数据可视化的 一个很重要的应用方面&#xff0c;我们接下来就重点讲解有关 3D 图形的实现方法。绘制 3D 图形通常需要导 入 mpl_toolkits 包中的 mplot3d 包的相关模块&#x…

【博主推荐】HTML5实现520表白、情人节表白模板源码

文章目录 1.设计来源1.1 表白首页1.2 甜蜜瞬间11.3 甜蜜瞬间21.4 甜蜜瞬间31.5 甜蜜瞬间41.6 甜蜜瞬间51.7 甜蜜瞬间61.8 永久珍藏 2.效果和源码2.1 页面动态效果2.2 页面源代码2.3 源码目录2.4 更多为爱表白源码 3.源码下载地址 作者&#xff1a;xcLeigh 文章地址&#xff1a;…

【每日一题】52.20个机器学习问题 2 (模型部署、实践流程和应用问题)

在上一篇《20个机器学习问答题》中&#xff0c;问题主要围绕机器学习的基础概念和理论知识。 这次&#xff0c;本篇内容针对机器学习的实践和应用继续提出了20个不同的问题。【点击跳转原文】 在实际应用中&#xff0c;机器学习模型的建立流程是怎样的&#xff1f; 机器学习模…

C++语言基础光速入门笔记

目录 从C到CC和C语言的关系C编译器C面向对象程序设计标准库ANSI 标准C的使用场景标准化 安装 GNU 的 C/C 编译器g 应用说明g 常用命令选项 C 基本语法C 关键字三字符组 C 数据类型基本的内置类型typedef 声明枚举类型类型转换静态转换&#xff08;Static Cast&#xff09;动态转…

Linux--09---RPM 、YUM

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 RPM1 什么是RPM2 RPM包的名称格式3.RPM查询命令4.RPM卸载命令5.RPM安装命令 YUM1 什么是YUMYUM优势1.自动下载RPM包并且安装2.自动处理依赖性关系&#xff0c;并且一…

【从C++到Java一周速成】章节14:网络编程

章节14&#xff1a;网络编程 【1】网络编程的概念【2】IP地址与端口的概念【3】网络通信协议引入网络通信协议的分层 【3】Socket套接字【4】单向通信【5】双向通信 【1】网络编程的概念 把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大、功能强的网…

记录Python低代码开发框架zdppy_amcrud的开发过程

实现新增接口 基础代码 import env import mcrud import api import snowflakeenv.load(".env") db mcrud.new_env()table "user" columns ["name", "age"]async def add_user(req):data await api.req.get_json(req)values [d…

贪心算法--区间调度问题

贪心算法 引言 贪心算法是一种简单而有效的算法设计技巧&#xff0c;在解决一些优化问题时具有广泛的应用。其基本思想是通过每一步的局部最优选择&#xff0c;最终达到全局最优解。贪心算法通常不会回溯之前的决策&#xff0c;而是根据当前状态作出最优决策&#xff0c;因此…

数据库的约束 not null, unique, default, primary key, foreign key, check

约束可以理解成 数据库提供的一种针对数据的合法性进行验证的机制, 在创建表的时候使用 1. 约束类型 NOT NULL - 指示某列不能存储 NULL 值, 表里的这个内容是必填项UNIQUE - 保证某列的每行必须有唯一的值, 不能重复 每次插入/修改时, 都要先触发查询, 如果当前插入/修改的…

Leetcode刷题2

文章目录 前言寻找两个正序数组的中位数1️⃣ 双指针快速排序2️⃣ 第k小数解法 Z 字形变换1️⃣ 个人解法2️⃣巧妙解法13️⃣巧妙解法2 字符串转换整数 (atoi)1️⃣ 常规方法2️⃣ 作弊方法&#x1f62b; 整数转罗马数字1️⃣ 常规方法&#xff1a;按照给定规则写出判断条件即…

Python使用thread模块实现多线程

介绍&#xff1a; 线程&#xff08;Threads&#xff09;是操作系统提供的一种轻量级的执行单元&#xff0c;可以在一个进程内并发执行多个任务。每个线程都有自己的执行上下文&#xff0c;包括栈、寄存器和程序计数器。 在Python中&#xff0c;可以使用threading模块创建和管理…

Mybatis源码剖析---第一讲

Mybatis源码剖析 基础环境搭建 JDK8 Maven3.6.3&#xff08;别的版本也可以…&#xff09; MySQL 8.0.28 --> MySQL 8 Mybatis 3.4.6 准备jar&#xff0c;准备数据库数据 把依赖导入pom.xml中 <properties><project.build.sourceEncoding>UTF-8</p…