C++ 数据结构B 树

news2025/4/17 13:00:43

目录

1. 常见的搜索结构

2. B树概念

3. B-树的插入分析

4. B-树的插入实现

        4.1 B-树的节点设计

       4.2 插入key的过程

4.4 B-树的简单验证

4.5 B-树的性能分析

4.6 B-树的删除

5. B+树和B*树

5.1 B+树

5.2 B*树

5.3 总结

6. B-树的应用

6.1 索引

6.2 MySQL索引简介

6.2.1 MyISAM

6.2.2 InnoDB


1. 常见的搜索结构

以上结构适合用于数据量相对不是很大,能够一次性存放在内存中,进行数据查找的场景。如果
数据量很大,比如有 100G 数据,无法一次放进内存中,那就只能放在磁盘上了,如果放在磁盘 上,有需要搜索某些数据,那么如果处理呢?那么我们可以考虑将存放关键字及其映射的数据的 地址放到一个内存中的搜索树的节点中,那么要访问数据时,先取这个地址去磁盘访问数据。

 

使用平衡二叉树搜索树的缺陷:
平衡二叉树搜索树的高度是 logN ,这个查找次数在内存中是很快的。但是当数据都在磁盘中时,访问磁盘速度很慢,在数据量很大时,logN 次的磁盘访问,是一个难以接受的结果。
使用哈希表的缺陷:
哈希表的效率很高是 O(1) ,但是一些极端场景下某个位置冲突很多,导致访问次数剧增,也是难以接受的。那如何加速对数据的访问呢?
1. 提高 IO 的速度 (SSD 相比传统机械硬盘快了不少,但是还是没有得到本质性的提升 )
2. 降低树的高度 --- 多叉树平衡树

2. B树概念

