C++版【AVL树的模拟实现】

前言

        在学习AVL树的底层之前,先回顾一下二叉搜索树,我们知道二叉搜索树在极端场景是会形成单支树的,如下图:

        在退化成单支树后,查找的效率就会降到O(n),所以为了解决退化成单支树的情况,AVL树就诞生了,在学习AVL树的模拟实现之前,先要明白AVL是如何避免单支树的产生的。

一、AVL树解决单支树问题的原理

        在搜索某个节点时,本质上是要搜索这个节点的高度次,但当形成单支树或接近单支树,就会使时间复杂度提升至O(n),所以为了提高搜索的效率,就要把高度降低。这也是AVL树解决单支树问题的关键!

原理:

        当向二叉搜索树中插入新结点后,必须保证每个结点的左右子树高度之差的绝对值不超过1即可降低树的高度,从而减少平均搜索长度

        所以根据原理,我们可以设计一个简单的AVL树,只需要在树的节点中增添一个成员变量来记录每个节点的左右子树的高度差,在插入节点时,来判断节点的左右子树高度差的绝对值是否超过1,再进行后续操作。

二、AVL树的模拟实现(源代码+解析)

        本次只实现中序遍历为升序,无重复值,且无删除操作的AVL树   (后续会完善功能)

1. 创造AVL树节点的类(AVLTreeNode)

        创造树节点的类时,需要考虑里面应该包含什么成员

成员变量:

指向左右子树的节点指针【 _left、 _right 】
指向父亲节点的指针(方便找父亲)【 _parent 】

记录每个节点左右子树高度差

平衡因子(balance factor)

【 _bf 】
该节点包含的数据【 _data 】

成员函数:

  • 构造函数
//我们在创造AVL树之前,先要把AVL树节点创造出来,要明确的是在AVLTreeNode类外
//我们还是需要使用到节点里面的内容,所以这里定义为struct类,默认权限为public
template <class T>   //模版,T可以为任何类型
struct AVLTreeNode
{AVLTreeNode(const T& data): _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _data(data){}AVLTreeNode<T>* _left;AVLTreeNode<T>* _right;AVLTreeNode<T>* _parent;int _bf;    //用_bf来记录树节点的左右子树高度差,bf = high(right) - high(left)T _data;
};

2. 创造AVL树的类(AVLTree)

成员变量:

  • 根节点指针 AVLTreeNode* 

成员函数:

  • 插入操作(intsert)
  • 验证是否为AVL树
  • 其余函数请自行添加
//创造完AVLTreeNode后,可以开始创造AVLTree
//需要注意的是,我们在日常使用AVL树时,只需要使用其成员函数,并不需要用成员变量
//所以设定为class类,成员函数权限为public,成员函数权限为private
template <class T>
class AVLTree
{
public:
成员函数//插入bool insert(const T& data);//判断是否为AVLbool _IsBalance(Node* root, int& height);
private:typedef AVLTreeNode<T> Node;Node* _root = nullptr; //等价于在初始化列表初始化,在没插入之前,默认为空树
};

3. 成员函数:插入【insert】

        在插入时,其他步骤均与二叉搜索树一致,都是小的去左子树,大的去右子树,唯一多出来的步骤是:更新平衡因子

4. 更新平衡因子【重点】

因为我们AVL树的规定为 每个节点的左右子树高度差不能超过1

那bf 只能为 -1、0、1;

所以在插入之前,parent节点的平衡因子值只能为-1、0、1三种情况

而在更新的过程中,因为bf = high(right) - high(left)

所以当在parent的左子树插入时,默认是增加左子树的高度,需要--bf;反之是增加右子树的高度,++bf;

在parent的平衡因子更新之后,bf结果只能为-2,-1,0,1,2这五种情况

我们后续就要根据这五种情况来判断,是否更新完成,更新完成直接退出,反之继续向上更新

(这里的更新是否完成是指:是否会影响上层节点的平衡因子bf的值,不影响就更新完成,影响就继续向上更新)

