AVL树(C++实现)

news/2024/4/24 6:38:51/

文章目录

  • AVL树的概念
  • AVL树结点定义
  • AVL树的插入
  • AVL树的旋转
    • 左单旋
    • 右单旋
    • 左右单旋
    • 右左双旋
  • AVL树的验证
  • AVL树的性能
  • AVL树及测试完整代码

AVL树的概念

二叉搜索树虽然可以缩短查找的效率,但如果数据有序或接近有序,那么二叉搜索树将退化为单支树,查找元素则相当于在顺序表中搜索元素,是将复杂度为O(n),效率低下.
如果当向二叉搜索树中插入新的结点后,如果能保证每个结点的左右子树高度只差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,减少平均搜索长度.

AVL树的性质:
1: 它的左右子树AVL树.
2: 任意一个子树左右高度差都不超过1(-1 / 0 / 1 )
在这里插入图片描述
如果一棵二叉搜索树是高度平衡的如果它有N个结点,其高度可保持在O(log2N),搜索时间复杂度也为O(log2N).

AVL树结点定义

我们实现的是KV模型的AVL树,为了方便后续的操作,这里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){}
};

AVL树的插入

AVL树的插入总共有三个步骤:
1: 找到要插入结点的位置.

2:插入成功后,控制平衡,更新平衡因子.

3:当插入时parent所在子树已经不平衡了,则需要旋转.

1:找到需要插入结点的位置
我们可以利用搜索二叉树基本思想进行查找插入位置:
1: 如果插入的结点值大于当前结点值,向右遍历.

2: 如果插入的结点值小于当前结点值,向左遍历.

3:如果插入的结点值等于当前结点值,则不需要插入.

2:插入成功后,更新平衡因子:
(a): 插入成功,将当前结点的parent指向插入结点,将插入结点的_parent指向parent,建立插入结点与parent结点的联系.
(b): 向上循环更新平衡因子
更新平衡因子时,我们将cur代表新插入的结点,parent代表插入结点的父亲结点,一直向上循环更新平衡因子知道parent为空.
1:如果新增在右,_bf++,如果新增在左,_bf–.

2: 如果更新后,abs( parent->_bf ) ==1,说明parent插入之前的平衡因子是0,即插入前左右子树高度相等,插入后,parent所在树的高度发生改变,即左右子树一边高一边低,进而导致parent的平衡因子改变,需要继续向上更新(因为会对parent的祖先平衡因子造成影响)

3:如果更新后,abs(parent->_bf ) == 0, 说明parent插入之前的平衡因子是1or-1,插入之后,parent所在树的高度不变,即对祖先无影响,此时不需要往上更新平衡因子.

4: 如果更新后,abs(parent->_bf) == 2,说明parent插入之前的平衡因子是1or-1,已经达到平衡临界值,插入新结点变成2or-2.此时,已经打破平衡,parent所在子树需要旋转处理.

5: 如果更新后,parent->_bf > 2或者parent->_bf < -2,此时说明在插入之前,这个AVL树已经不平衡了,此时需要断言处理,检查之前的操作问题.

3:当插入时parent所在子树已经不平衡了,则需要旋转

当更新完平衡因子,此时parent已经不平衡了,此时分为四种情况

1: 当parent的平衡因子为2,cur的平衡因子为1时,进行左单旋.

2: 当parent的平衡因子为-2,cur的平衡因子为-1时,进行右单旋.

3:当parent的平衡因子为-2,cur的平衡因子为1时,进行左右单旋.

4: 当parent的平衡因子为2,cur的平衡因子为-1时,进行右左单旋.