1970 年, R.Bayer E.mccreight 提出了一种适合外查找的树,它是一种平衡的多叉树,称为 B
( 后面有一个 B 的改进版本 B+ 树,然后有些地方的 B 树写的的是 B- 树,注意不要误读成 "B 减树 ")
m (m>2) B 树,是一棵平衡的 M 路平衡搜索树,可以是空树 或者满足一下性质:
1. 根节点至少有两个孩子
2. 每个分支节点都包含 k-1 个关键字和 k 个孩子,其中 ceil(m/2) ≤ k ≤ m ceil是向上取整函数
3. 每个叶子节点都包含 k-1 个关键字,其中 ceil(m/2) ≤ k ≤ m
4. 所有的叶子节点都在同一层
5. 每个节点中的关键字从小到大排列,节点当中 k-1 个元素正好是 k 个孩子包含的元素的值域划
6. 每个结点的结构为:( n A0 K1 A1 K2 A2 Kn An 其中, Ki(1≤i≤n) 为关键字,且Ki<Ki+1(1≤i≤n-1) Ai(0≤i≤n) 为指向子树根结点的指针。且 Ai 所指子树所有结点中的关键字均小于Ki+1 。n为结点中关键字的个数,满足 ceil(m/2)-1≤n≤m-1

3. B-树的插入分析

为了简单起见,假设 M = 3. 三叉树,每个节点中存储两个数据,两个数据可以将区间分割成三
个部分,因此节点应该有三个孩子 ,为了后续实现简单期间,节点的结构如下:

用序列 {53, 139, 75, 49, 145, 36, 101} 构建 B 树的过程如下:

 

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 返回值:PNode代表找到的节点,int为该元素在该节点中的位置
pair<PNode, int> Find(const K &key)
{
    // 从根节点的位置开始查找
    PNode pCur = _pRoot;
    PNode pParent = NULL;
    size_t i = 0;
    // 节点存在
    while (pCur)
    {
        i = 0;
            // 在该节点的值域中查找
            while (i < pCur->_size)
        {
                    // 找到返回
                if (key == pCur->_keys[i]) return pair<PNode, int>(pCur, i);
            else if (key < pCur->_keys[i])   // 该元素可能在i的左边的孩子节点中
                break;
            else i++;
              // 继续向右查找
        }
    
     // 在pCur中没有找到,到pCur节点的第i个孩子中查找
            pParent = pCur;
        pCur = pCur->_pSub[i];
    }
    // 没有找到
    return pair<PNode, int>(pParent, -1);
}

int main()
{

    system("pause");
    return 0;
}

 

 

插入过程总结:
1. 如果树为空,直接插入新节点中,该节点为树的根节点
2. 树非空,找待插入元素在树中的插入位置 ( 注意:找到的插入节点位置一定在叶子节点中 )
3. 检测是否找到插入位置 ( 假设树中的 key 唯一,即该元素已经存在时则不插入 )
4. 按照插入排序的思想将该元素插入到找到的节点中
5. 检测该节点是否满足 B- 树的性质:即该节点中的元素个数是否等于 M ,如果小于则满足
6. 如果插入后节点不满足 B 树的性质,需要对该节点进行分裂:申请新节点找到该节点的中间位置将该节点中间位置右侧的元素以及其孩子搬移到新节点中,将中间位置元素以及新节点往该节点的双亲节点中插入,即继续4
7. 如果向上已经分裂到根节点的位置,插入结束。

4. B-树的插入实现

        4.1 B-树的节点设计

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// M 叉树,即一个节点最多有M个孩子,M-1 个数据域
template <class K, int M = 3>

struct BTreeNode
{

    K _keys[M]; //存放元素
    BTreeNode<K, M> *_pSub[M + 1];
    BTreeNode<K, M> *_pParent;
    size_t _size;

    BTreeNode() : _pParent(NULL), _size(0)
    {
        for (size_t i = 0; i <= M; i++)
        {
            _pSub[i] = nullptr;
        }
    }
};

int main()
{

    system("pause");
    return 0;
}

       4.2 插入key的过程

按照插入排序的思想插入 key ,注意:在插入 key 的同时,可能还要插入新分裂出来的节点
        
void _InsertKey(PNode pCur, const K& key, PNode pSub)
{
// 按照插入排序思想插入key
 int end = pCur->_size-1;
 while(end >= 0)
 {
 if(key < pCur->_keys[end])
 {
         // 将该位置元素以及其右侧孩子往右搬移一个位置
 pCur->_keys[end+1] = pCur->_keys[end];
 pCur->_pSub[end+2] = pCur->_pSub[end+1];
 end--;
 }
 else
 break;
 }
// 插入key以及新分裂出的节点
 pCur->_keys[end+1] = key;
 pCur->_pSub[end+2] = pSub;
 
// 更新节点的双亲
 if(pSub)
 pSub->_pParent = pCur;
 
 pCur->_size++;
}

4.3 B-树的插入实现  

        

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// M 叉树,即一个节点最多有M个孩子,M-1 个数据域
template <class K, int M = 3>

struct BTreeNode
{

    K _keys[M]; //存放元素
    BTreeNode<K, M> *_pSub[M + 1];
    BTreeNode<K, M> *_pParent;
    size_t _size; //节点元素的个数

    BTreeNode() : _pParent(NULL), _size(0)
    {
        for (size_t i = 0; i <= M; i++)
        {
            _pSub[i] = nullptr;
        }
    }
    bool Insert(const K &key)
    {
        // 如果树为空,直接插入
        if (NULL == _pRoot)
        {
            _pRoot = new Node();
            _pRoot->_keys[0] = key;
            _pRoot->_size = 1;
            return true;
        }
        // 找插入位置,如果该元素已经存在,则不插入
        pair<PNode, int> ret = Find(key);
        if (-1 != ret.second)
            return false;
        K k = key;
        PNode temp = NULL;
        PNode pCur = ret.first;
        while (true)
        {
                // 将key插入到pCur所指向的节点中
                _InsertKey(pCur, k, temp);
                // 检测该节点是否满足B-树的性质,如果满足则插入成功返回,否则,对pCur节点进行分裂
                if (pCur->_size < M) return true;
            // 申请新节点
            temp = new Node;
            // 找到pCur节点的中间位置
                // 将中间位置右侧的元素以及孩子搬移到新节点中
                int mid = (M >> 1);
            for (size_t i = mid + 1; i < pCur->_size; ++i)
            {
                temp->_keys[temp->_size] = pCur->_keys[i];
                temp->_pSub[temp->_size++] = pCur->_pSub[i];

                          // 跟新孩子节点的双亲
                    if (pCur->_pSub[i])
                        pCur->_pSub[i]
                            ->_pParent = temp;
            }
            // 注意:孩子比关键字多搬移一个
            temp->_pSub[temp->_size] = pCur->_pSub[pCur->_size];
            if (pCur->_pSub[pCur->_size])
                pCur->_pSub[pCur->_size]->_pParent = temp;
                // 更新pCur节点的剩余数据个数
                pCur->_size -= (temp->_size + 1);
                // 如果分裂的节点为根节点,重新申请一个新的根节点,将中间位置数据以及分裂出的新节点
                插入到新的根节点中,插入结束 if (pCur == _pRoot)
            {
                _pRoot = new Node;
                _pRoot->_keys[0] = pCur->_keys[mid];
                _pRoot->_pSub[0] = pCur;
                4.4 B - 树的简单验证 对B树进行中序遍历,如果能得到一个有序的序列,说明插入正确。 4.5 B - 树的性能分析 对于一棵节点为N度为M的B - 树,查找和插入需要$log{M - 1} N$ ~$log{M / 2} N$次比较,这个很好证 明:对于度为M的B - 树,每一个节点的子节点个数为M / 2 ~(M - 1) 之间,因此树的高度应该在要 $log{M - 1} N$和$log{M / 2} N$之间,在定位到该节点后,再采用二分查找的方式可以很快的定位 到该元素。 B - 树的效率是很高的,对于N = 62 * 1000000000个节点,如果度M为1024,则 $log_{M / 2} N$ <=
                                                                                                                                                                                                                                                                                                                                                                                                                                              4,即在620亿个元素中,如果这棵树的度为1024,则需要小于4次即可定位到该节点,然后利用 二分查找可以快速定位到该元素,大大减少了读取磁盘的次数。 4.6 B - 树的删除 学习B树的插入足够帮助我们理解B树的特性了,如果对删除有兴趣的同学们参考《算法导论》--伪代码和《数据结构 - 殷人昆》--C++ 实现代码。 5. B + 树和B * 树 5.1 B + 树 B + 树是B树的变形,是在B树基础上优化的多路平衡搜索树,B + 树的规则跟B树基本类似,但是又 在B树的基础上做了以下几点改进优化: _pRoot->_pSub[1] = temp;
                _pRoot->_size = 1;
                pCur->_pParent = temp->_pParent = _pRoot;
                return true;
            }
            else
            {
                        // 如果分裂的节点不是根节点,将中间位置数据以及新分裂出的节点继续向pCur的双亲
                    中进行插入
                        k = pCur->_keys[mid];
                pCur = pCur->_pParent;
            }
        }
        return true;
    }
};

int main()
{

    system("pause");
    return 0;
}

4.4 B-树的简单验证

B 树进行中序遍历,如果能得到一个有序的序列,说明插入正确。
        
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// M 叉树,即一个节点最多有M个孩子,M-1 个数据域
template <class K, int M = 3>

struct BTreeNode
{

    K _keys[M]; //存放元素
    BTreeNode<K, M> *_pSub[M + 1];
    BTreeNode<K, M> *_pParent;
    size_t _size; //节点元素的个数

    BTreeNode() : _pParent(NULL), _size(0)
    {
        for (size_t i = 0; i <= M; i++)
        {
            _pSub[i] = nullptr;
        }
    }
    bool Insert(const K &key)
    {
        // 如果树为空,直接插入
        if (NULL == _pRoot)
        {
            _pRoot = new Node();
            _pRoot->_keys[0] = key;
            _pRoot->_size = 1;
            return true;
        }
        // 找插入位置,如果该元素已经存在,则不插入
        pair<PNode, int> ret = Find(key);
        if (-1 != ret.second)
            return false;
        K k = key;
        PNode temp = NULL;
        PNode pCur = ret.first;
        while (true)
        {
                // 将key插入到pCur所指向的节点中
                _InsertKey(pCur, k, temp);
                // 检测该节点是否满足B-树的性质,如果满足则插入成功返回,否则,对pCur节点进行分裂
                if (pCur->_size < M) return true;
            // 申请新节点
            temp = new Node;
            // 找到pCur节点的中间位置
                // 将中间位置右侧的元素以及孩子搬移到新节点中
                int mid = (M >> 1);
            for (size_t i = mid + 1; i < pCur->_size; ++i)
            {
                temp->_keys[temp->_size] = pCur->_keys[i];
                temp->_pSub[temp->_size++] = pCur->_pSub[i];

                          // 跟新孩子节点的双亲
                    if (pCur->_pSub[i])
                        pCur->_pSub[i]
                            ->_pParent = temp;
            }
            // 注意:孩子比关键字多搬移一个
            temp->_pSub[temp->_size] = pCur->_pSub[pCur->_size];
            if (pCur->_pSub[pCur->_size])
                pCur->_pSub[pCur->_size]->_pParent = temp;
                // 更新pCur节点的剩余数据个数
                pCur->_size -= (temp->_size + 1);
                // 如果分裂的节点为根节点,重新申请一个新的根节点,将中间位置数据以及分裂出的新节点
                插入到新的根节点中,插入结束 if (pCur == _pRoot)
            {
                _pRoot = new Node;
                _pRoot->_keys[0] = pCur->_keys[mid];
                _pRoot->_pSub[0] = pCur;
                4.4 B - 树的简单验证 对B树进行中序遍历,如果能得到一个有序的序列,说明插入正确。 4.5 B - 树的性能分析 对于一棵节点为N度为M的B - 树,查找和插入需要$log{M - 1} N$ ~$log{M / 2} N$次比较,这个很好证 明:对于度为M的B - 树,每一个节点的子节点个数为M / 2 ~(M - 1) 之间,因此树的高度应该在要 $log{M - 1} N$和$log{M / 2} N$之间,在定位到该节点后,再采用二分查找的方式可以很快的定位 到该元素。 B - 树的效率是很高的,对于N = 62 * 1000000000个节点,如果度M为1024,则 $log_{M / 2} N$ <=
                                                                                                                                                                                                                                                                                                                                                                                                                                              4,即在620亿个元素中,如果这棵树的度为1024,则需要小于4次即可定位到该节点,然后利用 二分查找可以快速定位到该元素,大大减少了读取磁盘的次数。 4.6 B - 树的删除 学习B树的插入足够帮助我们理解B树的特性了,如果对删除有兴趣的同学们参考《算法导论》--伪代码和《数据结构 - 殷人昆》--C++ 实现代码。 5. B + 树和B * 树 5.1 B + 树 B + 树是B树的变形,是在B树基础上优化的多路平衡搜索树,B + 树的规则跟B树基本类似,但是又 在B树的基础上做了以下几点改进优化: _pRoot->_pSub[1] = temp;
                _pRoot->_size = 1;
                pCur->_pParent = temp->_pParent = _pRoot;
                return true;
            }
            else
            {
                        // 如果分裂的节点不是根节点,将中间位置数据以及新分裂出的节点继续向pCur的双亲
                    中进行插入
                        k = pCur->_keys[mid];
                pCur = pCur->_pParent;
            }
        }
        return true;
    }
};
void _InOrder(PNode pRoot)
{
    if (NULL == pRoot)
        return;
    for (size_t i = 0; i < pRoot->_size; ++i)
    {
        _InOrder(pRoot->_pSub[i]);
        cout << pRoot->_keys[i] << " ";
    }
    _InOrder(pRoot->_pSub[pRoot->_size]);
}

int main()
{

    system("pause");
    return 0;
}

4.5 B-树的性能分析

对于一棵节点为 N 度为 M B- 树,查找和插入需要 $log {M-1}N$~$log {M/2}N$ 次比较,这个很好证
明: 对于度为 M B- 树,每一个节点的子节点个数为 M/2 ~(M-1) 之间,因此树的高度应该 $log {M-1}N$ $log {M/2}N$ 之间,在定位到该节点后,再采用二分查找的方式可以很快的定位 到该元素
B- 树的效率是很高的,对于 N = 62*1000000000 个节点,如果度 M 1024 ,则 $log_{M/2}N$ <=
4 ,即在 620 亿个元素中,如果这棵树的度为 1024 ,则需要小于 4 次即可定位到该节点,然后利用 二分查找可以快速定位到该元素,大大减少了读取磁盘的次数

4.6 B-树的删除

学习 B 树的插入足够帮助我们理解 B 树的特性了,如果对删除有兴趣的同学们参考《算法导论》 --
伪代码和《数据结构 - 殷人昆》 --C++ 实现代码。

5. B+树和B*

5.1 B+

B+ 树是 B 树的变形,是在 B 树基础上优化的多路平衡搜索树, B+ 树的规则跟 B 树基本类似,但是又
B 树的基础上做了以下几点改进优化:
2. 分支节点的子树指针与关键字个数相同
3. 分支节点的子树指针 p[i] 指向关键字值大小在 [k[i] k[i+1]) 区间之间
4. 所有叶子节点增加一个链接指针链接在一起
5. 所有关键字及其映射数据都在叶子节点出现

B+树的特性

1. 所有关键字都出现在叶子节点的链表中,且链表中的节点都是有序的。
2. 不可能在分支节点中命中。
3. 分支节点相当于是叶子节点的索引,叶子节点才是存储数据的数据层。

5.2 B*

B* 树是 B+ 树的变形,在 B+ 树的非根和非叶子节点再增加指向兄弟节点的指针。

B+ 树的分裂:
当一个结点满时,分配一个新的结点,并将原结点中 1/2 的数据复制到新结点,最后在父结点中增
加新结点的指针; B+ 树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针。
B* 树的分裂:
当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结
点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如
果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制 1/3 的数据到新结点,最后在父 结点增加新结点的指针。所以,B*树分配新结点的概率比 B+ 树要低,空间使用率更高;

 

5.3 总结

通过以上介绍,大致将 B 树, B+ 树, B* 树总结如下:
B 树:有序数组 + 平衡多叉树;
B+ 树:有序数组链表 + 平衡多叉树;
B* 树:一棵更丰满的,空间利用率更高的 B+

6. B-树的应用

6.1 索引

B- 树最常见的应用就是用来做索引 。索引通俗的说就是为了方便用户快速找到所寻之物,比如书籍目录可以让读者快速找到相关信息,hao123 网页导航网站,为了让用户能够快速的找到有值的分类网站,本质上就是互联网页面中的索引结构。MySQL官方对索引的定义为:索引 (index) 是帮助 MySQL 高效获取数据的数据结构,简单来说: 索引就是数据结构
 当数据量很大时,为了能够方便管理数据,提高数据查询的效率,一般都会选择将数据保存到数据库,因此数据库不仅仅是帮助用户管理数据,而且数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法,该数据结构就是索引。

6.2 MySQL索引简介

mysql 是目前 非常流行的开源关系型数据库,不仅是免费的,可靠性高,速度也比较快,而且拥 有灵活的插件式存储引擎 ,如下:

MySQL 中索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的。
注意:索引是基于表的,而不是基于数据库的。

6.2.1 MyISAM

MyISAM 引擎是 MySQL5.5.8 版本之前默认的存储引擎,不支持事物,支持全文检索,使用 B+Tree
作为索引结构,叶节点的 data 域存放的是数据记录的地址,其结构如下。

 

上图是以以 Col1 为主键, MyISAM 的示意图,可以看出 MyISAM 的索引文件仅仅保存数据记录的 地址 MyISAM 中,主索引和辅助索引( Secondary key )在结构上没有任何区别,只是主索 要求 key 是唯一的,而辅助索引的 key 可以重复 。如果想在 Col2 上建立一个辅助索引,则此索引 的结构如下图所示:

  同样也是一棵 B+Tree data 域保存数据记录的地址。因此, MyISAM 中索引检索的算法为首先按照B+Tree 搜索算法搜索索引,如果指定的 Key 存在,则取出其 data 域的值,然后以 data 域的值为地址,读取相应数据记录。MyISAM 的索引方式也叫做 非聚集索引 的。

6.2.2 InnoDB

InnoDB 存储引擎支持事务 ,其设计目标主要面向在线事务处理的应用,从 MySQL 数据库 5.5.8
本开始, InnoDB 存储引擎是默认的存储引擎 InnoDB 支持 B+ 树索引、全文索引、哈希索引。但
InnoDB 使用 B+Tree 作为索引结构时,具体实现方式却与 MyISAM 截然不同。
第一个区别是 InnoDB 的数据文件本身就是索引文件 MyISAM 索引文件和数据文件是分离的, 索引文件仅保存数据记录的地址 。而 InnoDB 索引,表数据文件本身就是按 B+Tree 组织的一个索引结构,这棵树的叶节点data 域保存了完整的数据记录。这个索引的 key 是数据表的主键,因此 InnoDB表数据文件本身就是主索引.

上图是 InnoDB 主索引 (同时也是数据文件)的示意图,可以看到 叶节点包含了完整的数据记录,
这种索引叫做聚集索引 。因为 InnoDB 的数据文件本身要按主键聚集,所以 InnoDB 要求表必须有
主键 MyISAM 可以没有), 如果没有显式指定,则 MySQL 系统会自动选择一个可以唯一标识数
据记录的列作为主键 如果不存在这种列,则 MySQL 自动为 InnoDB 表生成一个隐含字段作为主 键,这个字段长度为6 个字节,类型为长整型。第二个区别是InnoDB 的辅助索引 data 域存储相应记录主键的值而不是地址 , 所有辅助索引都引用主键作为data 域。

 

