【C++】AVL树详解

news2025/2/23 4:13:43

目录

一、AVL树的概念

二、AVL树节点的定义

三、AVL树的操作

3.1 AVL树的平衡因子

3.2 AVL树的插入

3.3 AVL树的旋转

3.4 AVL树的验证

 四、AVL树的完整代码


上一篇已经对关联式容器set/map/multiset/multimap进行了简答的介绍,大家可能发现它们有一个共同点:其底层都是按照二叉搜索树来实现的,但是学习二叉搜索树时,已经知道当树中插入的元素有序或接近有序时,二叉搜索树的会变得极不平衡,查找操作的时间复杂度可能达到 O(n),甚至退化成链表。因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

一、AVL树的概念

AVL树是一种自平衡二叉搜索树,它得名于它的发明者 Adelson-Velsky 和 Landis。AVL树通过在每次插入或删除节点时进行旋转操作来保持树的平衡,以确保每个结点的左右子树高度之差的绝对值不超过1,降低树的高度,从而实现较高效率的查找、插入和删除操作。

AVL树的性质:

  • 它的左右子树都是AVL树
  • 每个节点的左子树和右子树的高度差(平衡因子)不超过1。
  • 如果插入或删除操作导致树失去平衡,AVL树会通过旋转操作(包括单旋转和双旋转)来重新平衡。
  • 若一个AVL树有n个节点,它的查找、插入和删除操作的时间复杂度都是 O(log_2 n),高度可保持在 O(log_2 n)。

二、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;//平衡因子balance factor

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

三、AVL树的操作

3.1 AVL树的平衡因子

AVL 树的平衡因子是指每个节点的左子树高度减去右子树高度的值,即左子树高度 - 右子树高度。对于任意一个节点,其平衡因子可以是 -1、0 或 1。

AVL 树的平衡因子定义如下:

  • 如果一个节点的平衡因子为 -1,表示该节点的左子树比右子树高度高 1;
  • 如果一个节点的平衡因子为 0,表示该节点的左子树和右子树高度相等;
  • 如果一个节点的平衡因子为 1,表示该节点的右子树比左子树高度高 1。

AVL 树通过保持每个节点的平衡因子为 -1、0 或 1 来确保树的平衡。当插入或删除节点后,需要通过旋转操作来调整各个节点的平衡因子,以确保整棵树仍保持平衡状态。

3.2 AVL树的插入

在 AVL 树中插入元素的过程如下:

  1. 按照二叉搜索树的规则,找到新元素应该插入的位置,将其作为叶子节点插入到树中
  2. 在插入新元素后,从插入点开始向上回溯,更新每个祖先节点的平衡因子,并检查它们是否失去了平衡。
  3. 如果发现某个节点失去了平衡,则需要对其进行旋转操作,以恢复整棵树的平衡。