插入代码如下:

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->first < kv.first){parent = cur;cur = cur->_right;}else if (cur->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;//插入时,cur的parent也要维护.//控制平衡,如何更新平衡因子.while ( parent  ){if (cur == parent->_right){parent->_bf++;}else{parent->_bf--;}if (abs(parent->_bf) == 1){parent = parent->_parent;cur = cur->_parent;}else if (abs(parent->_bf) == 0 ) //对祖先的平衡因子没有影响,所以不需要再更新平衡因子.{break;                 }else if( abs( parent->_bf) == 2 ) //parent所在的子树已经不平衡了,需要旋转处理{if (parent->_bf == 2 && cur->_bf == 1)//parent的平衡因子等于2,且cur的平衡因子等于1的时候,需要左单旋.{RotateL(parent);      //左单旋}else if (parent->_bf == -2 && cur->_bf == -1) //parent的平衡因子等于-2,且cur的平衡因子等于-1的时候,需要右单旋.{RotateR(parent);     //右单旋}else if ( parent->_bf == -2 && cur->_bf == 1  )//符合parent的平衡因子等于-2,且cur的平衡因子等于1,采用左右双旋.{RotateLR(parent);}break;                 //例如,原来parent所在树的高度为h+2,插入新节点之后变成//h+3,可是右单旋之后又变成了h+2,此时parent高度并没有发生变化,//不必再往上更新平衡}else                             //走到这里,插入结点发现插入平衡因子的绝对值是大于2的.{assert(false);}	}return true;                       //插入,更新完平衡因子,即可插入成功.}

注意:当旋转完毕,在旋转时已经更新了平衡因子,此时parent的高度相较之前并没有发生改变,也就是说parent所在子树并不会影响祖先的平衡因子,此时可以退出,不必往上更新平衡因子了.

AVL树的旋转

左单旋

将新节点插入parent右子树较高的右侧:
左单旋的操作步骤:
1: 让parent作为subR的左子树.

2: 让subR的左子树作为parent的右子树.

3: 判断根,并相应作出调整
(a) : 如果parent是整棵树的根
如果parent是整棵树的根,旋转后subR就是这棵树的根,让_root指向subR.
(b): 如果parent是子树的根
如果parent是子树的根,此时要记录旋转前parent的parent为ppNode,将subR与
ppNode建立联系,此时又有两种情况:
(1): 如果旋转前,parent为ppNode的左子树,那么旋转后,subR就为ppNode的左子树.
(2): 如果旋转前,parent为ppNode的右子树,那么旋转后,subR就为ppNode的右子树.
4: 更新平衡因子
左单旋中,只有parent与subR的高度发生变化需要更新平衡因子,且二者的平衡因子单旋后都为0.
在这里插入图片描述
左单旋代码如下:

void RotateL(Node* parent)              //左旋{Node* subR = parent->_right;Node* subRL = subR->_left;Node* ppNode = parent->_parent;subR->_left = parent;parent->_right = subRL;if (subRL)                 //subRL的h有可能为0.{subRL->_parent = parent;}parent->_parent = subR;//判断parent是整棵树的根,还是其他树的子树.if (_root == parent)  //如果parent是整棵树的根{_root = subR;subR->_parent = nullptr;,}else{if (ppNode->_left == parent)  //如果parent在ppNode的左边{ppNode->_left = subR;}else                         //如果parent在ppNode的右边{ppNode->_right = subR;}subR->_parent = ppNode;  //subR的parent指向ppNode.}subR->_bf = parent->_bf = 0;              //旋转完毕答,即可更新平衡因子.}

注意:
当h==0时,即subRL为空时,此时不能访问subRL的父亲,所以针对它要额外进行判断.(包括右单旋)

右单旋

将新节点插入parent当前左子树较高的左侧:
右单旋的操作步骤:
1: 让parent作为subL的右子树.

2: 让subL的右子树作为parent的左子树.

3: 判断根,并相应作出调整
(a) : 如果parent是整棵树的根
如果parent是整棵树的根,旋转后subR就是这棵树的根,让_root指向subL.
(b): 如果parent是子树的根
如果parent是子树的根,此时要记录旋转前parent的parent为ppNode,将subL与
ppNode建立联系,此时又有两种情况:
(1): 如果旋转前,parent为ppNode的左子树,那么旋转后,subL就为ppNode的左子树.
(2): 如果旋转前,parent为ppNode的右子树,那么旋转后,subL就为ppNode的右子树.

4: 更新平衡因子
右单旋中,只有parent与subR的高度发生变化需要更新平衡因子,且二者的平衡因子单旋后都为0.
在这里插入图片描述
右单旋的代码如下:

void RotateR(Node* parent)    //右旋{Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppNode = parent->_parent;subL->_right = parent;parent->_left = subLR;if (subLR)                 //subRL的h有可能为0.{subLR->_parent = parent;}parent->_parent = subL;//判断parent是整棵树的根,还是其他树的子树.if (_root == parent)  //如果parent是整棵树的根{_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent)  //如果parent在ppNode的左边{ppNode->_left = subL;}else                         //如果parent在ppNode的右边{ppNode->_right = subL;}subL->_parent = ppNode;}subL->_bf = parent->_bf = 0;}

左右单旋

左右单旋主要有四个步骤:
1:在b处插入新结点.(假设在b处插入)
在这里插入图片描述
2: 以subL为旋转点左单旋.
在这里插入图片描述

3: 以subLR为旋转点右单旋.
在这里插入图片描述
4:更新平衡因子:
左右双旋中更新平衡因子根据插入新结点的位置有三种情况:

(1) 当在b处插入新结点时,即subRL->_bf 等于 1,左右双旋后,parent,subL,subLR的平衡因子分别为:0,-1,0.
在这里插入图片描述

(2)当在b处插入新结点时,即subLR等于-1,左右双旋后,parent,subL,subLR的平衡因子分别为:1,0,0
在这里插入图片描述

(3)当h==0,及subLR就为新插入的结时,左右双旋后,parent,subL,subLR的平衡因子都为0.
在这里插入图片描述
注意:
由以上更新平衡因子的情况可以看出,在插入新结点后,经过旋转后,parent的高度还是插入之前的高度,及不会影响其祖先的平衡因子,所以左右当选之后即可从更新平衡因子循环中退出.
左右双旋代码如下:

void RotateLR(Node* parent)       //左右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;          //记录subLR的平衡因子,根据该平衡因子的情况分三种//方式进行更新平衡因子.RotateL(parent->_left);       //左单旋RotateR(parent);             //右单旋subLR->_bf = 0;              //结点60的平衡因子一定为0.if ( bf == 1  )             //在c处插入{parent->_bf = 0;subL->_bf = -1;}else if ( bf == -1 )      //在b处插入{parent->_bf = 1subL->_bf = 0;}else if( bf == 0 )       //b处自己便是新增.{parent->_bf = subL->_bf = 0;}else                   //如果都不是,那就是有错误的地方.{assert(false);}}

右左双旋

右左单旋主要分为四个步骤:
1:插入新节点(假设在b处插入)
在这里插入图片描述

2: 以subR结点右单旋.
在这里插入图片描述

3: 以subRL结点左单旋.
在这里插入图片描述

4: 更新平衡因子
右左双旋中更新平衡因子根据插入新结点的位置有三种情况:
(1) :如果在c处插入,即subRL->_bf等于1,parent,subR,subRL的平衡因子分别为:
在这里插入图片描述

(2): 如果在b处插入,即subRL->_bf等于-1,parent,subR,subRL的平衡因子分别为:
在这里插入图片描述

(3):当h==0,即subRL结点就为新增结点,此时parent,subR,subRL的平衡因子都为0.
在这里插入图片描述

右左双旋代码如下:

void RotateRL( Node* parent ){RotateR(parent->_right);RotateL(parent);Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;subRL->_bf = 0;if( bf == 1 ){parent->_bf = -1;subR->_bf = 0;}else if ( bf == -1 ){parent->_bf = 0;subR->_bf = 1;}else if (bf == 0){parent->_bf = subR->_bf = 0;}else{assert(false);}}
private:Node* _root = nullptr;
};