聚集索引这种实现方式使得按主键的搜索十分高效 ,但是 辅助索引搜索需要检索两遍索引:首先 检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

 参考资料:

CodingLabs - MySQL索引背后的数据结构及算法原理

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

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

相关文章

浅谈建筑项目中的智能照明系统的设计

张心志 安科瑞电气股份有限公司 上海嘉定 201801 【摘要】&#xff1a;建筑智能照明工程中智能照明控制系统发展迅速&#xff0c;具有舒适性和节能性两方面优势。智能照明控制系统已经处于模块化高速发展阶段&#xff0c;如今更好的控制方案成为制约系统发展的瓶颈。文章在研…

工作总是忙不完是什么原因?时间管理软件帮你解决问题

随着市场竞争的激烈&#xff0c;有不少上班族都感受到了工作的压力&#xff0c;每天越来越多的工作任务需要完成。于是我们经常会听到这样的抱怨&#xff1a;“工作太多了&#xff0c;总是忙不完&#xff01;”那么工作总是忙不完是什么原因呢&#xff1f;其实一般来说工作总是…

GB50255-2014电气装置安装工程电力变流设备施工及验收规范

为了保证电力变流设备安装工程的施工质量&#xff0c;促进施工技术的提高&#xff0c;保证电力变流设备的安全稳定运行&#xff0c;制定本规范。 本规范适用于电力系统中除高压直流输电和柔性交流输电以外的电力变换设备的施工、调试及验收。 电力变流设备的施工、调试及验收…