步骤一:按照二叉搜索树的规则插入新元素

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv)
	{
		Node* pnode = new Node(kv);

		if (_root == nullptr)
		{
			_root = pnode;
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			if (cur->_kv.first < pnode->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > pnode->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		if (parent->_kv.first < pnode->_kv.first)
		{
			parent->_right = pnode;
			pnode->_parent = parent;
		}
		else
		{
			parent->_left = pnode;
			pnode->_parent = parent;
		}

		//开始处理平衡因子
        //...

private:
	Node* _root = nullptr;
};

步骤二:更新每个祖先节点的平衡因子,检查它们是否失去了平衡。

在插入元素之前,parent的平衡因子分为三种情况:0,1,-1,插入后分为以下两种情况:

  • 如果cur插入到parent的左侧,只需给parent的平衡因子-1即可
  • 如果cur插入到parent的右侧,只需给parent的平衡因子+1即可
bool Insert(const pair<K, V>& kv)
	{
        //.....
         
        //开始处理平衡因子
		cur = pnode;
		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)
			{
			    //失去了平衡,需要进行旋转
                //旋转操作。。。
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

插入后,parent节点的平衡因子发生变化,有这么三种情况:0,正负1, 正负2。

  • 情况1:parent的平衡因子变为0(parent的平衡因子一定是由1或-1变成0)

9的平衡因子由-1变成0,但是以9为根节点的这棵树整体高度不变,所以不会再对9的祖先节点造成影响,所以不需要再向上更新平衡因子,也不需要进行旋转的操作。
同理,由1变为0也是一样

操作:parent的平衡因子变为0时,不需要再向上更新平衡因子。

  • 情况2:parent的平衡因子变为 -1/1(parent的平衡因子一定是0由变成1或-1)

插入1后,1的父节点3的平衡因子变成-1,3这个节点的平衡因子是正常的,但是3的平衡因子从0变成-1时,它的左边变高了,3的父节点9的平衡因子变成-2,发生错误,我们就需要对9进行调整同理,如果变成1也是一样(例如插入18)。
所以插入新节点后,新节点的父节点(parent)的平衡因子如果变成了-1或1,我们就需要沿着这个父节点到根的路径依次更新父节点的祖先节点的平衡因子,如果某个祖先节点平衡因子更新后变成2或者-2(绝对值大于1),就立即进行旋转调整.

  • 情况3:parent的平衡因子变为 -2/2,此时违反平衡树的性质,需要对其进行旋转处理。

步骤三:如果发现某个节点失去了平衡,则需要对其进行旋转操作。

3.3 AVL树的旋转

根据节点插入位置的不同,AVL树的旋转分为四种:

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

右单旋步骤如下:

  1. 设key为12的节点为parent,parent的左节点为cur(key为6),cur的右节点为curR
  2. 将curR链接在parent的左边,parent链接在cur的右边。
  3. 调整各节点的父节点
  4. 将parent和cur的平衡因子置0。

注:

  • 使用右单旋的情况为:parent->_bf == -2 && cur->_bf == -1
  • 图中的a,b,c 均为抽象节点,h 可取值0、1、2...,虽然不同大小的h对应的情况很多,在此也不一一赘叙、但经过总结,只要满足新节点插入较高左子树的左侧,就可以使用右旋。
    下面是h=0 和h = 1的右单旋示例:(这里将cur命名为subL,curR命名为subLR)
  • 需要注意的是,此处的parent 可能是根节点,也可能是子树。如果是子树,可能是左子树也可能是右子树,这时就需要先备份一份parent的parent(下面命名为parentParent),将旋转后的根节点的_parent和parentParent指向parent的指针修改。
  • cur节点的右孩子可能存在,也可能不存在。当h等于0时,cur就没有右孩子。

右单旋的实现如下:

//右单旋
	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curR = cur->_right;

        //因为parent可能是棵子树,因此在更新其双亲前必须先保存parent的双亲
		Node* parentParent = parent->_parent;

		parent->_left = curR;
		parent->_parent = cur;

		cur->_right = parent;
		cur->_parent = parentParent;
        
        //旋转完成之后,cur的右孩子作为parent的左孩子
        //如果cur的右孩子存在,更新亲双亲
		if (curR)
		{
			curR->_parent = parent;
		}

        // 如果parent是根节点,_root指向新的根节点
		if (_root == parent)
		{
			_root = cur;
		}
		else //否则就要判断原来的parent是父节点的左子树还是右子树
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = cur;
			}
			else
			{
				parentParent->_right = cur;
			}
		}

        // 根据调整后的结构更新部分节点的平衡因子
		parent->_bf = cur->_bf = 0;
	}

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

使用左单旋的情况为:parent->_bf == 2 && cur->_bf == 1

左单旋步骤如下:

  1. 将curL链接在parent的右边
  2. 将parent链接在cur的左边
  3. 调整各节点的父节点
  4. 将cur和parent的平衡因子调整为0

 左单旋的抽象节点分析与右单旋类似,所以在此不再花费篇幅进行讲解

左单旋的实现如下:

//左单旋
	void RotateL(Node* parent)
	{
		Node* parentParent = parent->_parent;
		Node* cur = parent->_right;
		Node* curL = cur->_left;

		parent->_right = cur->_left;
		parent->_parent = cur;

		cur->_parent = parentParent;
		cur->_left = parent;

		if (curL)
		{
			curL->_parent = parent;
		}
		if (_root == parent)
		{
			_root = cur;
			//cur->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = cur;
			}
			else
			{
				parentParent->_right = cur;
			}
		}
		parent->_bf = cur->_bf = 0;
	}

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

使用左右双旋的情况为:parent->_bf == -2 && cur->_bf == 1

旋转步骤:

  1. 对cur进行左单旋(cur为parent的左节点)
  2. 对parent进行右单旋
  3. 调整平衡因子

对抽象节点进行分析:

  • 当 h=0 时,curR就是新增(curR->_bf = 0),旋转后curR = 0,cur = 0,parent = 0
  • 当 h=1 时,新增节点可能是curR的左节点,也可能是右节点。
    若为左节点,curR->_bf = -1,cur->_bf = 1,parent->_bf = -2。
    旋转后:curR->_bf = 0,cur->_bf= 0,parent->_bf = 1
    若为右节点,curR->_bf = 1,cur->_bf = 1,parent->_bf = -2。
    旋转后:curR->_bf = 0,cur->_bf = -1,parent->_bf = 0
  • 当 h=2、3、4...,结果与h=1相同

