数据结构07:查找[C++][平衡二叉排序树AVL]

news2024/12/23 18:46:40

图源:文心一言

考研笔记整理1w+字,小白友好、代码可跑,请小伙伴放心食用~~🥝🥝

第1版:查资料、写BUG、画导图、画配图~🧩🧩

参考用书:王道考研《2024年 数据结构考研复习指导》

参考用书配套视频:7.3_2 平衡二叉树_哔哩哔哩_bilibili

特别感谢: Chat GPT老师、文心一言老师~


📇目录

📇目录

🦮思维导图 

🧵基本概念

⏲️定义

🌰推算举栗

⌨️代码实现

🧵分段代码

 🔯P0:调用库文件

 🔯P1:定义结点

 🔯P2:读取、写入结点的高度

 🔯P3:计算结点的平衡因子

 🔯P4:结点旋转操作

 🔯P5:调整平衡操作 

 🔯P6:插入结点

 🔯P0-P6附录:构造二叉树

 🔯P7:寻找最小结点

 🔯P8:结点删除

 🔯P9:树的遍历

 🔯P10:main函数

🧵完整代码

 🔯P0:完整代码

 🔯P1:执行结果

🔚结语


🦮思维导图 

备注:

  • 思维导图为整理王道教材第7章 查找的所有内容;
  • 本篇仅涉及到平衡二叉排序树(AVL)的代码;
  • 系列博文有朴素二叉排序树的说明🌸数据结构07:查找[C++][朴素二叉排序树BST],后期会在博文列表中整理红黑树的相关内容[交可爱Ada小助手布置的红黑树作业],也可能会增加B树线性查找的相关内容~ //咳咳,上篇博文800阅读+2个赞+2个收藏足以让一个菜鸟博主开心得老~泪~纵~横~了~~

🧵基本概念

⏲️定义

  • 平衡二叉排序树,若树非空,满足下列特性:
    • 是一棵朴素二叉排序树🌸数据结构07:查找[C++][朴素二叉排序树BST];
    • 在插入和删除结点时,要保证任意结点的左、右子树高度差的绝对值不能超过1

树的插入操作:

  • 与结点进行比较,小于则向左子树遍历,大于则向右子树遍历,直到遍历为空结点时,为叶子结点的预插入路径。
  • 需要检查插入结点后是否会导致树不平衡。如果导致不平衡,需要调整子树的形态,直到其满足平衡二叉树定义为止。

 平衡树的定义,可见🌸数据结构05:树与二叉树[C++]

🌰推算举栗

  • 输入序列为{15、3、7、10、9、8}:
    • 插入结点时,若不调整平衡度,有时容易形成度为1的长树,如左图;
    • 插入结点时,若调整平衡度,可每次近似形成度为2的宽树,如右图~

// 备注:至于左侧这棵树调整为右侧这棵树的过程,我们会在本博文代码块“插入”操作中介绍~ 

  • ASL:平均查找长度,计算方式为 Σ(第 i 层结点数 x i 的层高)/ (结点个数),表示整棵树所有查找过程中,进行关键字比较次数的平均值~
  • 通过图中比较,可知:
    • 左侧树的查找:接近链表,与顺序查找相似,时间复杂度近似O(n);
    • 右侧树的查找:接近平衡树,与折半查找相似,时间复杂度近似O(log n);
  • 因此,朴素二叉排序树的查找的时间复杂度接近O(log n)~

下面我们以图中左侧的小树为例,说明如何创建及遍历平衡二叉排序树~


图源:文心一言

⌨️代码实现

🧵分段代码

 🔯P0:调用库文件

  • 输入输出流文件iostream{本代码用于输入与输出};
  • 动态数组的向量文件vector{本代码用于创建树结点的动态数组};
  • 基础算法函数文件algorithm{本代码用于计算比较结点子树的高度}~
#include <iostream>
#include <vector>
#include <algorithm>

 🔯P1:定义结点

相比朴素二叉排序树,平衡二叉排序树需要增加结点的1个属性height:以当前结点为根结点的子树高度,以保证树的平衡性~