linux epoll/select使用区分和实例对比

Linux内核poll&#xff0c;ppoll&#xff0c;epoll&#xff0c;select代码位置&#xff1a; poll&#xff0c;ppoll&#xff0c;select相关内核实现在在fs/select.c中; epoll_ctl和epoll_wait相关函数在fs/eventpoll.c中 epoll实测不支持监听普通文件&#xff0c;select可以…

医院中医临床护理中医理论考试题库及答案

本题库是根据最新考试大纲要求&#xff0c;结合近年来考试真题的重难点进行汇编整理组成的全真模拟试题&#xff0c;考生们可以进行专项训练&#xff0c;查漏补缺巩固知识点。本题库对热点考题和重难点题目都进行了仔细的整理和编辑&#xff0c;相信考生在经过了针对性的刷题练…

测试员拒绝当“背锅侠”,软件质量不是“测”出来的!

以前有个朋友跟我说&#xff1a;做测试这行就是在隐忍中负重前行。因为测试是软件发布前的最后一个环节&#xff0c;被称作质量出口儿&#xff0c;测试环节之前所有未做好的工作所带来的不良后果&#xff0c;似乎都能被轻松甩锅给测试人员。软件一旦出现问题&#xff0c;我们经…

django疫情防控常态化下疫情物资管理可视化系统-计算机毕设 附源码73893

