AVL树的实现

news2024/12/25 9:18:03

文章目录

  • AVL树
    • 前言
    • 1. AVL树的概念
    • 2. AVL树的结构
      • 2.1 AVL树节点的定义
      • 2.2 AVL树的结构
    • 3. AVL树的操作
      • 3.1 AVL树的插入
      • 3.2 AVL树的旋转(重要)
        • 3.2.1 左单旋
          • 过程
          • 代码
        • 3.2.2 右单旋
          • 过程
          • 代码
        • 3.2.3 左右双旋
          • 过程
          • 代码
        • 3.2.4 右左双旋
          • 过程
          • 代码
        • 旋转整体代码
      • 3.3 AVL树的验证
      • 3.4 AVL树的删除(了解)
    • 4. AVL树的整体代码
    • 5. AVL树的性能

AVL树

前言

前面对map/multimap/set/multiset进行了简单的介绍,在其文档介绍中发现,这几个容器有个共同点是:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

1. AVL树的概念

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

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

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

在这里插入图片描述

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

2. AVL树的结构

2.1 AVL树节点的定义

相比于普通的二叉搜索树, AVL树要保证每个结点的左右子树高度之差的绝对值不超过1,需要引入一个平衡因子保持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)
	{}
};

2.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)
	{}
};
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
		//一系列操作
private:
	Node* _root = nullptr;
}

3. AVL树的操作

3.1 AVL树的插入

AVL树的插入,本质上还是二叉搜索树的插入方式,因此大体的插入逻辑和普通的二叉搜索树相同,即:比根小向左遍历,比根大向右遍历,如果遇到相同节点,就插入失败,返回false,如果没遇到,遇到空的地方就直接插入,但是需要处理更新平衡因子保证左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

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
        {
            return false; // 该元素已经在树中存在了, 无法插入
        }
    }

    // 链接
    cur = new Node(kv);
    if (parent->_kv.first > kv.first)
    {
        parent->_left = cur;
    }
    else
    {
        parent->_right = cur;
    }
    cur->_parent = parent;

    // 更新平衡因子
    while (parent) // parent为空,也就更新到根
    {
        if (parent->_right == cur)
        {
            parent->_bf++;
        }
        else
        {
            parent->_bf--;
        }

        if (parent->_bf == 1 || parent->_bf == -1)
        {
            // 继续更新
            parent = parent->_parent;
            cur = cur->_parent;
        }
        else if (parent->_bf == 0) // 结束了
        {
            break;
        }
        else if (parent->_bf == 2 || parent->_bf == -2)
        {
            // 旋转处理 --- 1.让这棵子树平衡   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)   //左右双旋
            {
                RotateLR(parent);
            }
            else if (parent->_bf == 2 && cur->_bf == -1)   //右左双旋
            {
                RotateRL(parent);
            }
            else
            {
                assert(false);
            }
            break;
        }
        else
        {
            assert(false);
        }
    }
    return true;
}

3.2 AVL树的旋转(重要)

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

旋转的原则: 保持它继续是搜索树
旋转的目的: 左右均衡,降低整棵树的高度

3.2.1 左单旋

过程

在这里插入图片描述

代码

写单旋的代码时,要注意判断parent是根节点还是根节点的一棵子树分为两种情况,最后按照图中画的更新平衡因子

// 左单旋
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 (ppnode == nullptr) // parent本身就是根
    {
        _root = subR;
        _root->_parent = nullptr;
    }
    else // parent只是一棵子树
    {
        if (ppnode->_left == parent) // 判断原来的节点是左右哪一棵子树
        {
            ppnode->_left = subR;
        }
        else
        {
            ppnode->_right = subR;
        }
        subR->_parent = ppnode;
    }

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

3.2.2 右单旋

过程

在这里插入图片描述

代码
//右单旋
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 (ppnode == nullptr)
    {
        _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;
}

3.2.3 左右双旋

过程

在这里插入图片描述

代码

