AVL树性质和实现

AVL树

AVL是两名俄罗斯数学家的名字,以此纪念

与二叉搜索树的区别

AVL树在二叉搜索树的基础上增加了新的限制:需要时刻保证每个树中每个结点的左右子树高度之差的绝对值不超过1

因此,当向树中插入新结点后,即可降低树的高度,从而减少平均搜索长度

平衡因子

为了能方便处理平衡二叉搜索树的限制条件,通常会引入平衡因子的概念

某一节点的平衡因子=其右子树高度-其左子树高度

在AVL树中,并不是一定需要平衡因子的,有些代码的AVL树就没有平衡因子。

这里引入平衡因子只是更方便的去判断树是否平衡了

AVL树的效率推算

image-20231013200846180

我们知道树的增删查改的效率是与树的高度有关的

假如AVL树是满二叉树,此时:2h-1=N

假如AVL树不是满二叉树,设最底层的节点个数为X,此时:2h-X=N。X的范围为[1,最后一层结点树-1]

此时上述两种情况都可以得出树的高度的数量级为logN,因此增删查改的时间复杂度为O(logN)

AVL树的节点设计

首先,与二叉搜索树相比,每个节点多了一个平衡因子(balance factor)

其次,为了满足一定需求,还多了个_parent指针,指向节点的父亲

template<class K,class V>
struct AVLTreeNode
{
    pair<K, V> _kv;
	AVLTreeNode<K,V>* _left;
	AVLTreeNode<K,V>* _right;
	AVLTreeNode<K,V>* _parent;
	int _bf;//平衡因子

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

这是三叉链image-20231018232522035

AVL树的框架设计

template<class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
    //……
private:
	Node* _root=nullptr;
};

AVL树的插入

在面试时几乎不会让你手撕AVL树的插入。重要的是了解思想。

但让你手撕一个旋转是有可能的

插入的逻辑