struct AVLNode {
    int data;    //数据域
    int height;       //子树高度
    AVLNode* left;    //左孩子指针
    AVLNode* right;   //右孩子指针

    AVLNode(int value) : data(value), height(1), left(nullptr), right(nullptr) {}
};    //将构造函数的参数value赋给data,height初始化为1,左left、右孩子right置空

树的高度计算有点类似于这样子:查询左、右子树的高度,选择较高的子树+1作为本结点的高度~

 🔯P2:读取、写入结点的高度

  • 读取树的高度:查询结点中的node属性~
  • 写入树的高度:查询左子树的高度与右子树的高度,左、右子树中更高的值,并+1返回到本结点的高度~
// 读取结点的高度
int getHeight(AVLNode* node) {
    if (node == nullptr)
        return 0;
    return node->height;    //查询结点node中height属性的高度
}

// 更新结点的高度
void updateHeight(AVLNode* node) {
    if (node == nullptr)
        return;
    node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1;    //将左子树、右子树中更高的height赋值到当前结点
}

 🔯P3:计算结点的平衡因子

  • 结点平衡因子 = 左子树高度 - 右子树高度;
int getBalanceFactor(AVLNode* node) {
    if (node == nullptr)
        return 0;
    return getHeight(node->left) - getHeight(node->right);    //返回左子树高度-右子树高度
}

 🔯P4:结点旋转操作

子树的高度不平衡时(子树的平衡度>|1|),会出现度为1的现象,需要对树进行调整,目的是让树变宽;而平衡调整的基本操作是旋转,如下——

  • 左子树不平衡:当前结点移动到右子树的位置,在图中类似于右旋的操作~
  • 右子树不平衡:当前结点移动到左子树的位置,在图中类似于左旋的操作~

如果我没有理解错的话,非常简单版本的左旋、右旋,大概是这样的——

注意:括号内是平衡因子,计算公式=左子树高-右子树高~

// 右旋操作
AVLNode* rotateRight(AVLNode* node) {
    AVLNode* leftChild = node->left;    //定义左孩子结点
    node->left = leftChild->right;    //将当前结点的左指针指向左子结点的右指针
    leftChild->right = node;          //将左孩子结点的右指针指向当前结点

    updateHeight(node);            //更新当前结点高度
    updateHeight(leftChild);       //更新左子结点高度

    return leftChild;        //返回左子结点
}

// 左旋操作
AVLNode* rotateLeft(AVLNode* node) {
    AVLNode* rightChild = node->right;    //定义右孩子结点
    node->right = rightChild->left;    //将当前结点的右指针指向右子结点的左指针
    rightChild->left = node;           //将右孩子结点的左指针指向当前结点

    updateHeight(node);            //更新当前结点高度
    updateHeight(rightChild);      //更新右子结点高度

    return rightChild;       //返回右子结点
}

 🔯P5:调整平衡操作 

封装旋转的操作以后,此处我们正式介绍一下,当树出现不平衡时应该怎么调整~

因为是二叉树,只有两个孩子,所以插入结点可以汇总为四种情况——

  • 左左型:插入到结点左子树的左子树,导致不平衡,需要右旋;
  • 左右型:插入到结点左子树的右子树,导致不平衡,需要先左旋后右旋;
  • 右左型:插入到结点右子树的左子树,导致不平衡,需要先右旋后左旋;
  • 右右型:插入到结点右子树的右子树,导致不平衡,需要左旋;

考虑到左左型、右右型互为镜像操作 ,左右型、右左型互为镜像操作,此处以左左型与左右型举栗简单说明树的旋转调整过程~

左左型举栗:以下图为例,稍微复杂一点的左左型树,例如结点1插在结点2的左孩子导致不平衡,根结点向右旋转的操作分为3步:

  • 结点4挂到结点5的左孩子;
  • 结点5挂到结点3的右孩子;
  • 更新子树高度,返回根结点~

 以上都是调用P4右旋结点的操作完成~

注意:此处新增结点无论是挂在结点2的左孩子或是右孩子,都属于左左型;根据二叉排序树的性质,挂在结点2的左孩子数值m满足“m<2”,挂在结点2右孩子数值n满足“2<n<3”~