写双旋代码时,旋转的整个过程直接复用单旋的代码,要注意平衡因子的更新维护,照图中画的更新平衡因子,一共有3种情况

//左右双旋
void RotateLR(Node *parent)
{
    Node *subL = parent->_left;
    Node *subLR = subL->_right;
    int bf = subLR->_bf;

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

    if (bf == 1)
    {
        parent->_bf = 0;
        subLR->_bf = 0;
        subL->_bf = -1;
    }
    else if (bf == -1)
    {
        parent->_bf = 1;
        subLR->_bf = 0;
        subL->_bf = 0;
    }
    else if (bf == 0)
    {
        parent->_bf = 0;
        subLR->_bf = 0;
        subL->_bf = 0;
    }
    else
    {
        assert(false);
    }
}

3.2.4 右左双旋

过程

在这里插入图片描述

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

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

    if (bf == 1)
    {
        parent->_bf = -1;
        subRL->_bf = 0;
        subR->_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 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 (ppnode == nullptr) // parent本身就是根
    {
        _root = subR;
        _root->_parent = nullptr;
    }
    else // parent只是一棵子树
    {
        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 (ppnode == nullptr)
    {
        _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 RotateLR(Node *parent)
{
    Node *subL = parent->_left;
    Node *subLR = subL->_right;
    int bf = subLR->_bf;

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

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

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

3.3 AVL树的验证

AVL树是在二叉搜索树基础上增加了平衡性的限制,要验证是否为AVL树需要2步:

  • 验证其为二叉搜索树
    • 若中序遍历可得到一个有序序列,就说明为二叉搜索树
  • 验证其为平衡树
    • 每个节点左右子树高度差的绝对值不超过1
    • 节点的平衡因子是否计算正确
int _Height(Node *root)
{
    if (root == nullptr)
        return 0;

    int leftH = _Height(root->_left);
    int rightH = _Height(root->_right);

    return leftH > rightH ? leftH + 1 : rightH + 1;
}

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

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

    int leftH = _Height(root->_left);
    int rightH = _Height(root->_right);

    // 验证具体的每个平衡因子是否计算正确
    if (rightH - leftH != root->_bf)
    {
        cout << root->_kv.first << "节点平衡因子异常" << endl;
        return false;
    }

    // 当前左右高度差绝对值小于2, 再去检查左子树和右子树
    return abs(leftH - rightH) < 2 
        && _IsBalance(root->_left) 
        && _IsBalance(root->_right);
}

3.4 AVL树的删除(了解)

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。具体实现可参考《算法导论》或《数据结构-用面向对象方法与C++描述》殷人昆版。

4. AVL树的整体代码

#include<iostream>
#include<utility>
#include<assert.h>
#include<stdlib.h>
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)
		, _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->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;



		//更新平衡因子
		while(parent)		 //parent为空,也就更新到根
		{
			if (parent->_right == cur)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}

			if (parent->_bf == 1 || parent->_bf == -1)
			{
				//继续更新
				parent = parent->_parent;
				cur = cur->_parent;
			}
			else if (parent->_bf == 0)  //结束了
			{
				break;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//旋转处理 --- 1.让这棵子树平衡   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)
				{
					RotateLR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}

	void Inorder()
	{
		_Inorder(_root);
	}

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

	bool IsBalance()
	{
		return  _IsBalance(_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 (ppnode == nullptr)				//parent本身就是根
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else								//parent只是一棵子树
		{
			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 (ppnode == nullptr)
		{
			_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 RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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


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

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


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

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}
	

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

		int leftH= _Height(root->_left);
		int rightH = _Height(root->_right);

		//验证具体的每个平衡因子
		if (rightH - leftH != root->_bf)		
		{
			cout << root->_kv.first << "节点平衡因子异常" << endl;    
			return false;
		}


		//当前左右高度差绝对值小于2, 再去检查左子树和右子树
		return abs(leftH - rightH) < 2 
			&& _IsBalance(root->_left) 
			&& _IsBalance(root->_right);
	}

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

		_Inorder(root->_left);
		cout << root->_kv.first << " " ;
		_Inorder(root->_right);

	}

	Node* _root = nullptr;
};

5. AVL树的性能

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

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

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

相关文章

Day967.团队拓扑学 -遗留系统现代化实战

团队拓扑学 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于团队拓扑学的内容。 看看最近这几年来新诞生的组织结构模型——团队拓扑学&#xff08;Team Topologies&#xff09;。 一、团队拓扑 尽管组件团队、特性团队和 Spotify 模型&#xff0c;都为团队的组成提…

JavaScript实现输入年份判断是否为闰年的代码

以下为实现输入年份判断是否为闰年的程序代码和运行截图 目录 前言 一、输入年份判断是否为闰年 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据题目要…

阿里云 aliplayer 加密的视频 key解密解密下载过程实现

第一步&#xff1a;打开开发者工具 打开需要下载的视频链接&#xff0c;按F12打开开发者工具&#xff0c;然后强制刷新&#xff08;ctrlf5&#xff09; 第二步&#xff1a;定位key加密 内存搜索&#xff0c;关键词&#xff1a;_sce_dlgtqred 进入第二个结果&#xff1a;https…

Map在循环中修改自己的key与value

Map在循环中修改自己的key与value 1.解决方案2.深入了解 1.解决方案 使用ConcurrentHashMap package com.company.newtest;import java.util.*; import java.util.concurrent.ConcurrentHashMap;public class test30 {public static void main(String[] args) {Map<String…

【Linux】进程信号详解(一)信号概念信号产生

文章目录 前言信号概念信号入门1.查看所有信号2.信号处理常见方式3. 发送信号过程信号是谁发送的&#xff1f; 信号产生介绍signal函数来捕捉进程1.通过键盘产生例子&#xff1a;Core Dump核心转储 2.程序出现异常&#xff0c;导致收到信号空指针异常浮点数异常 3. 调用系统函数…

1_1torch基础知识

1、torch安装 pytorch cuda版本下载地址&#xff1a;https://download.pytorch.org/whl/torch_stable.html 其中先看官网安装torch需要的cuda版本&#xff0c;之后安装cuda版本&#xff0c;之后采用pip 下载对应的torch的gpu版本whl来进行安装。使用pip安装时如果是conda需要切…

【Linux】安装部署elasticsearch

安装 Java 在安装 Elasticsearch 之前&#xff0c;您需安装并配置好 JDK, 设置好环境变量 $JAVA_HOME。 众所周知&#xff0c;Elasticsearch 版本很多&#xff0c;不同的版本对 Java 的依赖也有所差别: Elasticsearch 5 需要 Java 8 以上版本&#xff1b;Elasticsearch 6.5 开…

旋转目标检测【1】如何设计深度学习模型

前言 平常的目标检测是平行的矩形框&#xff0c;“方方正正”的&#xff1b;但对于一些特殊场景&#xff08;遥感&#xff09;&#xff0c;需要倾斜的框&#xff0c;才能更好贴近物体&#xff0c;旋转目标检测来啦~ 一、如何定义旋转框 常见的水平框参数表达方式为&#xff0…

PMP项目管理-[第九章]资源管理

资源管理知识体系&#xff1a; 规划资源管理&#xff1a; 估算活动资源&#xff1a; 获取资源&#xff1a; 建设团队&#xff1a; 管理团队&#xff1a; 9.1 规划资源管理 定义&#xff1a;定义如何估算、获取、管理和利用团队以及实物资源的过程 作用&#xff1a;根据项目类型…

Azure Data Lake Storage Gen2 简介

Azure Data Lake Storage Gen2 基于 Azure Blob 存储构建&#xff0c;是一套用于大数据分析的功能。 Azure Data Lake Storage Gen1 和 Azure Blob Storage 的功能在 Data Lake Storage Gen2 中组合在一起。例如&#xff0c;Data Lake Storage Gen2 提供规模、文件级安全性和文…

Cesium入门之三:隐藏Cesium初始化页面小部件的两种方法

上一级我们实现了第一个三维地球&#xff0c;但是在这个页面上有很多小部件&#xff0c;我们不想让其显示&#xff0c;应该如何设置呢&#xff1f;这一节我们通过两种方式来隐藏小部件 方法1&#xff1a;通过js代码实现 在js代码中&#xff0c;通过在new Cesium.Viewer(conta…

算法之路--直接插入排序算法

在介绍插入排序算法之前&#xff0c;先举证一个我们都熟悉不过的例子即可理解插入排序。我们在打牌的时候&#xff0c;由于每次抽到的牌大小不一&#xff0c;为了在出牌时了解自己手里都还剩什么牌型&#xff0c;所以每次对抽到的新牌都会做一个排序&#xff0c;怎么比较呢&…

AWS 中的另外一种远程工具 AWS Session Manager

作者&#xff1a;SRE运维博客 博客地址&#xff1a;https://www.cnsre.cn/ 文章地址&#xff1a;https://www.cnsre.cn/posts/230129126154/ 相关话题&#xff1a;https://www.cnsre.cn/tags/aws/ 背景需求 因为项目的安全性。为了避免项目的服务器暴露在公网中。很多时候我们…

设计原则之【迪米特法则】,非礼勿近

文章目录 一、什么是迪米特法则1、理解迪米特法则2、如何理解“高内聚、松耦合”&#xff1f; 二、实例1、实例12、实例2 一、什么是迪米特法则 迪米特原则&#xff08;Law of Demeter LoD&#xff09;是指一个对象应该对其他对象保持最少的了解&#xff0c;又叫最少知道原则&…

支付系统设计三:渠道网关设计01-总览

文章目录 前言一、开发框架二、E-R图三、管理后台配置四、运行时执行流程五、屏蔽渠道差异总结 前言 在《支付系统设计一&#xff1a;支付系统产品化》文章中&#xff0c;我们知道支付渠道网关主要具有以下功能&#xff1a; 统一支付出口&#xff0c;提供丰富的支付工具原子能…

详解:扫雷游戏的实现

扫雷游戏的实现 设置雷排查雷标记雷打印棋盘初始化棋盘获得雷的个数扩展区域test.c的实现meni.c的实现meni.h的实现 铁汁们&#xff0c;今天给大家分享一篇扫雷游戏的实现&#xff0c;来吧&#xff0c;开造⛳️ 1.需要存储雷的信息&#xff0c;创建二维数组来存储数据信息&…

gateway的使用

什么是Spring Cloud Gateway 网关作为流量的入口&#xff0c;常用的功能包括路由转发&#xff0c;权限校验&#xff0c;限流等。 Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架&#xff0c;定位于取代 Netflix Zuul。相比 Zuul 来说&#xff0c;Spring Cloud …

第1章 量化设计与分析基础

1.1 引言 如今一台价格不足500美元的手机&#xff0c;性能便堪比1993年世界上最快的售价5000万美元的计算机&#xff0c;这种快速发展既得益于计算机制造技术的发展&#xff0c;也得益于计算机设计的创新。 纵观计算机发展的历史&#xff0c;技术一直在稳定地提升&#xff0c…

【COT】Chain-of-Thought Prompting Elicits Reasoning in Large Language Models

文章目录 主要解决什么问题采用什么方法实验分析与结果消融实验Commonsense ReasoningSymbolic Reasoning 问题与展望 Chain-of-Thought Prompting Elicits Reasoning in Large Language Models 主要解决什么问题 大语言模型越来越大&#xff0c;效果越来越好。但是在一些具有…

Sui Builder House首尔站|主网上线后首次亮相

Sui Builder House提供与全球Sui构建者会面、合作并学习Sui平台前沿技术的机会。Sui基金会计划将于2023年在全球12个城市举办Sui Builder House。截止目前&#xff0c;已成功在美国丹佛市、越南胡志明市和中国香港举办三场Builder House活动。 Sui Builder House首尔站将于6月…