AVL树是在二叉搜索树的基础上引入了平衡因子,因此AVL树的插入分为两个步骤:

  • 按照二叉搜索树的方式插入新节点(多了个链接_parent
  • 插入后,更新节点的平衡因子

首先注意:新插入的节点只会对其父亲和其祖先的平衡因子造成影响

插入节点的_parent的平衡因子是一定需要调整的;在插入之前其_parent的平衡因子有三个可能值1、-1、0,插入节点后:

  • 如果插入到_parent的左侧,则_parent的平衡因子–即可
  • 如果插入到_parent的右侧,则_parent的平衡因子++即可

此时:_parent的平衡因子可能有三大种情况:

  • 如果_parent的平衡因子为0,说明插入之前的平衡因子为正负1,插入后被调整成0,满足AVL树的性质,插入成功,插入结束
  • 如果_parent的平衡因子为±1,说明插入之前的平衡因子为0,此时树的高度增加,那么就需要继续向上更新祖先的平衡因子,直至某一祖先的平衡因子为0或者更新到根节点,才算插入成功,停止更新,插入结束。
  • 如果_parent的平衡因子为±2,此时违反了AVL树的性质,需要进行旋转处理。处理完成则算插入成功,插入结束
更新平衡因子

最坏的情况是一直更新到根,如下图:image-20231017154509542

因此在更新平衡因子时,我们的循环条件为:while(parent)

因为只有根节点的_parent为空。所以当更新完根节点的平衡因子后,循环结束

while (parent)
{
    if (cur == parent->_left)//节点插入在父亲左边
    {
        parent->_bf--;
    }
    else if (cur == parent->_right)//节点插入在父亲右边
    {
        parent->_bf++;
    }

    //进一步判断祖先节点的平衡因子
    if (parent->_bf == 0)//父亲的平衡因子为0,循环结束
    {
        break;
    }
    else if (parent->_bf == 1 || parent->_bf == -1)//父亲的平衡因子为1或-1,则需要继续向上调整
    {
        cur = parent;
        parent = cur->_parent;
    }
    else if (parent->_bf == 2 || parent->_bf == -2)//父亲的平衡因子为2或-2,则需要旋转,且选择完后树一定平衡,故结束循环
    {
        //左单旋
        if (parent->_bf == 2 && cur->_bf == 1)
        {
            RotateL(parent);
        }
        //右单旋
        if (parent->_bf == -2 && cur->_bf == -1)
        {
            RotateR(parent);
        }
        //右左双旋
        if (parent->_bf == 2 && cur->_bf == -1)
        {
            RotateRL(parent);
        }
        //左右双旋
        if (parent->_bf == -2 &&	 cur->_bf == 1)
        {
            RotateLR(parent);
        }

        break;
    }
    else//其他情况,此时说明在插入之前树就已经不是平衡树了
    {
        assert(false);
    }
}

最后一个else中的assert(false)看似是无用的,因为parent不可能是绝对值大于2的。但是代码都是人写的,不可排除一开始的树就是有问题的。因此这句代码很重要

AVL树的旋转

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

  • 新节点插入较高左子树的左侧–左左:右单旋
  • 新节点插入较高右子树的右侧–右右:左单旋
  • 新节点插入较高左子树的右侧—左右:先左单旋再右单旋(左右双旋)
  • 新节点插入较高右子树的左侧—右左:先右单旋再左单旋(右左双旋)

何时使用何种旋转:

假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑:

  1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR

    • 当pSubR的平衡因子为1时,执行左单旋

    • 当pSubR的平衡因子为-1时,执行右左双旋

  2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL

    • 当pSubL的平衡因子为-1是,执行右单旋

    • 当pSubL的平衡因子为1时,执行左右双旋

旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新

右单旋

image-20231019154016105

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

在旋转过程中,有以下几种情况需要考虑:

  1. cur节点的右孩子可能存在,也可能不存在

  2. parent可能是根节点,也可能是子树

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

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

这两点是所有旋转情况都需要考虑的

右单旋的核心操作:把cur的右孩子给到parent的左,再把parent给到cur的右

代码:

void RotateR(Node* parent)//parent的平衡因子绝对值为2
{
    Node* cur = parent->_left;
    Node* curright = cur->_right;

    parent->_left = curright;//把cur的右孩子给到parent的左
    if (curright)//如果cur的右孩子存在,则更新其父亲为parent
    {
        curright->_parent = parent;
    }
    cur->_right = parent;//再把parent给到cur的右

    Node* ppnode = parent->_parent;//记录parent的原父亲节点,用于对cur的父亲进行更新
    parent->_parent = cur;//更新parent的父亲

    //对cur的父亲进行更新
    if (parent == _root)//parent即为根节点
    {
        _root = cur;
        cur->_parent = nullptr;//那么cur作为新的根,其父亲为空
    }
    else//parent是子树
    {
        if (ppnode->_left == parent)
        {
            ppnode->_left = cur;
        }
        else
        {
            ppnode->_right = cur;
        }
        cur->_parent = ppnode;
    }

    cur->_bf = parent->_bf = 0;//更新完后,平衡因子一定为0
}
左单旋

image-20231019153929844

左单旋的核心操作与右单旋的核心操作正好是镜像的

左单旋的核心操作:把cur的左给parent的右,再把parent给到cur的左

代码:

void RotateL(Node* parent)
{
    Node* cur = parent->_right;
    Node* curleft = cur->_left;

    parent->_right = curleft;//把cur的左给parent的右
    if (curleft)//如果cur的左存在,则更新其父亲
    {
        curleft->_parent = parent;
    } 
    cur->_left = parent;//再把parent给到cur的左

    Node* ppnode = parent->_parent;//记录parent的原父亲节点,用于对cur的父亲进行更新

    parent->_parent = cur;

    //对cur的父亲进行更新
    if (parent == _root)
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else//说明左旋的部分只是某棵树的局部
    {
        if (ppnode->_left == parent)
        {
            ppnode->_left = cur;
        }
        else
        {
            ppnode->_right = cur;
        }
        cur->_parent = ppnode;
    }

    cur->_bf = parent->_bf = 0;
}
右左双旋

image-20231019155022899

先对90进行右单旋,再对30进行左单旋

两次旋转。第一次旋转使其变成单纯的右边高,第二次旋转对应的左单旋

双旋代码难的不是旋转,而是平衡因子的更新

从图中可以看到:最终60成了根,60的左孩子给了parent的右边,60的右孩子给了cur的左边

因此平衡因子的更新分为两种情况:

  • h == 0:那么60则作为新插入的节点,此时60的bf == 0,那么parent和cur的bf也一定为0
  • h>=0:
    • 假如新结点插入在60的左边,即60的bf == -1。那么最终parent的bf == 0,cur的bf == 1
    • 假如新结点插入在60的右边,即60的bf == 1。那么最终parent的bf == -1,cur的bf == 0
    • 而60作为根节点最终bf一定为0

代码:

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

    //先右旋再左旋
    RotateR(cur);
    RotateL(parent);

    //更新平衡因子
    if (bf == 0)
    {
        parent->_bf = 0;
        cur->_bf = 0;
        curleft->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = -1;
        cur->_bf = 0;
        curleft->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 0;
        cur->_bf = 1;
        curleft->_bf = 0;
    }
    else
    {
        assert(false);
    }
}
左右双旋