左右型举栗:以下图为例,稍微复杂一点的左右型树,例如结点3插在结点4的左孩子导致不平衡,需要根结点的左孩子先向左旋转,根结点再向右旋转:

  • 结点2左旋,使结点4可以挂在结点5的下方,具体操作如下:
    • 结点3挂到结点2的右孩子;
    • 结点2挂到结点4的左孩子;
    • 结点4挂到结点5的左孩子;
  • 结点5右旋,使整棵树达到平衡,此处操作同左左型旋转~
  • 更新子树高度,返回根结点~

注意:此处新增结点无论是挂在结点4的左孩子或是右孩子,都属于左右型;根据二叉树的性质,挂在结点4的左孩子数值m满足“2<m<4”,挂在结点4右孩子数值n满足“4<n<5”~

// 平衡操作
AVLNode* balanceNode(AVLNode* node) {
    if (node == nullptr)
        return nullptr;

    updateHeight(node);

    // 检查平衡因子
    int balanceFactor = getBalanceFactor(node);

    // 左左型,进行右旋
    if (balanceFactor > 1 && getBalanceFactor(node->left) >= 0)
        return rotateRight(node);

    // 右右型,进行左旋
    if (balanceFactor < -1 && getBalanceFactor(node->right) <= 0)
        return rotateLeft(node);

    // 左右型,先左旋再右旋
    if (balanceFactor > 1 && getBalanceFactor(node->left) < 0) {
        node->left = rotateLeft(node->left);
        return rotateRight(node);
    }

    // 右左型,先右旋再左旋
    if (balanceFactor < -1 && getBalanceFactor(node->right) > 0) {
        node->right = rotateRight(node->right);
        return rotateLeft(node);
    }

    // 结点平衡,无需调整
    return node;
}

 🔯P6:插入结点

同朴素二叉树,采用递归的方式创建树,插入的结点通常为叶节点,然后完成调整平衡:

  • 若二叉排序树为空,则创建根结点;若结点为空,则插入结点。
  • 若二叉树不为空:
    • 关键字 = 根结点值,树中已有此元素;
    • 关键字<根结点值,继续遍历左子树;
    • 关键字>根结点值,继续遍历右子树。
AVLNode* insertNode(AVLNode* root, int data) {
    if (root == nullptr)
        return new AVLNode(data);

    if (data < root->data)
        root->left = insertNode(root->left, data);
    else if (data > root->data)
        root->right = insertNode(root->right, data);
    else
        return root; // 重复值,不进行插入

    return balanceNode(root);
}

 🔯P0-P6附录:构造二叉树

本来想封装个构造二叉树的函数,但是封装函数在调用插入结点函数时一直在报奇怪的编译错误,因此本次代码在main中写入,实际操作就是把一个数组的内的元素分别执行插入结点的操作~

    AVLNode* root = nullptr;
    std::vector<int> data = {15, 3, 7, 10, 9, 8};

    // 插入结点
    for (int i = 0; i < data.size(); i++) {
        root = insertNode(root, data[i]);
    }

具体的创建过程嗯...大概是下图这样的~

 🔯P7:寻找最小结点

利用二叉排序树左<根<右的性质,采用递归方式一直向左寻找,就可以找到最小值结点~

AVLNode* findMinNode(AVLNode* node) {
    if (node == nullptr || node->left == nullptr)    //如果结点或其左孩子不为空
        return node;    //返回结点
    return findMinNode(node->left);    //否则,递归调用本函数向左查询
}

 🔯P8:结点删除

传入树的根结点root和关键字data,此处偷懒采用递归的方式删除 {执行效率很低,胜在代码少好理解,不然就又要人工左旋、右旋,如果不平衡性向上传导又得旋,旋转这么多,实在是太太太头晕啦😢😢} ,可分为以下4种情况考虑——