综上,平衡因子调整规则为:

  • 当curR->_bf = 0时:调整为curR->_bf = 0,cur->_bf = 0,parent->_bf = 0
  • 当curR->_bf = -1时:调整为curR->_bf = 0,cur->_bf= 0,parent->_bf = 1
  • 当curR->_bf = 1时:调整为curR->_bf = 0,cur->_bf = -1,parent->_bf = 0

注:

  • 旋转之前,保存curR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子。

左右双旋实现如下:

//先左再右
	void RotateLR(Node* parent)
	{
		//对parent的left左旋,再对parent右旋
		Node* cur = parent->_left;
		Node* curR = cur->_right;
		int bf = curR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == 0)//curR是新增的
		{
			parent->_bf = cur->_bf = curR->_bf = 0;
		}
		else if (bf == -1)//curR的left是新增的
		{
			parent->_bf = 1;
			cur->_bf = 0;
			curR->_bf = 0;
		}
		else if (bf == 1)//curR的right是新增的
		{
			parent->_bf = 0;
			cur->_bf = -1;
			curR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

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

使用右左双旋的情况为:parent->_bf == 2 && cur->_bf == -1

旋转步骤:

  1. 对subR进行右单旋
  2. 对parent进行左单旋
  3. 调整平衡因子

 右左双旋对抽象节点进行分析与左右双旋类似,大家可自行尝试分析。

平衡因子调整规则为:

  • 当curR->_bf = 0时:调整为curR->_bf = 0,cur->_bf = 0,parent->_bf = 0
  • 当curR->_bf = -1时:调整为curR->_bf = 0,cur->_bf= 1,parent->_bf = 0
  • 当curR->_bf = 1时:调整为curR->_bf = 0,cur->_bf = 0,parent->_bf = -1
//先右再左
	void RotateRL(Node* parent)
	{
		//对parent的right右旋,再对parent左旋
		Node* cur = parent->_right;
		Node* curL = cur->_left;
		int bf = curL->_bf;

		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 0)//curL是新增的
		{
			parent->_bf = cur->_bf = curL->_bf = 0;
		}
		else if (bf == -1)//curL的left是新增的
		{
			parent->_bf = 0;
			cur->_bf = 1;
			curL->_bf = 0;
		}
		else if (bf == 1)//curL的right是新增的
		{
			parent->_bf = -1;
			cur->_bf = 0;
			curL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

3.4 AVL树的验证

如果要验证一个二叉搜索树是否为AVL 树,则需要检查每个节点的平衡因子是否符合 AVL 树的定义,即平衡因子为 -1、0 或 1,并且整棵树中的每个节点都满足 AVL 树的平衡性质。

验证 AVL 树的一般步骤如下

  1. 对树中的每个节点,计算其左子树的高度和右子树的高度;
  2. 计算每个节点的平衡因子(左子树高度 - 右子树高度);
  3. 检查每个节点的平衡因子是否为 -1、0 或 1,如果不是,则说明该节点不满足 AVL 树的平衡性质;
  4. 递归地对树中的每个节点进行上述检查,确保整棵树都符合 AVL 树的定义。

验证的实现:

	//判断是否平衡
    bool IsBalance()
	{
		return _IsBalance(_root);
	}

    //求树的高度
    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;
	}

	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) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

 四、AVL树的完整代码