AVL树的验证

因为AVL树也是二叉搜索树,我们可以采用中序遍历的方式遍历AVL树:

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

但是中序遍历只能证明AVL树是二叉搜索树,并不能证明它具备平衡的性质,所以我们可以根据该树的平衡因子来判断这棵树是否平衡,而求平衡因子首先就要求这棵树(或者子树的高度).

获取二叉树的高度采用的是后序遍历的思想,判断AVL树的平衡必须要求得左右子树的高度差(即平衡因子),如果平衡因子的绝对值 < 2,说明概树处于平衡状态.

注意:
判断AVL树的平衡是要判断它本身以及左右子树的平衡.

int Height(Node* root)                 //求高度{if (root == nullptr){return 0;}int leftHT = Height(root->_left);int rightHT = Height(root->_right);return max(leftHT, rightHT) + 1;      //获取完这棵子树的高度,然后返回到上一层递归的地方.}bool IsBanlance()           {return _IsBanlance(_root);}bool _IsBanlance(Node* root)           //判断AVL树是否平衡.{if (root == nullptr){return true;}int leftHT = Height(root->_left);int rightHT = Height(root->_right);int diff = rightHT - leftHT;return abs(diff) < 2 && _IsBanlance(root->_left) && _IsBanlance(root->_right);//要判断整棵树以及这棵树的左右子树是否平衡.}

AVL树的性能

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

AVL树及测试完整代码