// PS:考研的同学可以参考王道视频,我记得好像是有删除的单独章节说明...如果需要我整理非递归删除的话也可以在评论区留言,有时间我试试...

  • 特殊情况:
    • 根结点为空,则直接返回空指针;
  • 删除的数据 < 根节点:
    • 递归调用deleteNode函数,将左子树作为新的根节点进行操作;
  • 删除的数据 > 根节点:
    • 递归调用deleteNode函数,将右子树作为新的根节点进行操作;
  • 删除的数据 = 根节点:
    • 树仅有根结点,直接删除,将根指针设为空指针;
    • 树仅有左孩子结点,交换与左孩子结点的位置,删除左孩子结点;
    • 树仅有右孩子结点,交换与右孩子结点的位置,删除右孩子结点;
    • 树具有双子树结点,找到右子树中的最小值节点,将其值赋给当前根节点。然后递归调用deleteNode函数,在右子树中删除最小值节点。
  • 执行删除后,使用balance函数调整平衡。 
AVLNode* deleteNode(AVLNode* root, int data) {
    if (root == nullptr)    //根结点为空
        return root;

    if (data < root->data)    //删除数据 < 根结点
        root->left = deleteNode(root->left, data);    //左子树递归调用deletenode函数
    else if (data > root->data)    //删除数据 > 根结点
        root->right = deleteNode(root->right, data);    //右子树递归调用deletenode函数
    else {    //删除数据 = 根结点
        if (root->left == nullptr && root->right == nullptr) {    //仅有根结点
            delete root;
            root = nullptr;
        } else if (root->left == nullptr) {    //根结点仅有右子树
            AVLNode* temp = root;
            root = root->right;    //交换根结点与右子树
            delete temp;    //删除根结点
        } else if (root->right == nullptr) {   //根结点仅有左子树
            AVLNode* temp = root;
            root = root->left;    //交换根结点与左子树
            delete temp;    //删除根结点
        } else {    //根结点具有双子树
            AVLNode* minRight = findMinNode(root->right);    //寻找右子树的最小值
            root->data = minRight->data;    //右子树的最小值替换根结点
            root->right = deleteNode(root->right, minRight->data);    //右子树递归调用deletenode函数
        }
    }

    return balanceNode(root);    //执行平衡子树的操作
}

 🔯P9:树的遍历

传入树的根结点内存地址,由于二叉树遵循:“左<根<右” 的原则,因此可以通过二叉树的中序遍历完成,此处采用递归方式完成~

void inOrderTraversal(AVLNode* root) {
    if (root == nullptr)
        return;

    InOrderTraversal(root->lchild);    //遍历左子树

    std::cout << root->data << " ";    //输出当前结点的值

    InOrderTraversal(root->rchild);    //遍历右子树
}

敲黑板中序遍历这个已经写过很多次此处不再赘述了~ 🌸数据结构05:树与二叉树[C++]

 🔯P10:main函数

main函数除了P0~P9的函数调用,就创建了1棵树,以及示意性地增加删除结点的操作~

int main() {
    AVLNode* root = nullptr;
    std::vector<int> data = {15, 3, 7, 10, 9, 8};    //树中结点

    // 以插入结点的方式创建树
    for (int i = 0; i < data.size(); i++) {
        root = insertNode(root, data[i]);
    }

    // 中序遍历输出
    std::cout << "中序遍历结果: ";
    inOrderTraversal(root);
    std::cout << std::endl;

    // 删除结点7
    root = deleteNode(root, 7);

    // 中序遍历输出
    std::cout << "删除结点后的中序遍历结果: ";
    inOrderTraversal(root);
    std::cout << std::endl;

    return 0;
}

🧵完整代码

 🔯P0:完整代码

按照惯例,为了凑本文的字数,我这里贴一下整体的代码,删掉了细部注释~🫥🫥

// 头文件
#include <iostream>
#include <vector>
#include <algorithm>

// 二叉平衡树结点定义与初始化
struct AVLNode {
    int data;
    int height;
    AVLNode* left;
    AVLNode* right;

    AVLNode(int value) : data(value), height(1), left(nullptr), right(nullptr) {}
};

// 读取结点的高度
int getHeight(AVLNode* node) {
    if (node == nullptr)
        return 0;
    return node->height;
}