#pragma once
#include <assert.h>
#include<iostream>
using namespace std;

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:
	bool Insert(const pair<K, V>& kv)
	{
		Node* pnode = new Node(kv);

		if (_root == nullptr)
		{
			_root = pnode;
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			if (cur->_kv.first < pnode->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > pnode->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		if (parent->_kv.first < pnode->_kv.first)
		{
			parent->_right = pnode;
			pnode->_parent = parent;
		}
		else
		{
			parent->_left = pnode;
			pnode->_parent = parent;
		}

		//开始处理平衡因子
		cur = pnode;
		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)
				{
					//先右旋再左旋
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					//先左旋再右旋
					RotateLR(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

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



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

	//左旋
	void RotateL(Node* parent)
	{
		Node* parentParent = parent->_parent;
		Node* cur = parent->_right;
		Node* curL = cur->_left;

		parent->_right = cur->_left;
		parent->_parent = cur;

		cur->_parent = parentParent;
		cur->_left = parent;

		if (curL)
		{
			curL->_parent = parent;
		}
		if (_root == parent)
		{
			_root = cur;
			//cur->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = cur;
			}
			else
			{
				parentParent->_right = cur;
			}
		}
		parent->_bf = cur->_bf = 0;
	}

	//右旋
	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curR = cur->_right;
		Node* parentParent = parent->_parent;

		parent->_left = curR;
		parent->_parent = cur;

		cur->_right = parent;
		cur->_parent = parentParent;

		if (curR)
		{
			curR->_parent = parent;
		}

		if (_root == parent)
		{
			_root = cur;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = cur;
			}
			else
			{
				parentParent->_right = cur;
			}
		}
		parent->_bf = cur->_bf = 0;
	}
	//先右再左
	void RotateRL(Node* parent)
	{
		//对parent的right右旋,再对parent左旋
		Node* cur = parent->_right;
		Node* curL = cur->_left;
		int bf = curL->_bf;

		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 0)//curL是新增的
		{
			parent->_bf = cur->_bf = curL->_bf = 0;
		}
		else if (bf == -1)//curL的left是新增的
		{
			parent->_bf = 0;
			cur->_bf = 1;
			curL->_bf = 0;
		}
		else if (bf == 1)//curL的right是新增的
		{
			parent->_bf = -1;
			cur->_bf = 0;
			curL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	//先左再右
	void RotateLR(Node* parent)
	{
		//对parent的left左旋,再对parent右旋
		Node* cur = parent->_left;
		Node* curR = cur->_right;
		int bf = curR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == 0)//curR是新增的
		{
			parent->_bf = cur->_bf = curR->_bf = 0;
		}
		else if (bf == -1)//curR的left是新增的
		{
			parent->_bf = 1;
			cur->_bf = 0;
			curR->_bf = 0;
		}
		else if (bf == 1)//curR的right是新增的
		{
			parent->_bf = 0;
			cur->_bf = -1;
			curR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

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

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}

	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) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

private:
	Node* _root = nullptr;
};

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

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

相关文章

Tomcat服务部署

1、安装jdk、设置环境变量并测试 第一步&#xff1a;安装jdk 在部署 Tomcat 之前必须安装好 jdk&#xff0c;因为 jdk 是 Tomcat 运行的必要环境。 1. #关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 02. #将安装 Tomcat 所需软件包传到/opt…

在Windows中安装PyTorch

文章目录 1. 创建虚拟环境2. 检查显卡版本和CUDA3. 下载链接4. 下载5. 等待6. 检测 1. 创建虚拟环境 具体查看我之前写的 《在Windows中利用Python的venv和virtualenv创建虚拟环境》 2. 检查显卡版本和CUDA 这种情况是需要电脑上有单独的英伟达的显卡、或者英伟达的显卡和集显…

Retrofit核心原理

Retrofit是一个类型安全的HTTP客户端库&#xff0c;广泛用于Android和Java应用中&#xff0c;用于简化网络请求和响应的处理。本文将深入探讨Retrofit的核心原理&#xff0c;帮助开发者理解其背后的工作机制。 Retrofit简介 Retrofit是Square公司开发的一个开源库&#xff0c…

keepalived+HAProxy+MySQL双主实验

keepalivedHAProxyMySQL双主实验 环境准备 node1(HAProxy1):192.168.184.10 node2(HAProxy2):192.168.184.20 node3(MySQL1):192.168.184.30 node4(MySQL2):192.168.184.40 虚拟IP vip&#xff1a;192.168.184.100MySQL部署 在node3执行以下脚本&#xff1a; #!/bin/bash sy…

Linpmem:一款功能强大的Linux物理内存提取工具

关于Linpmem Linpmem是一款功能强大的Linux物理内存提取工具&#xff0c;该工具专为x64 Linux设计&#xff0c;可以帮助广大研究人员在执行安全分析过程中快速读取Linux物理内存数据。 该工具类似Windows下的Winpmem&#xff0c;Linpmem不是一个传统的内存转储工具&#xff0…

Leetcode—63. 不同路径 II【中等】

2024每日刷题&#xff08;115&#xff09; Leetcode—63. 不同路径 II 动态规划算法思想 实现代码 class Solution { public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m obstacleGrid.size();int n obstacleGrid[0].size();…

【python】Python Turtle绘制流星雨动画效果【附源码】

在这篇技术博客中&#xff0c;我们将学习如何使用 Python 的 Turtle 模块绘制一个流星雨的动画效果。通过简单的代码实现&#xff0c;我们可以在画布上展现出流星闪耀的场景&#xff0c;为视觉带来一丝神秘与美感。 一、效果图&#xff1a; 二、准备工作 &#xff08;1)、导入…

Stable Diffusion中的Clip模型

