C++AVL树详解

news2024/12/23 13:49:28

什么是AVL树

AVL树是最先发明的⾃平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的
左右⼦树都是AV树,且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树,
通过控制⾼度差去控制平衡。
简单的来说AVL树就是可以控制自身高度差的搜索二叉树

AVL树通过什么来控制平衡

AVL树实现平衡这⾥我们引⼊⼀个平衡因⼦(balance factor)的概念,每个结点都有⼀个平衡因⼦,任何
结点的平衡因⼦等于右⼦树的⾼度减去左⼦树的⾼度,也就是说任何结点的平衡因⼦等于0/1/-1,
AVL树并不是必须要平衡因⼦,但是有了平衡因⼦可以更⽅便我们去进⾏观察和控制树是否平衡,
就像⼀个指向标⼀样。通过平衡因子我们可以更加直观的去判断AVL树到底是不是平衡的

为什么要求⾼度差不超过1,⽽不是⾼度差是0呢0不是更好的平衡吗?画画图分析我们发现,不是不想这样设计,⽽是有些情况是做不到⾼度差是0的。⽐如⼀棵树是2个结点,4个结点等情况下,⾼度差最好就是1,⽆法作为⾼度差是0
我们可以来画图看看
在这里插入图片描述

AVL树整体结点数量和分布和完全⼆叉树类似,⾼度可以控制在 ,那么增删查改的效率也可
以控制在 ,相⽐⼆叉搜索树有了本质的提升。避免了像搜索二叉树出现的一些极端情况,比如说一直插入一个比根节点小的树,就一直往左边插入,这样搜索效率就会变得很差

AVL树的插入

AVL树大致可以分为一下插入过程
1.插入和搜索二叉树一样,都是通过插入节点和当前节点进行比较小的往左走,大的往右走。

  1. 新增结点以后,只会影响祖先结点的⾼度,也就是可能会影响部分祖先结点的平衡因⼦,所以更新
    从新增结点->根结点路径上的平衡因⼦,实际中最坏情况下要更新到根,有些情况更新到中间就可
    以停⽌了,具体情况我们下⾯再详细分析。

  2. 更新平衡因⼦过程中没有出现问题,则插⼊结束

  3. 更新平衡因⼦过程中出现不平衡,对不平衡⼦树旋转,旋转后本质调平衡的同时,本质降低了⼦树
    的⾼度,不会再影响上⼀层,所以插⼊结束。

平衡因子的更新

更新规则
平衡因子 = 右子树的高度-左子树的高度

只有⼦树⾼度变化才会影响当前结点平衡因⼦

插⼊结点,会增加⾼度,所以新增结点在parent的右⼦树,parent的平衡因⼦++,新增结点在
parent的左⼦树,parent平衡因⼦–

parent所在⼦树的⾼度是否变化决定了是否会继续往上更新
更新结果
这里要分三种情况
如果更新完成之后parent是0则说明更新前子树的高度是-1或者1,说明原来的子树不平衡,一边高一边低,更新后变成0说明新插入的节点插入在低的那一边,插⼊后parent所在的⼦树⾼度不变,不会
影响parent的⽗亲结点的平衡因⼦,更新结束。
在这里插入图片描述

更新后parent的平衡因⼦等于1 或 -1,更新前更新中parent的平衡因⼦变化为0->1 或者 0->-1,说
明更新前parent⼦树两边⼀样⾼,新增的插⼊结点后,parent所在的⼦树⼀边⾼⼀边低,parent所
在的⼦树符合平衡要求,但是⾼度增加了1,会影响parent的⽗亲结点的平衡因⼦,所以要继续向上
更新
在这里插入图片描述

更新后parent的平衡因⼦等于2 或 -2,更新前更新中parent的平衡因⼦变化为1->2 或者 -1->-2,说
明更新前parent⼦树⼀边⾼⼀边低,新增的插⼊结点在⾼的那边,parent所在的⼦树⾼的那边更⾼
了,破坏了平衡,parent所在的⼦树不符合平衡要求,需要旋转处理,旋转的⽬标有两个:1、把
parent⼦树旋转平衡。2、降低parent⼦树的⾼度,恢复到插⼊结点以前的⾼度。所以旋转后也不
需要继续往上更新,插⼊结束。
在这里插入图片描述
这个旋转等下会详细说明,下面来看看AVL树的插入的实现代码

AVL树的插入

在看插入之前,我们先看看AVL树的每个节点的构造