// 写入结点的高度
void updateHeight(AVLNode* node) {
    if (node == nullptr)
        return;
    node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1;
}

// 计算结点的平衡因子
int getBalanceFactor(AVLNode* node) {
    if (node == nullptr)
        return 0;
    return getHeight(node->left) - getHeight(node->right);
}

// 右旋操作
AVLNode* rotateRight(AVLNode* node) {
    AVLNode* leftChild = node->left;
    node->left = leftChild->right;
    leftChild->right = node;

    updateHeight(node);
    updateHeight(leftChild);

    return leftChild;
}

// 左旋操作
AVLNode* rotateLeft(AVLNode* node) {
    AVLNode* rightChild = node->right;
    node->right = rightChild->left;
    rightChild->left = node;

    updateHeight(node);
    updateHeight(rightChild);

    return rightChild;
}

// 平衡操作
AVLNode* balanceNode(AVLNode* node) {
    if (node == nullptr)
        return nullptr;

    updateHeight(node);

    int balanceFactor = getBalanceFactor(node);

    if (balanceFactor > 1 && getBalanceFactor(node->left) >= 0)
        return rotateRight(node);

    if (balanceFactor < -1 && getBalanceFactor(node->right) <= 0)
        return rotateLeft(node);

    if (balanceFactor > 1 && getBalanceFactor(node->left) < 0) {
        node->left = rotateLeft(node->left);
        return rotateRight(node);
    }

    if (balanceFactor < -1 && getBalanceFactor(node->right) > 0) {
        node->right = rotateRight(node->right);
        return rotateLeft(node);
    }

    return node;
}

// 插入结点
AVLNode* insertNode(AVLNode* root, int data) {
    if (root == nullptr)
        return new AVLNode(data);

    if (data < root->data)
        root->left = insertNode(root->left, data);
    else if (data > root->data)
        root->right = insertNode(root->right, data);
    else
        return root; // 重复值,不进行插入

    return balanceNode(root);
}

// 查找最小结点
AVLNode* findMinNode(AVLNode* node) {
    if (node == nullptr || node->left == nullptr)
        return node;
    return findMinNode(node->left);
}

// 删除结点
AVLNode* deleteNode(AVLNode* root, int data) {
    if (root == nullptr)
        return root;

    if (data < root->data)
        root->left = deleteNode(root->left, data);
    else if (data > root->data)
        root->right = deleteNode(root->right, data);
    else {
        if (root->left == nullptr && root->right == nullptr) {
            delete root;
            root = nullptr;
        } else if (root->left == nullptr) {
            AVLNode* temp = root;
            root = root->right;
            delete temp;
        } else if (root->right == nullptr) {
            AVLNode* temp = root;
            root = root->left;
            delete temp;
        } else {
            AVLNode* minRight = findMinNode(root->right);
            root->data = minRight->data;
            root->right = deleteNode(root->right, minRight->data);
        }
    }

    return balanceNode(root);
}

// 中序遍历
void inOrderTraversal(AVLNode* root) {
    if (root == nullptr)
        return;

    inOrderTraversal(root->left);
    std::cout << root->data << " ";
    inOrderTraversal(root->right);
}

int main() {
    AVLNode* root = nullptr;
    std::vector<int> data = {15, 3, 7, 10, 9, 8};

    for (int i = 0; i < data.size(); i++) {
        root = insertNode(root, data[i]);
    }

    std::cout << "中序遍历结果: ";
    inOrderTraversal(root);
    std::cout << std::endl;

    root = deleteNode(root, 7);

    std::cout << "删除结点后的中序遍历结果: ";
    inOrderTraversal(root);
    std::cout << std::endl;

    return 0;
}

 🔯P1:执行结果

运行结果如下图所示~


🔚结语

博文到此结束,写得模糊或者有误之处,欢迎小伙伴留言讨论与批评,督促博主优化内容,不限于以下内容~😶‍🌫️😶‍🌫️

  • 有错误:这段注释南辕北辙,理解错误,需要更改~
  • 难理解:这段代码雾里看花,需要更换排版、增加语法、逻辑注释或配图~
  • 不简洁:这段代码瘠义肥辞,好像一座尸米山,需要更改逻辑;如果是C++语言,调用某库某语法还可以简化~
  • 缺功能:这段代码败絮其中,能跑,然而不能用,想在实际运行或者通过考试需要增加功能~
  • 跑不动:这不可能——好吧,如果真不能跑,告诉我哪里不能跑我再回去试试...