image-20231019160543333

与右左双旋是镜像的,不再赘述

代码:

void RotateLR(Node* parent)
{
    Node* cur = parent->_left;
    Node* curright = cur->_right;
    int bf = curright->_bf;

    //先左旋再右旋
    RotateL(cur);
    RotateR(parent);

    //更新平衡因子
    if (bf == 0)
    {
        parent->_bf = 0;
        cur->_bf = 0;
        curright->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = 0;
        cur->_bf = -1;
        curright->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 1;
        cur->_bf = 0;
        curright->_bf = 0;
    }
    else
    {
        assert(false);
    }
}
AVL树插入的完整代码
bool Insert(const pair<K, V>& kv)
{
    //先帮助插入节点找到正确位置
    if (_root == nullptr)//树为空
    {
        _root = new Node(kv);
        return true;
    }

    Node* cur = _root;
    Node* parent = nullptr;
    while (cur)
    {
        //插入节点大于根节点
        if (kv.first > cur->_kv.first)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (kv.first < cur->_kv.first)//插入节点小于根节点
        {
            parent = cur;
            cur = cur->_left;
        }
        else//所插入节点已经存在
        {
            return false;
        }
    }

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

    cur->_parent = parent;

    //控制平衡因子
    while (parent)//
    {
        if (cur == parent->_left)//节点插入在父亲左边
        {
            parent->_bf--;
        }
        else if (cur == parent->_right)//节点插入在父亲右边
        {
            parent->_bf++;
        }

        //进一步判断祖先节点的平衡因子
        if (parent->_bf == 0)//父亲的平衡因子为0,循环结束
        {
            break;
        }
        else if (parent->_bf == 1 || parent->_bf == -1)//父亲的平衡因子为1或-1,则需要继续向上调整
        {
            cur = parent;
            parent = cur->_parent;
        }
        else if (parent->_bf == 2 || parent->_bf == -2)//父亲的平衡因子为2或-2,则需要旋转,且选择完后树一定平衡,故结束循环
        {
            //左单旋
            if (parent->_bf == 2 && cur->_bf == 1)
            {
                RotateL(parent);
            }
            //右单旋
            if (parent->_bf == -2 && cur->_bf == -1)
            {
                RotateR(parent);
            }
            //右左双旋
            if (parent->_bf == 2 && cur->_bf == -1)
            {
                RotateRL(parent);
            }
            //左右双旋
            if (parent->_bf == -2 &&	 cur->_bf == 1)
            {
                RotateLR(parent);
            }

            break;
        }
        else//其他情况,此时说明在插入之前树就已经不是平衡树了
        {
            assert(false);
        }
    }

}

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

    parent->_right = curleft;
    if (curleft)
    {
        curleft->_parent = parent;
    } 
    cur->_left = parent;

    Node* ppnode = parent->_parent;//记录parent的原父亲节点

    parent->_parent = cur;

    //对cur的父亲进行更新
    if (parent == _root)
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else//说明左旋的部分只是某棵树的局部
    {
        if (ppnode->_left == parent)
        {
            ppnode->_left = cur;
        }
        else
        {
            ppnode->_right = cur;
        }
        cur->_parent = ppnode;
    }

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

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

    //让右节点接到parent的左边,再将parent接到cur的右边
    parent->_left = curright;
    if (curright)
    {
        curright->_parent = parent;
    }
    cur->_right = parent;

    Node* ppnode = parent->_parent;//记录parent的原父亲节点
    parent->_parent = cur;

    //对cur的父亲进行更新
    if (parent == _root)
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else//说明右旋的部分只是某棵树的局部
    {
        if (ppnode->_left == parent)
        {
            ppnode->_left = cur;
        }
        else
        {
            ppnode->_right = cur;
        }
        cur->_parent = ppnode;
    }

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

//右左双旋
void RotateRL(Node* parent)
{
    Node* cur = parent->_right;
    Node* curleft = cur->_left;
    int bf = curleft->_bf;

    //先右旋再左旋
    RotateR(cur);
    RotateL(parent);

    //更新平衡因子
    if (bf == 0)
    {
        parent->_bf = 0;
        cur->_bf = 0;
        curleft->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = -1;
        cur->_bf = 0;
        curleft->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 0;
        cur->_bf = 1;
        curleft->_bf = 0;
    }
    else
    {
        assert(false);
    }
}

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

    //先左旋再右旋
    RotateL(cur);
    RotateR(parent);

    //更新平衡因子
    if (bf == 0)
    {
        parent->_bf = 0;
        cur->_bf = 0;
        curright->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = 1;
        cur->_bf = -1;
        curright->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 1;
        cur->_bf = 0;
        curright->_bf = 0;
    }
    else
    {
        assert(false);
    }
}