#include <map>
#include <iostream>
#include <assert.h>
#include <algorithm>
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;        //平衡因子AVLTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0),_kv(kv) //走pair的构造函数{}
};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;}else{parent->_left = cur;}cur->_parent = parent;//插入时,cur的parent也要维护.//控制平衡,如何更新平衡因子.while (parent){if (cur == parent->_right){parent->_bf++;}else{parent->_bf--;}if (abs(parent->_bf) == 1){parent = parent->_parent;cur = cur->_parent;}else if (abs(parent->_bf) == 0) //对祖先的平衡因子没有影响,所以不需要再更新平衡因子.{break;}else if (abs(parent->_bf) == 2) //parent所在的子树已经不平衡了,需要旋转处理{if (parent->_bf == 2 && cur->_bf == 1)//parent的平衡因子等于2,且cur的平衡因子等于1的时候,需要左单旋.{RotateL(parent);      //左单旋}else if (parent->_bf == -2 && cur->_bf == -1) //parent的平衡因子等于-2,且cur的平衡因子等于-1的时候,需要右单旋.{RotateR(parent);     //右单旋}else if (parent->_bf == -2 && cur->_bf == 1)//符合parent的平衡因子等于-2,且cur的平衡因子等于1,采用左右双旋.{RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else{assert(false);}break;               //例如,原来parent所在树的高度为h+2,插入新节点之后变成//h+3,可是右单旋之后又变成了h+2,此时parent高度并没有发生变化,//不必再往上更新平衡}else                             //走到这里,插入结点发现插入平衡因子的绝对值是大于2的.{assert(false);}}return true;                       //插入,更新完平衡因子,即可插入成功.}void InOrder(){_InOrder(_root);}bool IsBanlance(){return _IsBanlance(_root);}
private:bool _IsBanlance(Node* root){if (root == nullptr){return true;}int leftHT = Height(root->_left);int rightHT = Height(root->_right);int diff = rightHT - leftHT;return abs(diff) < 2 && _IsBanlance(root->_left) && _IsBanlance(root->_right); //整棵树平衡且左子树和右子树都是平衡的.}//求高度int Height(Node* root){if (root == nullptr){return 0;}int leftHT = Height(root->_left);int rightHT = Height(root->_right);return max(leftHT, rightHT) + 1;}//遍历void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}void RotateL(Node* parent)              //左旋{Node* subR = parent->_right;Node* subRL = subR->_left;Node* ppNode = parent->_parent;subR->_left = parent;parent->_right = subRL;if (subRL)                 //subRL的h有可能为0.{subRL->_parent = parent;}parent->_parent = subR;//判断parent是整棵树的根,还是其他树的子树.if (_root == parent)  //如果parent是整棵树的根{_root = subR;subR->_parent = nullptr;}else{if (ppNode->_left == parent)  //如果parent在ppNode的左边{ppNode->_left = subR;}else                         //如果parent在ppNode的右边{ppNode->_right = subR;}subR->_parent = ppNode;}subR->_bf = parent->_bf = 0;              //旋转完毕答,即可更新平衡因子.}void RotateR(Node* parent)    //右旋{Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppNode = parent->_parent;subL->_right = parent;parent->_left = subLR;if (subLR)                 //subRL的h有可能为0.{subLR->_parent = parent;}parent->_parent = subL;//判断parent是整棵树的根,还是其他树的子树.if (_root == parent)  //如果parent是整棵树的根{_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent)  //如果parent在ppNode的左边{ppNode->_left = subL;}else                         //如果parent在ppNode的右边{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;          //记录subLR的平衡因子,根据该平衡因子的情况分三种//方式进行更新平衡因子.RotateL(parent->_left);       //左单旋RotateR(parent);             //右单旋subLR->_bf = 0;              //结点60的平衡因子一定为0.if (bf == 1)             //在c处插入{parent->_bf = 0;subL->_bf = -1;}else if (bf == -1)      //在b处插入{parent->_bf = 1;subL->_bf = 0;}else if (bf == 0)       //b处自己便是新增.{parent->_bf = subL->_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);subRL->_bf = 0;if (bf == 1){subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;parent->_bf = 0;}else if (bf == 0){parent->_bf = 0;subR->_bf = 0;}else{assert(false);}}private:Node* _root = nullptr;
};//测试代码1:
void TestAVLTree1()
{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();
}
//测试代码2:
//随机样式
//查AVL树的高度
void TestAVLTree2()
{int a[] = {4,2,6,1,3,5,15,7,16,14 };AVLTree<int, int> t1;for (auto e : a){t1.Insert(make_pair(e, e));}t1.InOrder();cout << "IsBanlance:" << t1.IsBanlance();
}//测试3
//随机数测试,使用10000个随机数测试
void TestAVLTree3()
{size_t N = 10000;srand(time(0));AVLTree<int, int> t1;for (size_t i = 0; i < N; ++i){int x = rand();               //x接收产生的随机数.t1.Insert(make_pair(x, i));   //在树中插入随机数.}cout << "IsBanlance:" << t1.IsBanlance() << endl;
}

http://www.ppmy.cn/news/46958.html

相关文章

WHOIS查询参考:域名注册信息的含义

以下详细说明了域名的域名所有者、管理联系人、技术联系人、付费联系人等信息的中英文含义。为注册域名和whois查询提供参考。 域名所有者注册信息含义 域名所有者&#xff08;Registrant&#xff09;是指域名属于谁&#xff0c;用于判定域名所有权。域名所有者可以是单位企业…

C语言实现顺序表--数据结构

魔王的介绍&#xff1a;&#x1f636;‍&#x1f32b;️一名双非本科大一小白。魔王的目标&#xff1a;&#x1f92f;努力赶上周围卷王的脚步。魔王的主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王.&#x1f525;&#x1f525;&#x1f525; ❤️‍&#x1…

如何使用Tensorflow神经网络模型来完成兰州房价预测分析?

兰州房价预测是一个非常热门的话题,许多人都对如何预测兰州房价感兴趣。在本文中,我将介绍如何使用TensorFlow来预测兰州房价,并提供Python源代码。 首先,我们需要收集兰州的房价数据。我们可以从房地产网站或政府统计数据中获取。在本文中,我们将使用Kaggle上提供的兰州…

收音机知识,调谐(选频/滤波),调制(升频)

参考&#xff1a;https://www.bilibili.com/video/BV1d14y1N7nm/?spm_id_from333.999.0.0&vd_source00bd76f9d6dc090461cddd9f0deb2d51 有关知识提纲 整个信号的传输变化调谐人耳听到声音的频率范围&#xff08;20~20000Hz&#xff09;天线和传送信号的波长关系波长和天线…

RHCE第一次作业at和cront两个任务管理程序的区别

1.at 单一执行的例行性工作&#xff1a;仅处理执行一次就结束了 -m 当任务完成之后&#xff0c;即使没有标准输出&#xff0c;将给用户发送邮件 -l atq的别名&#xff0c;可列出目前系统上面的所有该用户的at调度 -d atrm的别名,可以取消一个在at调度中的工作 -v 使用较明显的…

pandas中df.groupby详解?

df.groupby 是 pandas 库用于实现按照某些列进行拆分&#xff0c;应用函数和组合的一个功能。步骤如下&#xff1a; 1. 按照指定的一列或多列进行分组 (grouping) 2. 对每个分组应用一个聚合函数 (aggregation) 3. 将每个分组的聚合结果合并成一个数据结构 语法&#xff1a; df…

Session详解(重点)

类似于去服务器注册账号&#xff0c;只要服务器不停机&#xff0c;自己注册的账号一直会存在服务器。 什么是Session&#xff1a; 1.服务器会给每一个用户&#xff08;浏览器&#xff09;创建一个对象&#xff1b; 2.一个Session独占一个浏览器&#xff0c;只要浏览器没有关…

***大论文中插入Visio不失真方法:word插入viso图片方法

***大论文中插入Visio不失真方法&#xff1a;word插入viso图片方法 1、可以直接导出emf2、如果利用emf导致字符间距过大&#xff0c;可以选择下面方式 1、可以直接导出emf 导出emf方法&#xff1a; 打开visio --> 另存为 --> 选择emf格式文件 打开word --> 插入图片…

[API]ListList方法集合排序Lambda表达式(四)

List接口&#xff1a; 继承自Collection接口&#xff0c;List集合是可重复集合&#xff0c;并且有序&#xff0c;还提供了一套可以通过下标来操作元素的方法 常见的实现类&#xff1a; ArrayList&#xff1a;内部使用数组实现&#xff0c;查询性能更好(直接下标找到物理地址)、…

开源GPT-4小羊驼(Vicuna)快速上手指南

小羊驼&#xff08;Vicuna)是什么 Vicuna: 一个开源的GPT&#xff0c;宣称实现了GPT-4 90%的功能。 UC伯克利学者联手CMU、斯坦福等&#xff0c;再次推出一个全新模型70亿/130亿参数的Vicuna&#xff0c;俗称「小羊驼」&#xff08;骆马&#xff09;。 并且和其他以往不同的是…

2023红明谷杯部分WP

0x00 签到 一直点就能得到flag 0x01 Dreamer 拿到题感觉有点儿懵 先下发靶机看一眼 梦想家CMS&#xff0c;好嘛&#xff0c;我直接一手查找官网 直接一手演示中心碰运气 哎嘿嘿&#xff0c;运气不错进去了&#xff0c;突然想起之前有位大佬写的关于Dreamer CMS的代码审…

基于ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析能力与项目科研水平

【原文链接】&#xff1a;基于ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土壤、农业、大气等领域的数据分析https://mp.weixin.qq.com/s?__bizMzU5NTkyMzcxNw&mid2247537467&idx4&sn10c4c12897282daf5320efae05caf3a4&chksmfe689551…

​​2021遥感应用组二等奖:基于机器学习回归算法的鄱阳湖水质遥感定量反演及时序变化监测研究

作品介绍 一、作品背景 鄱阳湖是中国第一大淡水湖&#xff0c;也是中国第二大湖&#xff0c;它在调节长江水位、涵养水源、改善当地气候等方面起着重大的作用。但近年来受围垦、环境污染等人类活动影响&#xff0c;鄱阳湖湿地退化严重&#xff0c;同时使鄱阳湖的容量减少&…

Kafka的历史版本对应SpringBoot版本

截至目前&#xff08;2023年&#xff09;&#xff0c;Kafka的最新版本是2.9.0&#xff0c;发布于2022年11月30日。Kafka的历史版本可以在Kafka官方网站的下载页面中找到。Kafka从0.8版本开始发布&#xff0c;经历了多个版本的迭代和升级。以下是一些比较重要的Kafka版本及其发布…

US News退榜风波后,发布最新美国最佳法学院和医学院排名

从2022年11月开始&#xff0c;美国权威排名机构US News不断陷入风波。耶鲁大学法学院率先宣布退出US News法学院排名&#xff0c;先是法学院&#xff0c;后是医学院&#xff0c;包括哈佛大学大学、斯坦福大学、哥伦比亚大学和加州大学伯克利分校等名校也纷纷宣布退出。 这些老…

The 1st Universal Cup Stage 12: ̄Ookayama, April 15-16, 2023 题解

A XOR Tree Path 给一颗树&#xff0c;树上点有黑白两色&#xff0c;每次可以选一个叶子节点&#xff0c;翻转其到根路径上所有点的颜色&#xff0c;问最大黑色点数。 树dp #include<bits/stdc.h> using namespace std; #define MAXN (10000010) #define ll long long…

【社区图书馆】启迪后人——GPT 与读书的奇妙之旅

随着科技的发展和人工智能的不断进步&#xff0c;我们的阅读方式也在逐渐改变。作为一个热爱读书的人&#xff0c;我深感好奇与惊讶地发现&#xff0c;GPT&#xff08;即生成预训练 Transformer&#xff09;正以前所未有的方式拓展我们的阅读视野。在这篇博客中&#xff0c;我将…

RabbitMQ-整合mqtt

用 springboot rabbitmq可以搭建物联网&#xff08;IOT&#xff09;平台&#xff0c;rabbitmq 不是消息队列吗&#xff0c;原来rabbitmq有两种协议&#xff0c;消息队列是用的AMQP协议&#xff0c;而用在智能硬件中的是MQTT协议。 一、rabbitmq是什么&#xff1f; RabbitMQ就…

Windows 自带环境变量

目录 Windows自带环境变量说明Windows自带环境变量总结 所谓 Windows 环境变量&#xff0c;指的是 Windows 指定操作系统工作环境的一些设置选项或属性参数&#xff0c;比方说指定系统文件夹或临时文件夹的位置等。与常量相比&#xff0c;一个环境变量往往由变量名称和变量值组…

MySQL全局锁、表级锁、行级锁介绍演示(详细)

目录 介绍 分类 1、全局锁 1.1介绍 1.2场景 1.3语法 1.4演示 2、表级锁 2.1介绍 2.2分类 2.3语法 2.4演示 3、行级锁 3.1介绍 3.2分类 3.3场景 介绍 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;…