博文若有帮助,欢迎小伙伴动动可爱的小手默默给个赞支持一下~🌟🌟

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

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

相关文章

Linux 超级漂亮的 Shell

先来一张美图 zsh 介绍 1 Linux shell Linux/Unix 提供了很多种 Shell&#xff0c;为毛要这么多 Shell&#xff1f; 难道用来炒着吃么&#xff1f;那我问你&#xff0c;你同类型的衣服怎么有那么多件&#xff1f;花色&#xff0c;质地还不一样。写程序比买衣服复杂多了&#xf…

在vite+vue3中使用less

目录标题 一、首先&#xff0c;安装插件二、在 vite.config.js中配置三、最后在模版里声明css类型 一、首先&#xff0c;安装插件 npm install less npm install less-loader二、在 vite.config.js中配置 css: {preprocessorOptions: {less: {math: "always", // 括…

VS里拉取时候,变成变基中,变成分离分支状态,git 头指针分离于 baf67ff

分离头指针&#xff08;detached HEAD&#xff09; 通常&#xff0c;我们工作在某一个分支上&#xff0c;比如 master 分支。这个时候 master 指针和 HEAD 指针是一起前进的&#xff0c;每做一次提交&#xff0c;这两个指针就会一起向前挪一步。但是在某种情况下&#xff08;例…

docker搭建达梦数据库

搭建达梦数据库 搭建达梦数据库 拉取镜像 # 拉取镜像 docker pull registry.cn-shanghai.aliyuncs.com/techerwang/dbhub:jem_dm8 或去官网下载 https://eco.dameng.com/document/dm/zh-cn/start/dm-install-docker.html我是选择了去官网下载 然后上传到linux服务器 do…

Selenium教程__按钮的点击、文本框的输入和清除操作(3)

from selenium import webdriverdriver webdriver.Chrome() driver.maximize_window() driver.get("https://www.baidu.com")# 定位元素 input_box driver.find_element_by_id("kw") search_btn driver.find_element_by_id("su")# sen_keys&a…

pycharm修改文件大小限制

场景&#xff1a; 方法&#xff1a; 打开pycharm 安装目录下的idea.properties 增加配置项&#xff1a;idea.max.intellisense.filesize99999

电脑word如何转化为pdf格式,其实非常简单!

作为一名在职场工作多年的资深达人&#xff0c;我们都知道工作中常常需要转换各种文件格式&#xff0c;其中最常见的就是Word文档和PDF文档。有时候根据领导、客户或甲方的要求&#xff0c;我们需要进行格式转换&#xff0c;尤其是将Word转换为PDF格式。PDF格式的文件不仅方便阅…

Spring基础知识(四)

目录 1.Spring包含的模块主要有什么 2.Core Container的作用 3.Data Access/Integration模块的作用 4.AOP模块的作用 5.Spring Web模块的作用 6.Test模块的作用 7.如何将一个类声明为Bean 8.Component和Bean的区别 9.能够注入Bean的注解有什么 10.Resource注解 1.Spr…

【Java】Java 中格式化字符串:String.format() 方法

本文仅供学习参考&#xff01; 相关文章地址&#xff1a; https://www.cnblogs.com/Dhouse/p/7776780.html https://www.javatpoint.com/java-string-format https://www.geeksforgeeks.org/java-string-format-method-with-examples/ 前言 虽然System.out.println()适合调试和…

5 STM32复位和时钟控制

STM32的复位功能 系统复位&#xff1a; 系统复位将复位除时钟控制寄存器CSR中的复位标志和备份区域中的寄存器以外的所有寄存器为它们的复位数值。&#xff08;按键复位和软件复位属于系统复位&#xff09; 电源复位&#xff1a; 电源复位将复位除了备份区域外的所有寄存器。…