基础介绍 Stable Diffusion 是一个文本到图像的生成模型&#xff0c;它能够根据用户输入的文本提示&#xff08;prompt&#xff09;生成相应的图像。在这个模型中&#xff0c;CLIP&#xff08;Contrastive Language-Image Pre-training&#xff09;模型扮演了一个关键的角色&a…

关键字:private关键字作用,解析及用法

private关键字在 Java 中用于定义类的成员&#xff08;如变量、方法&#xff09;的访问权限。它表示该成员只能在类的内部被访问和修改&#xff0c;而在类的外部是不可见的。 以下是private关键字的主要作用和解析&#xff1a; 作用&#xff1a; 封装性&#xff1a;通过将类的…

【常用】【测速】ptflops库---速度FPS、参数Params、计算复杂度Flops

一、常用名字 中文名字 英文名字 简称 单位 模型参数量 number of parameters. param. (单位B M) 计算复杂度 computational…

《TCP/IP详解 卷一》第11章 DNS

目录 11.1 引言 11.2 DNS名称空间 11.2.1 DNS命名语法 11.3 名称服务器和区域 11.4 DNS缓存 11.5 DNS 协议 11.5.1 DNS消息格式 11.5.2 DNS 扩展格式&#xff08;EDNS0&#xff09; 11.5.3 UDP 或 TCP 11.5.4 问题&#xff08;查询&#xff09;和区域区段格式 11.5.…

VR元宇宙的概念|VR体验店加盟|虚拟现实设备销售

VR元宇宙是一个结合了虚拟现实&#xff08;Virtual Reality&#xff09;和增强现实&#xff08;Augmented Reality&#xff09;等技术的概念&#xff0c;代表着一个虚拟的多维度世界。它是一个由数字化的空间构成的虚拟环境&#xff0c;可以通过虚拟现实设备进行交互和探索。 元…

常用网络协议的学习

TCP/IP TCP/IP的定义 TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff0c;传输控制协议/互联网协议&#xff09;是互联网的基本协议&#xff0c;也是国际互联网络的基础。 TCP/IP 不是指一个协议&#xff0c;也不是 TCP 和 IP 这两个协议的合称…

逻辑电路集成块手册

还在查找74XX集成块的数据手册吗,还在找逻辑门电路的手册吗 不用找了,直接打开此电子书,查找就可以了,内部框图,真值表引脚序号都有DOWNLOAD:https://www.ti.com/lit/pdf/scyd013?keyMatchLOGIC%20POCKET%20DATA%20BOOK 失效直接上TI官方网站搜索logic pocket data book即可搜…

统计业务流量的毫秒级峰值 - 华为机试真题题解

考试平台&#xff1a; 时习知 分值&#xff1a; 200分&#xff08;第二题&#xff09; 考试时间&#xff1a; 两小时&#xff08;共3题&#xff09; 题目描述 业务模块往外发送报文时&#xff0c;有时会出现网卡队列满而丢包问题&#xff0c;但从常规的秒级流量统计结果看&…

基础内容哦!!!吴恩达deeplearning.ai:利用计算图求导(反向传播)

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 文章目录 一个小型神经网络的例子利用计算图逐步计算价值函数J利用计算图求出价值函数的导数 计算图是深度学习中的一个关键概念&#xff0c;它也是Tensorflow等编程框架自动计算神经网络导…

小而巧的数字压缩算法:zigzag

阅读facebook开源的 RPC&#xff08;Remote Procedure Call&#xff09; 框架thrift源代码的时候&#xff0c;本来是在阅读框架&#xff0c;却不小心被 zigzag 这个钻石般闪耀的代码吸引。后来去百度搜索zigzag&#xff0c;却得到满屏图像相关的一个算法&#xff08;看来起名字…

李沐动手学习深度学习——3.1练习

字写的有点丑不要介意 由于公式推导烦的要死&#xff0c;所以手写形式&#xff0c;欢迎进行讨论&#xff0c;因为我也不知道对错

HarmonyOS—端云一体化组件

概述 DevEco Studio还为您提供多种端云一体化组件。集成端云一体化组件后&#xff0c;您只需进行简单配置即可向应用用户提供登录、支付等众多功能。 登录组件 您可使用端云一体化登录组件向应用用户提供登录和登出功能&#xff0c;目前支持帐号密码登录、手机验证码登录、以…

在VMware中安装CentOS 7并配置Docker

VMware安装CentOS 7 一、介绍 该文章介绍如何使用启动U盘在虚拟机里面安装系统&#xff0c;虚拟机版本为VMware Workstation 16 pro&#xff0c;Linux版本为CentOS Linux release 7.9.2009 (Core)。 二、安装 1、创建虚拟机 点击创建新的虚拟机 选择典型就可以了&#xf…