AVL树的验证

验证AVL树分为两步:

  • 验证其为二叉搜索树:如果中序遍历历可得到一个有序的序列,就说明为二叉搜索树(这里就不详细介绍了,详情可以看二叉搜索树那里)

  • 验证其为平衡树:

    • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
    • 节点的平衡因子是否计算正确
    int TreeHeight(Node* root)
    {
        if (root == nullptr)
            return 0;
    
        int leftHeight = TreeHeight(root->_left);
        int rightHeight = TreeHeight(root->_right);
    
        return rightHeight > leftHeight ? rightHeight + 1: leftHeight + 1;
    }
    
    bool IsBalance()//两个IsBalance构成重载
    {
        return IsBalance(_root);
    }
    
    bool IsBalance(Node* root)
    {
        if (root == nullptr)
            return true;
    
        int leftHeight = TreeHeight(root->_left);
        int rightHeight = TreeHeight(root->_right);
    
        if (rightHeight - leftHeight != root->_bf)
        {
            cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
            return false;
        }
    
        return abs(rightHeight - leftHeight) < 2
            && IsBalance(root->_left)
            && IsBalance(root->_right);
    }
    

    这里给出一个调试技巧:

    image-20231018182647226假如我运行出现了下面的情况

    image-20231018182657070

    我们现在知道在插入11的时候出了问题,那么就可以针对e=11时进行调试

    但如果现在有100个值,我在第99个值才出现问题,那是不是需要按F10按99次呢?

    两种方法,:

    • 一个是利用条件断点

      image-20231018183053645

    • 还一个是我们自己写代码让它停到想停的地方

      image-20231018183141199

      这里的int x=0;是随便写的,目的是能让断点在这里停下来。因为断点打在空行上是停不住的

      当然,我停在断点处不是为了调int x=0;,而是为了调下面

AVL树的删除

了解即可

image-20231019171629540

总结

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

闪客网盘系统源码,已测试对接腾讯COS及本地和支付(支持限速+按时收费+文件分享+可对接易支付)- 修复版

正文概述 资源入口 支持对文件下载限速 对接易支付 推广赚钱啥的功能 源码非常的好 支持腾讯cos 阿里云cos 本地储存 远程存储 源码仅支持服务器搭建 php7.2 伪静态thinkphp 运行目录public 导入数据库 修改config目录下的database.php数据库信息 后台地址&#xff1a; 域名/ad…

如何在CentOS上安装SQL Server数据库并通过内网穿透工具实现远程访问

文章目录 前言1. 安装sql server2. 局域网测试连接3. 安装cpolar内网穿透4. 将sqlserver映射到公网5. 公网远程连接6.固定连接公网地址7.使用固定公网地址连接 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;…

CLIP Surgery论文阅读

CLIP Surgery for Better Explainability with Enhancement in Open-Vocabulary Tasks&#xff08;CVPR2023&#xff09; M norm ⁡ ( resize ⁡ ( reshape ⁡ ( F i ˉ ∥ F i ‾ ∥ 2 ⋅ ( F t ∥ F t ‾ ∥ 2 ) ⊤ ) ) ) M\operatorname{norm}\left(\operatorname{resize}\…

文件包含 [ZJCTF 2019]NiZhuanSiWei1