template<class key,class value>
struct avl_node
{
	int _bf;
	pair<key, value>_kv;
	avl_node<key, value>* _left;
	avl_node<key, value>* _right;
	avl_node<key, value>* _pre_node;

	avl_node(const pair<key,value>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_bf(0)
		,_pre_node(nullptr)
	{}
};

这里我们有三个指针,一个指向当前节点的左边,一个指向当前节点的右边,一个指向当前节点的上一个节点(父节点),然后我们用了一个pair键值对来存放key,value的值

template<class key,class value>
class AVLtree
{
	typedef avl_node<key, value> node;
public:
	AVLtree()
		:_root(nullptr)
	{}
	bool insert(const pair<key, value>& kv)
	{
		if (_root == nullptr)
		{
			_root = new node(kv);
			return true;
		}
		node* cur = _root;
		node* parent = nullptr;
		while (cur != nullptr)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_kv.first<kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
	    }
		cur = new node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_pre_node = parent;
		while (parent != nullptr)//回溯跟新
		{
			if (parent->_left == cur)
			{
				parent->_bf--;//插入在左边--
			}
			else
			{
				parent->_bf++;//插入在右边++
			}
			if (parent->_bf == 0)
			{
				break;
			}
			if (parent->_bf == 1 || parent->_bf == -1)//快不平衡了
			{
				cur = parent;
				parent = parent->_pre_node;
			}
			else if (parent->_bf == -2 || parent->_bf == 2)//更新节点
			{
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					rotetR(parent);//这些调整下面会详细说明
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					rotetL(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					rotetRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					rotetLR(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
    }

这里插入逻辑还是和搜索二叉树一样,我们依次比较大小,当找到位置的时候,就需要向上更新平衡因子
好了接下来来说说旋转

AVL树的旋转

旋转的规则

  1. 保持搜索树的规则**(这是很重要的)**

  2. 让旋转的树从不满⾜变平衡,其次降低旋转树的⾼度
    旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋。

rotetR右单旋转

首先我们来画图看看,什么情况需要进行右旋转
这里我们用a,b,c来代表一个抽象的子树,这样的好处是可以一张图片概括所有的右单旋转的所有场景

在这里插入图片描述
下面就需要开始旋转了
在这里插入图片描述
具体步骤说明
在b⼦树中插⼊⼀个新结点,导致b⼦树的⾼度从h变成h+1,不断向上更新平衡因⼦,导致10的平
衡因⼦从-1变成-2,10为根的树左右⾼度差超过1,违反平衡规则。10为根的树左边太⾼了,需要
往右边旋转,控制两棵树的平衡。

旋转核⼼步骤,因为5 < c⼦树的值 < 10,将c变成10的左⼦树,10变成5的右⼦树,5变成这棵树新
的根,符合搜索树的规则,控制了平衡,同时这棵的⾼度恢复到了插⼊之前的h+2(加2是把5和10加进去算的),符合旋转原则。如果插⼊之前10整棵树的⼀个局部⼦树,旋转后不会再影响上⼀层,插⼊结束了。
下面来带入具体的数字来说明一下
情况1插入之前 a/b/c的高度是0
在这里插入图片描述
情况2插入之前a/b/c的高度是1
在这里插入图片描述
在这里插入图片描述
好了下面我们来看看代码是怎么实现的

右旋代码

这里面其实有很多的坑,不要看着简单,实际上要注意的点有点多,首先我们需要记录三个节点第一个是subl节点(以不平衡的节点为主的左边节点),然后sublr节点也就是subl的右边节点,pparent也就是(以不平衡节点为主的上一个节点)
其实不难发现旋转时要动的节点就那三个

void rotetR(node* parent)
{
	node* subl = parent->_left;
	node* sublr = subl->_right;
	node* Pparent = parent->_pre_node;
	首先我们让parent的左边指向sublr
	parent->_left = sublr;
	为什么这里需要判断一下sublr是不是为空呢,这是因为有可能出现一种情况一直插入的
	比根小,一直往左边插入,这时候就会形成一条像”/“形状的二叉树,这时sublr是空的,所以需要判断一下
	if (sublr != nullptr)
	{
	不为空就sublr的上一个节点就是parent
		sublr->_pre_node = parent;
	}
	旋转完成之后subl就是新的根了,需要把右边->panrent
	subl->_right = parent;
	parent->_pre_node = subl;同理,向上更新
    到这里就旋转完成了
    下面就需要判断旋转的点到底是不是root节点
    
	if (parent == _root)
	{
		_root = subl;
		subl->_pre_node = nullptr;如果是就把上一个节点置为空
	}
	else
	{
	这里就体现了我们pparent的用法了
		if (Pparent->_left == parent)
		{
			Pparent->_left = subl;
		}
		else
		{
			Pparent->_right = subl;
		}
		subl->_pre_node = Pparent;
	}
	更新节点完成之后更新相应改变的平衡因子
	subl->_bf = 0;
	parent->_bf = 0;
}

这就是右旋

左旋代码

同理右旋

void rotetL(node* parent)
{
	node* subr = parent->_right;
	node* subrl = subr->_left;
	node* pparent = parent->_pre_node;

	parent->_right = subrl;
	if (subrl != nullptr)
	{
		subrl->_pre_node = parent;
	}
	subr->_left = parent;
	parent->_pre_node = subr;
	if (parent == _root)
	{
		_root = subr;
		subr->_pre_node = nullptr;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subr;
		}
		else
		{
			pparent->_right = subr;
		}
		subr->_pre_node = pparent;
	}
	subr->_bf = 0;
	parent->_bf = 0;
}

左右双旋

通过下面的图可以看到,左边⾼时,如果插⼊位置不是在a⼦树,⽽是插⼊在b⼦树,b⼦树⾼度从h变
成h+1,引发旋转,右单旋⽆法解决问题,右单旋后,我们的树依旧不平衡。右单旋解决的纯粹的左边
⾼,但是插⼊在b⼦树中,10为跟的⼦树不再是单纯的左边⾼,对于10是左边⾼,但是对于5是右边
⾼,需要⽤两次旋转才能解决,以5为旋转点进⾏⼀个左单旋,以10为旋转点进⾏⼀个右单旋,这棵树
这棵树就平衡了。
下面是a/b/c高度是0的情况
在这里插入图片描述
我们来看看旋转的过程
在这里插入图片描述
这样这颗树就平衡了
现在我们来看看a/b/c高度是1的情况
在这里插入图片描述
有了以上详细的例子我们来看看抽象的把a/b/c抽象成一个子树

下⾯我们将a/b/c⼦树抽象为⾼度h的AVL⼦树进⾏分析,另外我们需要把b⼦树的细节进⼀步展开为8和左⼦树⾼度为h-1的e和f⼦树,因为我们要对b的⽗亲5为旋转点进⾏左单旋,左单旋需要动b树中的左⼦树。b⼦树中新增结点的位置不同,平衡因⼦更新的细节也不同,通过观察8的平衡因⼦不同,这⾥我们要分三个场景讨论。

• 场景1:h >= 1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1变为h并不断更新8->5->10平衡因⼦,
引发旋转,其中8的平衡因⼦为-1,旋转后8和5平衡因⼦为0,10平衡因⼦为1。

• 场景2:h >= 1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新8->5->10平衡因⼦,引
发旋转,其中8的平衡因⼦为1,旋转后8和10平衡因⼦为0,5平衡因⼦为-1。

• 场景3:h == 0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新5->10平衡因⼦,引发旋
转,其中8的平衡因⼦为0,旋转后8和10和5平衡因⼦均为0。
像下面这样
在这里插入图片描述
首先我们讨论情况1
在这里插入图片描述
在这里插入图片描述
旋转完成之后,我们发现10的平衡因子是1(左边低右边高)
再来看看情况2
在这里插入图片描述
在这里插入图片描述
这时5的平衡因子是-1(右边低左边高)
最后一种情况类似于上面这种,就不做过多解释了
在这里插入图片描述

下面来看看代码

void rotetLR(node* parent)
{
	node* subl = parent->_left;
	node* sublr = subl->_right;
	int bf = sublr->_bf;
	rotetL(parent->_left);
	rotetR(parent);
	if (bf == 0)//对应第三种
	{
		parent->_bf = 0;
		subl->_bf = 0;
		sublr->_bf = 0;
	}
	else if (bf == 1)//对应第一种
	{
		subl->_bf = -1;
		parent->_bf = 0;
		sublr->_bf = 0;
	}
	else if (bf == -1)//对应第二种
	{
		parent->_bf = 1;
		subl->_bf = 0;
		sublr->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

右左双旋

逻辑和上面一样

void rotetRL(node* parent)
{
	node* subr = parent->_right;
	node* subrl = subr->_left;
	int bf = subrl->_bf;
	rotetR(parent->_right);
	rotetL(parent);
	if (bf == 1)
	{
		subr->_bf = 0;
		parent->_bf = -1;
		subrl->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 0;
		subrl->_bf = 0;
		subr->_bf = 1;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		subrl->_bf = 0;
		subr->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

AVL树的中序遍历

和普通搜索二叉树一样

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

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

怎么判断是不是AVL树

这里可以采用递归的方法来判断
先从根节点去递归检查高度,用高度差和平衡因子来检查,以此类推

int heigh(node* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	int lef = heigh(root->_left);
	int rig = heigh(root->_right);
	return lef > rig ? lef + 1 : rig + 1;
}
bool cheak_AVL(node* root)
{
	if (root == nullptr)
	{
		return true;
	}
	int lef = heigh(root->_left);
	int rig = heigh(root->_right);
	int diff =rig-lef;
	if (abs(diff) >= 2)
	{
		printf("高度异常");
		return false;
	}
	 if (root->_bf != diff)
	{
		printf("平衡因子异常");
		return false;
	}
	return cheak_AVL(root->_left) && cheak_AVL(root->_right);
}

源码

#pragma once
#include <utility>
#include<assert.h>
# include<iostream>
using namespace std;
template<class key,class value>
struct avl_node
{
	int _bf;
	pair<key, value>_kv;
	avl_node<key, value>* _left;
	avl_node<key, value>* _right;
	avl_node<key, value>* _pre_node;

	avl_node(const pair<key,value>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_bf(0)
		,_pre_node(nullptr)
	{}
};
template<class key,class value>
class AVLtree
{
	typedef avl_node<key, value> node;
public:
	AVLtree()
		:_root(nullptr)
	{}
	bool insert(const pair<key, value>& kv)
	{
		if (_root == nullptr)
		{
			_root = new node(kv);
			return true;
		}
		node* cur = _root;
		node* parent = nullptr;
		while (cur != nullptr)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_kv.first<kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
	    }
		cur = new node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_pre_node = parent;
		while (parent != nullptr)//回溯
		{
			if (parent->_left == cur)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			if (parent->_bf == 0)
			{
				break;
			}
			if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_pre_node;
			}
			else if (parent->_bf == -2 || parent->_bf == 2)//更新节点
			{
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					rotetR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					rotetL(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					rotetRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					rotetLR(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
    }
	void rotetR(node* parent)
	{
		node* subl = parent->_left;
		node* sublr = subl->_right;
		node* Pparent = parent->_pre_node;
		parent->_left = sublr;
		if (sublr != nullptr)
		{
			sublr->_pre_node = parent;
		}
		subl->_right = parent;
		parent->_pre_node = subl;
		if (parent == _root)
		{
			_root = subl;
			subl->_pre_node = nullptr;
		}
		else
		{
			if (Pparent->_left == parent)
			{
				Pparent->_left = subl;
			}
			else
			{
				Pparent->_right = subl;
			}
			subl->_pre_node = Pparent;
		}
		subl->_bf = 0;
		parent->_bf = 0;
	}

	void rotetL(node* parent)
	{
		node* subr = parent->_right;
		node* subrl = subr->_left;
		node* pparent = parent->_pre_node;

		parent->_right = subrl;
		if (subrl != nullptr)
		{
			subrl->_pre_node = parent;
		}
		subr->_left = parent;
		parent->_pre_node = subr;
		if (parent == _root)
		{
			_root = subr;
			subr->_pre_node = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subr;
			}
			else
			{
				pparent->_right = subr;
			}
			subr->_pre_node = pparent;
		}
		subr->_bf = 0;
		parent->_bf = 0;

	}
	void rotetLR(node* parent)
	{
		node* subl = parent->_left;
		node* sublr = subl->_right;
		int bf = sublr->_bf;
		rotetL(parent->_left);
		rotetR(parent);
		if (bf == 0)
		{
			parent->_bf = 0;
			subl->_bf = 0;
			sublr->_bf = 0;
		}
		else if (bf == 1)
		{
			subl->_bf = -1;
			parent->_bf = 0;
			sublr->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subl->_bf = 0;
			sublr->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
	void rotetRL(node* parent)
	{
		node* subr = parent->_right;
		node* subrl = subr->_left;
		int bf = subrl->_bf;
		rotetR(parent->_right);
		rotetL(parent);
		if (bf == 1)
		{
			subr->_bf = 0;
			parent->_bf = -1;
			subrl->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subrl->_bf = 0;
			subr->_bf = 1;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subrl->_bf = 0;
			subr->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
	void inoder()
	{
		_InOrder(_root);
	}
	bool ck()
	{
		return cheak_AVL(_root);
	}
private:
	void _InOrder(node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}
	int heigh(node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int lef = heigh(root->_left);
		int rig = heigh(root->_right);
		return lef > rig ? lef + 1 : rig + 1;
	}
	bool cheak_AVL(node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int lef = heigh(root->_left);
		int rig = heigh(root->_right);
		int diff =rig-lef;
		if (abs(diff) >= 2)
		{
			printf("高度异常");
			return false;
		}
		 if (root->_bf != diff)
		{
			printf("平衡因子异常");
			return false;
		}
		return cheak_AVL(root->_left) && cheak_AVL(root->_right);
	}
	node* _root;
};

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

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

相关文章

python的介绍以及基本操作

python的介绍 &#xff08;1&#xff09;python是一门编程语言&#xff08;比如&#xff1a;java、c、c、.net、go等都是编程语言&#xff09; python 也是胶水语言 &#xff08;2&#xff09;python是一门面向对象&#xff0c;解释型的动态类型的编程语言&#xff0c; a、什…

select、epoll相关

select函数&#xff1a; int select(int nfds, // 监控的文件描述符集里最大文件描述符加1fd_set *readfds, // 监控有读数据到达文件描述符集合&#xff0c;引用类型的参数fd_set *writefds, // 监控写数据到达文件描述符集合&…

【零散技术】一分钟完成Odoo悬挂网站备案号

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 目录 1.激活开发者模式 2.修改视图 Odoo套上域名是常见的需求&#xff0c;当我们兴致勃勃的做好 域名申请&#xff0c;网站备案&#xff0c;域名解析&#xff0c;SSL证书申请&#xff0c;Nginx转发后&#xff0c;就可以通过域…

横向移动与痕迹清理

目录 横向移动漏洞利⽤服务利⽤IPC横向计划任务横向计划任务横向WMI横向SMB横向DCOM横向WinRM横向PSEXEC横向其他⽅式横向 软件部署利⽤GPO组策略横向 密码喷洒密码策略检查喷洒主机喷洒⽤户名喷洒密码喷洒hash喷洒服务 痕迹清除OPSEC清除webshell清除隧道⼯具清除落地样本清除…

由于找不到krpt.dll,无法继续执行代码该怎么办?总结三种简单有效修复方法

1. krpt.dll 简介 1.1 定义 krpt.dll 是一个 Windows 动态链接库文件&#xff08;Dynamic Link Library&#xff09;&#xff0c;这种类型的文件包含可由多个应用程序共享的函数和资源。它是Windows操作系统中的一个重要组件&#xff0c;对于系统的正常运行起着至关重要的作用…

模块化沙箱的功能特点

模块化沙箱是一种高灵活性和高扩展性的数据安全产品&#xff0c;通过选择不同的沙箱模块&#xff0c;满足不同的安全需求。 同时&#xff0c;深信达模块化沙箱&#xff0c;根据企事业单位各类国密标准需求&#xff0c;合理转换沙箱模式&#xff0c;满足不同场景、不同类型的数…

TK东南亚、美区、英区产品投放内容该如何选择?

TikTok是抖音在海外市场的版本&#xff0c;已经成为全球最受欢迎的短视频应用之一&#xff0c;并被视为品牌国际化的重要平台。卖家若能有效利用 TikTok&#xff0c;有望在全球范围内提升企业知名度和产品销量&#xff0c;吸引大量的粉丝和订单。那么&#xff0c;在不同国家&am…

每日论文13-18TCAS2数控调谐电感的V波段CMOS压控振荡器

《A V-Band CMOS VCO With Digitally-Controlled Inductor for Frequency Tuning》 18TCAS2 广东省毫米波与太赫兹重点实验室 有个手头上的东西感觉粗调电感可能会比粗调电容好一些&#xff0c;所以拜读一下老板18年的这篇TCAS2&#xff0c;这感觉是个偏理论一点的工作。 首…

哇塞!FLUX 杠上 Midjourney,你选谁?

大家和大家聊聊最近超火的 AI 绘图工具 ——Black Forest Labs 的 FLUX 和一直备受青睐的 Midjourney。 来源&#xff1a;blackforestlabs.ai FLUX 这套开源的文本转图像模型一经推出&#xff0c;就掀起了不小的波澜。好多设计同行都对它充满了好奇与期待&#xff0c;这无疑给…

封装、继承、抽象类

面向对象共有三个特征&#xff1a;封装&#xff0c;继承&#xff0c;多态。 封装 封装表现&#xff1a; &#xff08;1&#xff09;方法就是一个最基本封装体。 &#xff08;2&#xff09;类其实也是一个封装体。 封装的好处&#xff1a; &#xff08;1&#xff09;提高…

Jquery serialize()、serializeArray()、$.param()

param()方法 1.定义&#xff1a;param() 方法创建**数组或对象**的序列化表示。》》该序列化值可在进行 AJAX 请求时在 URL 查询字符串中使用。2.语法&#xff1a;$.param(object,trad)object&#xff1a;必需&#xff0c;规定要序列化的数组或对象。trad&#xff1a;可选。布尔…

如何提高LabVIEW编程效率

提高LabVIEW编程效率对开发者来说非常重要&#xff0c;尤其是在处理复杂项目或紧迫的开发周期时。以下是一些可以显著提升LabVIEW编程效率的技巧&#xff0c;从代码结构、工具使用到团队协作的多个角度进行详细分析&#xff1a; 1. 模块化设计 模块化设计 是提高代码可维护性和…

Linux——grep-wc-管道符

grep命令 利用关键字过滤文件行&#xff0c;找到关键字所在那一行 wc命令 统计文件行数&#xff0c;单词数量 wc命令 不带选项全选 wc -c test.txt 字节bytes数量 wc -m test.txt 字符数量 wc -l test-txt 行数 wc -w test-txt 单词数量 管道符 | 将左边命令的…

【LLM论文日更】| BGE-M3E embedding模型

论文&#xff1a;https://arxiv.org/pdf/2402.03216代码&#xff1a;GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs机构&#xff1a;BAAI领域&#xff1a;embedding model发表&#xff1a; ​ 研究背景 研究问题&#xff1a;这篇文章要解决的问…

AI时代大厂AI项目管理学习路线

AI时代避免被裁员&#xff0c;大厂AI项目管理学习路线主要包括&#xff1a; 1、AI项目管理基础技能。 2、项目管理AI技术知识。 3、数据分析与决策。 4、AI项目管理工具。 5、AI项目管理知识扩展。 01 AI项目管理基础技能。 AI项目管理基础技能构成了项目管理的骨架&…

SQL 干货 | 使用 EXISTS 编写 SELECT 查询

基于 SQL 中的 EXISTS 运算符为我们提供了一种基于其他数据是否存在&#xff08;或不存在&#xff09;来检索数据的简便方法。更具体地说&#xff0c;它是一个逻辑运算符&#xff0c;用于评估子查询的结果&#xff0c;并返回一个布尔值&#xff0c;该值指示是否返回了行。尽管 …

《用comfyUI挑战全网AI图片产品实践案例》之comfyUI抠图工作流,用免费打败收费,实现素材自由

近段时间AI非常的火。目前有很多软件已经拥抱了AI&#xff0c;加入了AI的一些功能。像AI绘画的功能&#xff0c;基本上是每个大厂的软件产品都会配备。但是呢&#xff0c;这些功能都是要付费的。而且是按月收费或者是按年收费。整体算下来十分的不划算。所以我尝试用stable dif…

基础岛 第3关 :浦语提示词工程实践

作业 基础任务 (完成此任务即完成闯关) 背景问题&#xff1a;近期相关研究发现&#xff0c;LLM在对比浮点数字时表现不佳&#xff0c;经验证&#xff0c;internlm2-chat-1.8b (internlm2-chat-7b)也存在这一问题&#xff0c;例如认为13.8<13.11。 任务要求&#xff1a;利用…

嵌入式面试——FreeRTOS篇(五) 事件标志组

本篇为&#xff1a;FreeRTOS事件标志组篇 1、事件标志组介绍 答&#xff1a; 事件标志位&#xff1a;用一个位&#xff0c;来表示事件是否发生。 事件标志组是一组事件标志位的合集&#xff0c;可以简单的理解事件标志组&#xff0c;就是一个整数。 2、事件标志组的特点 答&am…

R包:APAlyzer从RNA-seq数据计算APA表达丰度

文章目录 介绍教程实战案例数据脚本运行 介绍 今天安利APAlyzer工具&#xff0c;它是通过RNA-seq数据获取3′UTR APA, intronic APA等表达谱的R包。 APAlyzer将bam文件比对到PolyA-DB数据库识别APA。 Most eukaryotic genes produce alternative polyadenylation (APA) isofo…