django疫情防控常态化下疫情物资管理可视化系统 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。…

YOLOv5改进系列(13)——更换激活函数之SiLU,ReLU,ELU,Hardswish,Mish,Softplus,AconC系列等

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

javaWeb中的Ajax_待后期增加

前言&#xff1a; Ajax是一种在JavaWeb开发中常用的技术&#xff0c;通过它可以实现异步通信和动态加载数据&#xff0c;提升用户体验。 正文&#xff1a; 首先我们得明白异步通信&#xff0c;客户端发出请求后可以继续执行其他操作 由于原生的Ajax过于复杂 so&#xff1a; …

FastDFS【SpringBoot操作FastDFS、复习】(三)-全面详解(学习总结---从入门到深化)

目录 SpringBoot操作FastDFS 复习&#xff1a; SpringBoot操作FastDFS 由GitHub大牛tobato在原作者YuQing与yuqih发布的JAVA客户端基 础上进行了大量重构工作&#xff0c;并于GitHub上发布了FastDFS-Client1.26.5。 主要特性 1 对关键部分代码加入了单元测试&#xff0c;便于…

leetcode 21.合并两个有序链表

⭐️ 往期相关文章 &#x1f4ab;链接1&#xff1a;链表中倒数第k个结点(快慢指针问题) &#x1f4ab;链接2&#xff1a;leetcode 876.链表的中间结点(快慢指针问题) &#x1f4ab;链接3&#xff1a;leetcode 206.反转链表 &#x1f4ab;链接4&#xff1a;leetcode 203.移除链…