【Flink】DataStream API使用之转换算子(Transformation)

转换算子&#xff08;Transformation&#xff09; 数据源读入数据之后&#xff0c;就是各种转换算子的操作&#xff0c;将一个或者多个DataSream转换为新的DataSteam&#xff0c;并且Flink可以针对一条流进行转换处理&#xff0c;也可以进行分流或者河流等多流转换操作&#xf…

sql with子语句的理解

在用sqlite时&#xff0c;用到了with子语句&#xff0c;记录下对其的理解 目的&#xff1a;在1个字段中找到真正包含的数据&#xff0c;一般用于like&#xff0c;但该字段可能是复杂的字符串数组。 例子数据&#xff0c;如&#xff1a; 【E2806894200050010B93C473,123456】…

软件分享:Cpu-Z电脑CPU检测工具介绍(附下载)

目录 一、软件介绍 二、功能介绍 三、使用方法 1、通过 CPU-Z 查看 CPU 信息 2、缓存信息 3、主板信息 4、内存信息 5、内存规格信息&#xff08;SPD&#xff09; CPU是计算机中负责读取指令&#xff0c;对指令译码&#xff0c;并执行指令的核心部件。CPU自产生以来&am…

chatgpt赋能python:用Python计算BIM:优点、应用和结论

用Python计算BIM&#xff1a;优点、应用和结论 Building Information Modeling&#xff08;BIM&#xff09;是一种数字化的建筑设计和施工方法&#xff0c;它包括从建筑物的设计、施工到维护和拆除的整个生命周期。BIM在大规模工程建设中越来越受到欢迎&#xff0c;Python编程…

单片机强排热水器方案

单片机强排热水器方案 MCU型号 MM32SPIN05TW、MM32SPIN080C 产品概述 MM32SPIN05TW本产品使用高性能的ArmCortex-M0为内核的32位MCU&#xff0c;最高工作频率可达72MHz&#xff0c;内置高速存储器&#xff0c;丰富的I/O端口和外设连接到外部总线。本产品包含1个12位ADC、1个比…

【群智能算法改进】基于二次插值策略的改进白鲸优化算法 改进后的EBWO[3]算法【Matlab代码#44】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始BWO算法2. 基于二次插值策略的EBWO算法3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原始BWO算法 白鲸优化算法 (BWO&#xff0c;beluga whale optimization)…

宝塔SSL续签以及申请报CURL(7)与CURL(28)错误解决方案

说一下具体背景以及遇到的问题&#xff1a; 主要是网站的SSL过期以后&#xff0c;点击宝塔里的续签功能&#xff0c;弹出一个黑框&#xff0c;等超时后报以下错误 其中也尝试过很多方法&#xff0c;都没什么用&#xff0c;当然&#xff0c;如果报错信息不是这类情况&#xff…

3D开发工具HOOPS可构建PLM程序,数据共享更便捷

产品生命周期管理 (PLM) 解决方案实际上都是将制造生产系统粘合在一起的粘合剂&#xff0c;提供从头到尾的数字主线并为最终用户优化流程。 Tech Soft 3D不是构建 PLM 应用程序的专家&#xff0c;但却对构建领先应用程序所基于的组件技术&#xff0c;以及 SDK 如何提供必要的核…

MiniGPT-4原理解读——大模型论文阅读笔记三

论文&#xff1a;https://arxiv.org/pdf/2304.10592v1.pdf 代码&#xff1a;https://github.com/vision-cair/minigpt-4 一. 作者动机 GPT-4展示了非凡的多模态能力&#xff0c;比如直接从手写文本生成网站&#xff0c;以及识别图像中的幽默元素。这些特性在以前的视觉语言模…

Scrum敏捷看板工具在项目管理中的作用

Scrum是迭代式增量软件开发过程&#xff0c;是敏捷方法论中的重要框架之一&#xff0c;通常用于敏捷软件开发。 当开发团队在用 Scrum 这种开发方法开发项目时&#xff0c;可以利用敏捷看板&#xff0c;可视化透明且灵活的高效的完成工作。 使用敏捷看板可以使&#xff1a; …