(1)更新后:parent->_bf == 0

        当更新完之后parent的平衡因子为0时,我们认为更新之前的parent平衡因子只有1或-1两种情况;

        我们通过抽象图可以看出,不会影响parent以上的节点的平衡因子

        所以在parent->_bf == 0时,平衡因子的更新结束!

结论:当parent->_bf == 0时,不会影响上层节点的bf,不需要继续更新,更新结束!

(2)更新后:parent->_bf == -1

        下图,我将更新之前,分为三种情况(有很多情况,不一列举)

        因为无论怎么插入节点,已有节点的平衡因子,也无非是-1,0,1;所以我将parent的父亲节点的平衡因子分为0,-1,1三种情况;

        以便探索在更新parent的平衡因子之后,是否还需要继续向上更新

        更新之后是否还满足AVL的规则(  | bf |   ≤  1  )

        所以我们通过图中可以看出,无论是哪一种情况,当parent的bf == -1时,都会影响上层的节点的平衡因子,都要继续向上更新

结论:当parent->_bf == -1时,需要继续向上更新

(3)更新后:parent->_bf == 1

        我们发现,当更新后parent的平衡因子bf == 1时,也会影响上层的节点的平衡因子,所以仍需要继续向上更新

结论:当parent->_bf == 1时,需要继续向上更新!

通过(1)(2)可以得到当parent的平衡因子 bf == 正负1 时,都需要继续向上更新

(4)更新后:parent->_bf == 2 或 parent->_bf == -2

        当更新后的平衡因子超过1的时候,已经违反AVL树的规则,此时要进行旋转

        旋转的目的就是为了让高度降低,那如何来旋转请看下图:

通过图中可以看出在违背规则时,一共有4种不同的旋转方式可以解决

第一种:左旋转

旋转之后,不会影响上层节点的平衡因子,更新结束

第二种:右旋转

旋转之后,不会影响上层节点的平衡因子,更新结束

第三种:先左旋转,后右旋转

        这里需要进行两次旋转,因为最开始是右边高,要左旋,后来是左边高,再右旋

        但是在平衡因子的更新就需要分情况讨论了

        我们在cur的子树的左右侧分别插入,会导致最后的平衡因子为不同值,而最开始导致不同的原因是cright的bf的不同,所以在旋转结束后,更新平衡因子时以cright的bf不同值做分情况讨论

情况1,当h不存在,cright->_bf == 0

情况2,h存在,且在右侧插入,cright->_bf == 1

情况3,h存在且在左子树插入,cright->_bf == -1;

第四种:先右旋转,后左旋转

        这里与上面的采用统一思想

情况1:当h == 0时,插入的节点cleft->_bf == 0

情况2:在cur的左子树的左侧插入,cleft->_bf == -1

情况3:在cur的左子树的右侧插入,cleft->_bf == 1