蓝牙Beacon+Loa网关推出人员隔离监管解决方案

三年新冠已经过去&#xff0c;以空间换时间的防疫策略让疑似、确诊、有接触史人员得以有效控制&#xff0c;其中居家隔离的防疫措施对新冠的防控是行之有效的。 不过&#xff0c;在“画地为牢”的隔离区也有些需隔离观察人员私自外出&#xff0c;对防疫工作带来相当程度上的影…

数据库性能测试报告总结模板

目录 1计划概述 2参考资料 3术语解释 4系统简介 5测试环境 6测试指标 7测试工具和测试策略 8测试数据收集 9测试结果数据以及截图 9.1Jmeter性能指标 9.2硬件指标图 10 测试结论 需要完整报告模板记得文章末尾找我哦。 1计划概述 目的&#xff1a;找出系统潜在的…

Android Studio中配置aliyun maven库

Android Studio中配置aliyun maven库 在项目的根build.gradle里面&#xff08;不是module&#xff09;buildscriptde对应位置添加配置&#xff1a; buildscript {repositories {maven {url http://maven.aliyun.com/nexus/content/groups/public/allowInsecureProtocol true…

深入了解gradio库的Interpretation模块

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

解码大数据时代的信息安全:低代码开发平台的先锋之道

前言 随着大数据时代的到来&#xff0c;信息安全成为了不可忽视的重要议题。随之而来的是技术的迅速发展和创新&#xff0c;为信息安全领域带来了挑战&#xff0c;也为其提供了前所未有的机遇。在这个背景下&#xff0c;低代码开发平台作为一种创新的解决方案崭露头角&#xff…

速看,多设备上使用 WhatsApp的最完整指南

速看&#xff01;多设备上使用 WhatsApp的最完整指南&#xff01; 在过去的几年里&#xff0c;WhatsApp一直在不断更新。最近它正在推出一项新功能&#xff1a;允许移动和非手机设备使用该服务&#xff0c;而不需要将注册的手机连接到互联网。也就是说&#xff0c;您可以不再需…

cancal 同步mysql数据到es中

1.环境&#xff1a; windocs service2012 、 jdk版本1.8 、canal版本1.5、mysql版本5.7、 注意&#xff1a;canal版本1.5需要的jdk是1.8 如果你下载的是canal1.6&#xff0c;jdk是1.8&#xff0c;那样会报错。 下载地址 Releases alibaba/canal GitHub 下载并上传到服…

CAP结构体之字节对齐

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

No10.精选前端面试题,享受每天的挑战和学习

文章目录 Linux 中五种 IO 模型四次挥手之后为什么还要等待 2mslcookie 和 session 的区别是什么tcp 怎么保证可靠性css实现图片自适应宽高的几种方法 Linux 中五种 IO 模型 在 Linux 中&#xff0c;常见的五种 I/O&#xff08;Input/Output&#xff09;模型包括&#xff1a; …