打开题目 代码审计 if(isset($text)&&(file_get_contents($text,r)"welcome to the zjctf")){ 首先isset函数检查text参数是否存在且不为空 用file_get_contents函数读取text制定的文件内容并与welcome to the zjctf进行强比较 echo "<br><h…

【C++】类型转换【4中类型转换】

目录 1. C语言中的类型转换 2. C的四种类型转换 2.1 static_cast 3.2 reinterpret_cast 3.3 const_cast 3.4 dynamic_cast 3. explict 4. RTTI&#xff08;了解&#xff09; 1. C语言中的类型转换 在 C 语言中&#xff0c;如果 赋值运算符左右两侧类型不同&#xff0…

如何用Excel做最小二乘法②

因为在Excel里面做最小二乘法是需要用到LINEST函数的&#xff0c;所以如果不知道怎么对数据进行最小二乘法时&#xff0c;就应该研究一下LINEST函数。 LINEST 函数语法 LINEST(known_ys, [known_xs], [const], [stats]) known_ys (必须) 因变量&#xff0c;单行/单列known_xs…

xxl-job 原理

一、xxl-job 架构设计 总体分两个部分&#xff1a; 调度中心&#xff1a;负责管理调度信息&#xff0c;按照调度配置发出调度请求&#xff0c;自身不承担业务代码。调度系统和任务解耦&#xff0c;提高了系统可用性和稳定性。通调度性能不在受限于任务模块。执行器&#xff1a…

Mysql配置主从复制-GTID模式

目录 主从复制 主从复制的定义 主从复制的原理 主从复制的优势 主从复制的形式 主从复制的模式 主从复制的类型 GTID模式 GTID的概念 GTID的优势 GTID的原理 GTID的配置 Mysql主服务器 ​编辑 Mysql从服务器 ​编辑 主从复制 主从复制的定义 是指把数据从一个…

Docker安装Mysql

Docker常用指令&#xff1a; docker search 镜像名&#xff1a;寻找镜像 docker pull 镜像名&#xff1a;拉去镜像 docker images :查看拥有镜像 docker ps :查看正在运行容器 docker pa -a &#xff1a;查看所有容器&#xff08;包含运行中的和停止的&#xff09; dock…

案例 - 拖拽上传文件,生成缩略图

直接看效果 实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>拖拽上传文件</title>&l…

Python 实现动态动画心形图

在抖音上刷到其他人用 matlab 实现的一个动态心形图&#xff0c;就想也用 Python 实现&#xff0c;摸索了两种实现方式&#xff0c;效果如下&#xff1a; 方法一&#xff1a; 利用循环&#xff0c;结合 pyplot 的 pause 与 clf 参数实现图像的动态刷新 import matplotlib.p…

【漏洞复现】weblogic-10.3.6-‘wls-wsat‘-XMLDecoder反序列化(CVE-2017-10271)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描nacsweblogicScanner3、漏洞验证 说明内容漏洞编号CVE-2017-10271漏洞名称Weblogic < 10.3.…

【Unity细节】VS不能附加到Unity程序中解决方法大全

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

物联网AI MicroPython学习之语法 二进制与ASCII转换

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; ubinascii 介绍 ubinascii模块实现了二进制数据与各种ASCII编码之间的转换。 接口说明 a2b_base64 - 解码base64编码的数据 函数原型&#xff1a;ubinascii.a2b_base64(data)注意事项&#xff1a; 在解码…

GPT-4-Turbo的128K长度上下文性能如何?超过73K Tokens的数据支持依然不太好!

本文原文来自DataLearnerAI官方网站&#xff1a;GPT-4-Turbo的128K长度上下文性能如何&#xff1f;超过73K Tokens的数据支持依然不太好&#xff01; | 数据学习者官方网站(Datalearner)https://www.datalearner.com/blog/1051699526438975 GPT-4 Turbo是OpenAI最新发布的号称…

【MongoDB】索引 – 文本索引

一、准备工作 这里准备一些数据 db.books.insertMany([{_id: 1, name: "Java", description: "java 入门图书", translation: [{ language: "english", description: "java basic book" }]},{_id: 2, name: "C", descript…

Vue2 基础语法

学习视频 bilibili 动力节点 老杜Vue视频教程&#xff0c;Vue2 Vue3实战精讲&#xff0c;一套通关vue【动力节点】 Vue2学习笔记 Vue程序初体验1 实例与容器只能是一对一的关系2 插值语法3 指令语法3.1 v-once 只渲染元素和组件一次3.2 条件渲染3.2.1 v-if 表达式的值决定标签是…

踩坑记录一

先呼自己两耳巴 临床采集的增强CT数据&#xff0c;有时候是同时采集了静脉期和动脉期。就会导致图像多一分如下&#xff1a; 但是勾画的时候&#xff0c;是以下面的期相进行标注的。所以在训练分割&#xff0c;对于这种案例&#xff0c;他识别到了在上面一个期相的目标位置&am…

利用Ansible实现批量Linux服务器安全配置

1.摘要 在上一篇<<初步利用Ansible实现批量服务器自动化管理>>文章中, 我初步实现了通过编写清单和剧本来实现多台服务器的自动化管理,在本章节中, 我将利用Ansible的剧本来实现更实用、更复杂一点的功能, 主要功能包括三个:1.同时在三台服务器中增加IP访问控制,只…

风力发电场集中监控系统解决方案

安科瑞 崔丽洁 作为清洁能源之一&#xff0c;风力发电场近几年装机容量快速增长。8月17日&#xff0c;国家能源局发布1-7月份全国电力工业统计数据。截至7月底&#xff0c;全国累计发电装机容量约27.4亿千瓦&#xff0c;同比增长11.5%。其中&#xff0c;太阳能发电装机容量约4.…