【insert代码实现】

    bool insert(const T& data){//这里插入规则与二叉搜索树一致//空树if(_root == nullptr){_root = new Node(data);//创建失败if(_root == nullptr){perror("new error");return false;}return true;}//不为空树else{Node* cur = _root;  //遍历找到插入的位置Node* parent = _root; //记录插入位置的父亲节点位置while(cur){if(cur->_data > data)  //插入值小于节点值:去左子树插入{parent = cur;cur = cur->_left;}else if(cur->_data < data) //插入值大于节点值:去右子树插入{parent = cur;cur = cur->_right;}else //插入值等于节点值:插入失败{perror("insert a same data");return false;}}//找到插入位置了,创造新节点Node* newnode = new Node(data);if(parent->_data > data) //新节点的值比父亲的值小,插入父亲的左侧{parent->_left = newnode;}else                     //新节点的值比父亲的值大,插入父亲的右侧{parent->_right = newnode;}newnode->_parent = parent;//更新平衡因子bfUpdateBF(newnode, parent);return true;}}
//左旋转void RotationL(Node* parent, Node* cur){Node* cleft = cur->_left;parent->_right = cleft;//cleft不为空,则需要链接其父亲,为空,则不用链接if(cleft)cleft->_parent = parent;cur->_left = parent;//Node* pparent = parent->_parent;parent->_parent = cur;if(parent == _root) //当parent就是根,旋转之后,cur就为根{_root = cur;cur->_parent = nullptr;}else //如果parent不是根,则需要判断parent在其父亲的左节点还是右节点{if(pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}cur->_parent = pparent;}//旋转之后,parent和cur的平衡因子都为0,不影响上层节点parent->_bf = 0;cur->_bf = 0;}//右旋转void RotationR(Node* parent, Node* cur){Node* cright = cur->_right;parent->_left = cright;//cright存在,则链接其父亲到parent,不存在,不链接if(cright)cright->_parent = parent;cur->_right = parent;Node* pparent = parent->_parent;parent->_parent = cur;if(parent == _root) //当parent就是根,旋转之后,cur就为根{_root = cur;cur->_parent = nullptr;}else //如果parent不是根,则需要判断parent在其父亲的左节点还是右节点{if(pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}cur->_parent = pparent;}//旋转之后,parent和cur的平衡因子都为0,不影响上层节点parent->_bf = 0;cur->_bf = 0;}//左右旋转void RotationLR(Node* parent, Node* cur){Node* cright = cur->_right;int bf = cright->_bf;RotationL(cur, cright);RotationR(parent, cright);if(bf == 1){parent->_bf = 0;cright->_bf = 0;cur->_bf = -1;}else if(bf == -1){parent->_bf = 1;cur->_bf = 0;cright->_bf = 0;}else{parent->_bf = 0;cur->_bf = 0;cright->_bf = 0;}}//右左旋转void RotationRL(Node* parent, Node* cur){Node* cleft = cur->_left;int bf = cleft->_bf;RotationR(cur, cleft);RotationL(parent, cleft);if(bf == 1){parent->_bf = -1;cleft->_bf = 0;cur->_bf = 0;}else if(bf == -1){parent->_bf = 0;cur->_bf = 1;cleft->_bf = 0;}else{parent->_bf = 0;cur->_bf = 0;cleft->_bf = 0;}}//更新平衡因子void UpdateBF(Node*& cur, Node*& parent){while(parent){//我们在插入之前,parent的平衡因子可能为三种情况// 1、0、-1if(parent->_left == cur) //插入的节点在父亲的左边,bf--{--parent->_bf;}else //插入的节点在父亲的右边,bf++{++parent->_bf;}//更新完平衡因子之后,parent的bf值可能为:2,1,0,-1,-2if(parent->_bf == 0)//说明parent在插入之前的bf为1或-1,以parent为根的左右子树高度差1//新增节点之后,也并不会影响整颗树的bf,只会让以parent为根的左右子树高度差更新为0{break;}else if(parent->_bf == -1 || parent->_bf == 1)//说明parent在插入之前的bf == 0,是AVL树,但是插入之后,会影响整体的高度//所以要依次向上更新{cur = parent;parent = cur->_parent;}else if(parent->_bf == 2 || parent->_bf == -2)//parent的平衡因子超过1,破坏了AVL树的规则,需要进行旋转//但是有4种旋转的情况{//情况1:左旋转if(parent->_bf == 2 && cur->_bf == 1){RotationL(parent, cur);}//情况2:右左旋转else if(parent->_bf == 2 && cur->_bf == -1){RotationRL(parent, cur);}//情况3:右旋转else if(parent->_bf == -2 && cur->_bf == -1){RotationR(parent, cur);}//情况4:左右旋转else if(parent->_bf == -2 && cur->_bf == 1){RotationLR(parent, cur);}else;//旋转结束、不需要更新break;}else{//平衡因子有问题assert(false);}}}

5. 判断是否为AVL树

        要想验证我们上面插入之后,是否为AVL树,可以写一个函数去判断一下,根据AVL树的性质:任意一个节点的左右子树的高度差的绝对值不超过1,所以我们可以根据这一性质来编写代码:

//判断是否为AVL树bool IsBalance(){int height = 0;return _IsBalance(_root, height);}//判断是否是AVL树bool _IsBalance(Node* root, int& height){if (root == nullptr){height = 0;return true;}int leftHeight = 0, rightHeight = 0;if (!_IsBalance(root->_left, leftHeight)|| !_IsBalance(root->_right, rightHeight)){return false;}if (abs(rightHeight - leftHeight) >= 2){cout <<root->_data<<"不平衡" << endl;return false;}if (rightHeight - leftHeight != root->_bf){cout << root->_data <<"平衡因子异常" << endl;return false;}height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;return true;}

这里建议可以把不想向外部展露的函数放在private里,避免对自身调用时的影响

三、代码汇总

#include <iostream>
#include <cassert>
using namespace std;
//
//我们在创造AVL树之前,先要把AVL树节点创造出来,要明确的是在AVLTreeNode类外
//我们还是需要使用到节点里面的内容,所以这里定义为struct类,默认权限为public
template <class T>
struct AVLTreeNode
{AVLTreeNode(const T& data): _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _data(data){}AVLTreeNode<T>* _left;AVLTreeNode<T>* _right;AVLTreeNode<T>* _parent;int _bf;    //用_bf来记录树节点的左右子树高度差T _data;
};
///
//创造完AVLTreeNode后,可以开始创造AVLTree
//需要注意的是,我们在日常使用AVL树时,只需要使用其成员函数,并不需要用成员变量
//所以设定为class类,成员函数权限为public,成员函数权限为private
template <class T>
class AVLTree
{
public://成员函数//插入函数bool insert(const T& data){//这里插入规则与二叉搜索树一致//空树if(_root == nullptr){_root = new Node(data);//创建失败if(_root == nullptr){perror("new error");return false;}return true;}//不为空树else{Node* cur = _root;  //遍历找到插入的位置Node* parent = _root; //记录插入位置的父亲节点位置while(cur){if(cur->_data > data)  //插入值小于节点值:去左子树插入{parent = cur;cur = cur->_left;}else if(cur->_data < data) //插入值大于节点值:去右子树插入{parent = cur;cur = cur->_right;}else //插入值等于节点值:插入失败{perror("insert a same data");return false;}}//找到插入位置了,创造新节点Node* newnode = new Node(data);if(parent->_data > data) //新节点的值比父亲的值小,插入父亲的左侧{parent->_left = newnode;}else                     //新节点的值比父亲的值大,插入父亲的右侧{parent->_right = newnode;}newnode->_parent = parent;//更新平衡因子bfUpdateBF(newnode, parent);return true;}}//遍历void InOrder(){_InOrder(_root);}//判断是否为AVL树bool IsBalance(){int height = 0;return _IsBalance(_root, height);}
private:typedef AVLTreeNode<T> Node;Node* _root = nullptr;//以下成员函数不希望被外部看到,所以设为private//判断是否是AVL树bool _IsBalance(Node* root, int& height){if (root == nullptr){height = 0;return true;}int leftHeight = 0, rightHeight = 0;if (!_IsBalance(root->_left, leftHeight)|| !_IsBalance(root->_right, rightHeight)){return false;}if (abs(rightHeight - leftHeight) >= 2){cout <<root->_data<<"不平衡" << endl;return false;}if (rightHeight - leftHeight != root->_bf){cout << root->_data <<"平衡因子异常" << endl;return false;}height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;return true;}//遍历void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_data  << " ";_InOrder(root->_right);}//左旋转void RotationL(Node* parent, Node* cur){Node* cleft = cur->_left;parent->_right = cleft;//cleft不为空,则需要链接其父亲,为空,则不用链接if(cleft)cleft->_parent = parent;cur->_left = parent;//Node* pparent = parent->_parent;parent->_parent = cur;if(parent == _root) //当parent就是根,旋转之后,cur就为根{_root = cur;cur->_parent = nullptr;}else //如果parent不是根,则需要判断parent在其父亲的左节点还是右节点{if(pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}cur->_parent = pparent;}//旋转之后,parent和cur的平衡因子都为0,不影响上层节点parent->_bf = 0;cur->_bf = 0;}//右旋转void RotationR(Node* parent, Node* cur){Node* cright = cur->_right;parent->_left = cright;//cright存在,则链接其父亲到parent,不存在,不链接if(cright)cright->_parent = parent;cur->_right = parent;Node* pparent = parent->_parent;parent->_parent = cur;if(parent == _root) //当parent就是根,旋转之后,cur就为根{_root = cur;cur->_parent = nullptr;}else //如果parent不是根,则需要判断parent在其父亲的左节点还是右节点{if(pparent->_left == parent){pparent->_left = cur;}else{pparent->_right = cur;}cur->_parent = pparent;}//旋转之后,parent和cur的平衡因子都为0,不影响上层节点parent->_bf = 0;cur->_bf = 0;}//左右旋转void RotationLR(Node* parent, Node* cur){Node* cright = cur->_right;int bf = cright->_bf;RotationL(cur, cright);RotationR(parent, cright);if(bf == 1){parent->_bf = 0;cright->_bf = 0;cur->_bf = -1;}else if(bf == -1){parent->_bf = 1;cur->_bf = 0;cright->_bf = 0;}else{parent->_bf = 0;cur->_bf = 0;cright->_bf = 0;}}//右左旋转void RotationRL(Node* parent, Node* cur){Node* cleft = cur->_left;int bf = cleft->_bf;RotationR(cur, cleft);RotationL(parent, cleft);if(bf == 1){parent->_bf = -1;cleft->_bf = 0;cur->_bf = 0;}else if(bf == -1){parent->_bf = 0;cur->_bf = 1;cleft->_bf = 0;}else{parent->_bf = 0;cur->_bf = 0;cleft->_bf = 0;}}//更新平衡因子void UpdateBF(Node*& cur, Node*& parent){while(parent){//我们在插入之前,parent的平衡因子可能为三种情况// 1、0、-1if(parent->_left == cur) //插入的节点在父亲的左边,bf--{--parent->_bf;}else //插入的节点在父亲的右边,bf++{++parent->_bf;}//更新完平衡因子之后,parent的bf值可能为:2,1,0,-1,-2if(parent->_bf == 0)//说明parent在插入之前的bf为1或-1,以parent为根的左右子树高度差1//新增节点之后,也并不会影响整颗树的bf,只会让以parent为根的左右子树高度差更新为0{break;}else if(parent->_bf == -1 || parent->_bf == 1)//说明parent在插入之前的bf == 0,是AVL树,但是插入之后,会影响整体的高度//所以要依次向上更新{cur = parent;parent = cur->_parent;}else if(parent->_bf == 2 || parent->_bf == -2)//parent的平衡因子超过1,破坏了AVL树的规则,需要进行旋转//但是有4种旋转的情况{//情况1:左旋转if(parent->_bf == 2 && cur->_bf == 1){RotationL(parent, cur);}//情况2:右左旋转else if(parent->_bf == 2 && cur->_bf == -1){RotationRL(parent, cur);}//情况3:右旋转else if(parent->_bf == -2 && cur->_bf == -1){RotationR(parent, cur);}//情况4:左右旋转else if(parent->_bf == -2 && cur->_bf == 1){RotationLR(parent, cur);}else;//旋转结束、不需要更新break;}else{//平衡因子有问题assert(false);}}}
};

四、总结与完善

  1. 希望大家可以自行写出遍历操作、求树的高度、求树的节点个数等成员函数,需要注意的是,利用递归来完成的,都需要写个子函数,然后封装一下子函数.
  2. 大家感兴趣,可以自行查阅删除操作
  3. 后续会实现红黑树,红黑树那里会模拟实现一下map和set

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

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

相关文章

stm32移植嵌入式数据库FlashDB

本次实验的程序链接stm32f103FlashDB嵌入式数据库程序资源-CSDN文库 一、介绍 FlashDB 是一款超轻量级的嵌入式数据库&#xff0c;专注于提供嵌入式产品的数据存储方案。与传统的基于文件系统的数据库不同&#xff0c;FlashDB 结合了 Flash 的特性&#xff0c;具有较强的性能…

Ubuntu20.04安装FloodLight最新版本

Ubuntu20.04安装FloodLight最新版本 网上的很多教程尝试了一下都不对&#xff0c;并且很多都是基于Ubuntu14的旧版本系统&#xff0c;其中的Python环境大多是基于2.0的&#xff0c;由于本人所使用的系统是Ubuntu20.04&#xff0c;后再油管澳大利亚某个学校的网络教学视频的帮助…

【Vue】面试题

vue的组建通信方式 父子关系&#xff1a;props & $emit 、 $parent / $children 、 ref / $refs 、 插槽跨层级关系&#xff1a; provide & inject通用方案&#xff1a;Vuex 或 eventbus 插播&#xff1a;兄弟组建怎么通信&#xff1f; eventbusVuex通过中间件&…

架构师系列-搜索引擎ElasticSearch(六)- 映射

映射配置 在创建索引时&#xff0c;可以预先定义字段的类型&#xff08;映射类型&#xff09;及相关属性。 数据库建表的时候&#xff0c;我们DDL依据一般都会指定每个字段的存储类型&#xff0c;例如&#xff1a;varchar、int、datetime等&#xff0c;目的很明确&#xff0c;就…

STM32之DHT11温湿度传感器

目录 一 DHT11温湿度传感器简介 1.1 传感器特点 1.2 传感器特性 1.3 传感器引脚说明 二 测量原理及方法 2.1 典型应用电路 2.2 单线制串行简介 2.2.1 串行接口 (单线双向) 2.2.2 数据示例 2.3 通信时序 三 单片机简介 3.1 STM32F103C8T6最小系统板 四 接线说明 …

011、Python+fastapi,第一个后台管理项目走向第11步:建立python+fastapi项目,简单测试一下

一、说明 本文章就是记录自己的学习过程&#xff0c;如果有用您可以参考&#xff0c;没用你就略过&#xff0c;没有好与不好之分&#xff0c;今天主要是参考了gitee上的一些项目&#xff0c;一步一步的往后i建立 对于学习来说&#xff0c;如果您有java c等经验&#xff0c;py…

wpf下RTSP|RTMP播放器两种渲染模式实现

技术背景 在这篇blog之前&#xff0c;我提到了wpf下播放RTMP和RTSP渲染的两种方式&#xff0c;一种是通过控件模式&#xff0c;另外一种是直接原生RTSP、RTMP播放模块&#xff0c;回调rgb&#xff0c;然后在wpf下渲染&#xff0c;本文就两种方式做个说明。 技术实现 以大牛直…

RT-thread信号量与互斥量

1,信号量 信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。理解资源计数适合于线程间工作处理速度不匹配的场合;信号量在大于0时才能获取,在中断、线程中均可释放信号量。 为了体现使用信号量来达到线程间的同步,…

qemu源码解析一

基于qemu9.0.0 简介 QEMU是一个开源的虚拟化软件&#xff0c;它能够模拟各种硬件设备&#xff0c;支持多种虚拟化技术&#xff0c;如TCG、Xen、KVM等 TCG 是 QEMU 中的一个组件&#xff0c;它可以将高级语言编写的代码&#xff08;例如 C 代码&#xff09;转换为可在虚拟机中…

稀碎从零算法笔记Day49-LeetCode:设计哈希集合

题型&#xff1a;模拟 链接&#xff1a;705. 设计哈希集合 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 不使用任何内建的哈希表库设计一个哈希集合&#xff08;HashSet&#xff09;。 实现 MyHashSet 类&#xff1a; void add(key) 向哈…

关闭win10搜索中的热门搜索广告

任务目标&#xff0c;关闭掉这个煞笔热门搜索功能 1.首先WinR快捷键&#xff0c;输入“regedit”来打开注册表 2.在注册表中定位到计算机\HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows 并在Windows下新建“项”&#xff0c;命名为“Explorer”&#xff0c;并在这新…

Python大数据分析——一元与多元线性回归模型

Python大数据分析——一元与多元线性回归模型 相关分析概念示例 一元线性回归模型概念理论分析函数示例 多元线性回归模型概念理论分析示例 线性回归模型的假设检验模型的F检验理论分析示例 模型的T检验理论分析示例 相关分析 概念 a 正相关&#xff1b;b 负相关&#xff1b;c…

2024 十五届蓝桥杯省赛Python B组

以下仅是我的答案&#xff0c;仅供参考&#xff0c;欢迎讨论。 A&#xff1a;穿越时空之门 二进制、四进制转换。答案&#xff1a;63。 B&#xff1a;数字串个数 排除0&#xff0c;总的方案数9^10000,减去不存在3和不存在7的2*8^10000&#xff0c;再加上同时不存在3和7的7^…

RedisTemplate

3.3.RedisTemplate 在Sentinel集群监管下的Redis主从集群&#xff0c;其节点会因为自动故障转移而发生变化&#xff0c;Redis的客户端必须感知这种变化&#xff0c;及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换。 下面&#xff0c;我们…

InnoDB中高度为3的B+树最多可以存多少数据?

参考&#xff1a; &#x1f525;我说MySQL每张表最好不超过2000万数据&#xff0c;面试官让我回去等通知&#xff1f; - 掘金 考虑到磁盘IO是非常高昂的操作&#xff0c;计算机操作系统做了预读的优化&#xff0c;当一次IO时&#xff0c;不光把当前磁盘地址的数据&#xff0c;…

计算机网络常问面试题

一.HTTPS是如何保证安全传输的 https通过使⽤对称加密、⾮对称加密、数字证书等⽅式来保证数据的安全传输。 客户端向服务端发送数据之前&#xff0c;需要先建⽴TCP连接&#xff0c;所以需要先建⽴TCP连接&#xff0c;建⽴完TCP连接后&#xff0c;服务端会先给客户端发送公钥…

您与此网站之间建立的连接不安全

正如标题一样&#xff0c;打开的网站地址栏显示&#xff1a;如果你使用浏览器提示您与此网站之间建立的连接不安全、与此站点的连接不安全、网站非安全连接等类似提示。 是因为网站采取的是http地址协议&#xff0c;这种协议有一种缺点&#xff0c;当您常使用的网站出现上述提示…

Vue项目实战:基于用户身份的动态路由管理

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

如何安装MacOS的虚拟机?mac安装虚拟机的步骤 虚拟机安装MacOS VMware Fusion和Parallels Desktop19

要在Mac上运行MacOS的虚拟机&#xff0c;常用的方法是使用虚拟化软件如VMware Fusion或Parallels Desktop。 以下是安装MacOS的虚拟机的主要步骤&#xff1a; 1. 检查系统要求&#xff1a;确定您的Mac硬件和操作系统满足安装要求。您需要一台具备足够性能的Mac&#xff0c;并…

223 基于matlab的结构有限元分析

基于matlab的结构有限元分析。包括基于4节点四面体单元的空间块体分析、基于4节点四边形单元的矩形薄板分析、基于3节点三角形单元的矩形薄板分析、三梁平面框架结构的有限元分析、四杆桁架结构的有限元分析、基于8节点六面体单元的空间块体分析。每个程序都要相应的文档说明。…