当前位置: 首页 > news >正文

【C++】B树及其实现

写目录

  • 一、B树的基本概念
    • 1.引入
    • 2.B树的概念
  • 二、B树的实现
    • 1.B树的定义
    • 2.B树的查找
    • 3.B树的插入操作
    • 4.B树的删除
    • 5.B树的遍历
    • 6.B树的高度
    • 7.整体代码
  • 三、B+树和B*树
    • 1.B+树
    • 2.B*树
    • 3.总结

一、B树的基本概念

1.引入

我们已经学习过二叉排序树、AVL树和红黑树三种树形查找结构,但上述结构适用于数据量相对不是很大,能够一次性放进内存中,进行数据查找的场景。如果数据量非常大,比如由100G数据,无法一次放进内存中,那就只能放在磁盘上了。此时想要搜索数据就需要将存放关键字及其映射的数据的地址放到内存中的搜索树的节点中,那么要访问数据时,先取这个地址去磁盘访问数据。在这里插入图片描述
但是由于磁盘访问的速度很慢,对于上述树形查找结构来说就是需要logN次的IO,这是一个很难接受的结果。
那么如何加速对数据的访问呢?

  1. 提高IO的速度(SSD相比传统机械硬盘是快了不少,但还是没有得到本质性的提升)
  2. 降低树的高度——多路平衡查找树

2.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。

二、B树的实现

1.B树的定义

为了方便学习,我们在这里先将m设为3,这时每个结点能存2个关键字和3个孩子。不过为了方便后续插入,分别额外多开了一个空间。
在这里插入图片描述

template<class K, size_t M>
struct BTreeNode
{K _keys[M];						// 用于存储关键字BTreeNode<K, M>* _subs[M + 1];	// 用于存储孩子BTreeNode<K, M>* _parent;		size_t _n;						// 存储当前孩子的数量BTreeNode(){for (size_t i = 0; i < M; ++i){_keys[i] = K();_subs[i] = nullptr;}_subs[M] = nullptr;_parent = nullptr;_n = 0;}
};

2.B树的查找

查找的具体步骤应该是先进入根结点,如果等于data1,则返回,如果小于data1,则进入child1,如果大于data1则++i,此时i指向data2,若小于data2,则进入child2,若大于data2,则进入child3。

pair<Node*, int> Find(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){size_t i = 0;while (i < cur->_n)		// 在当前结点进行查找{if (key < cur->_keys[i])	// 如果小于则进入与当前下标相同的孩子{break;}else if (key > cur->_keys[i])	//如果大于则++i{++i;}else{return make_pair(cur, i);	// 找到了就返回当前结点以及该关键字所在的位置}}parent = cur;cur = cur->_subs[i];}return make_pair(parent, -1);	//找不到返回父结点
}

3.B树的插入操作

用序列{53, 139, 75, 49, 145, 36, 101}构建B树的过程如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
插入过程总结:

  1. 如果树为空,直接插入新节点中,该节点为树的根节点
  2. 树非空,找待插入元素在树中的插入位置(注意:找到的插入节点位置一定在叶子节点中)
  3. 检测是否找到插入位置(假设树中的key唯一,即该元素已经存在时则不插入)
  4. 按照插入排序的思想将该元素插入到找到的节点中
  5. 检测该节点是否满足B-树的性质:即该节点中的元素个数是否等于M,如果小于则满足
  6. 如果插入后节点不满足B树的性质,需要对该节点进行分裂:
    • 申请新节点
    • 找到该节点的中间位置
    • 将该节点中间位置右侧的元素以及其孩子搬移到新节点中
    • 将中间位置元素以及新节点往该节点的双亲节点中插入,即继续4
  7. 如果向上已经分裂到根节点的位置,插入结束
// 插入操作中用到的函数
void InsertKey(Node* node, const K& key, Node* child)	// 插入新关键字
{int end = node->_n - 1;while (end >= 0){if (key < node->_keys[end]){node->_keys[end + 1] = node->_keys[end];node->_subs[end + 2] = node->_subs[end + 1];--end;}else{break;}}node->_keys[end + 1] = key;node->_subs[end + 2] = child;if (child){child->_parent = node;}++node->_n;
}
// B树的插入
bool Insert(const K& key)
{if (_root == nullptr)	//如果根结点为空{_root = new Node;_root->_keys[0] = key;++_root->_n;return true;}pair<Node*, int> ret = Find(key);	// 借助查找操作来判定是否存在要插入的值if (ret.second >= 0)				// 如果不存在则可以找到要进行插入的位置{return false;}Node* parent = ret.first;K newKey = key;Node* child = nullptr;while (1){InsertKey(parent, newKey, child);	// 先利用插入排序的方法进行插入if (parent->_n < M)					// 如果没有破坏B树的性质则返回true{return true;}else								// 否则进行分裂操作{size_t mid = M / 2;Node* brother = new Node;size_t i = mid + 1;size_t j = 0;for (; i < M; ++i)				// 把一半的数据分给新建的兄弟结点{brother->_keys[j] = parent->_keys[i];brother->_subs[j] = parent->_subs[i];if (parent->_subs[i]){parent->_subs[i]->_parent = brother;}++j;// 拷走重置一下方便观察parent->_keys[i] = K();parent->_subs[i] = nullptr;}brother->_subs[j] = parent->_subs[i];if (parent->_subs[i]){parent->_subs[i]->_parent = brother;}parent->_subs[i] = nullptr;brother->_n = j;parent->_n -= (brother->_n + 1);K midKey = parent->_keys[mid];parent->_keys[mid] = K();if (parent->_parent == nullptr)	// 如果没有父结点,则新建{_root = new Node;_root->_keys[0] = midKey;_root->_subs[0] = parent;_root->_subs[1] = brother;_root->_n = 1;parent->_parent = _root;brother->_parent = _root;break;}else	// 有父结点则将插入排序的参数修改,然后回到循环开始插入{newKey = midKey;child = brother;parent = parent->_parent;}}}return true;
}

B树插入的代码实现非常复杂,需要十分细心,要注意对结点的维护。

4.B树的删除

B树的删除分为3种情况:

  1. 直接删除关键字:若删除当前关键字后仍然满足B树定义,则直接删除该关键字。
  2. 兄弟够借:若再删除一个关键字就会破坏B树定义,并且左,右兄弟的关键字个数大于等于ceil(m/2),则需要调整该结点、右(或左)兄弟结点及其父结点(父子换位法),以达到新的平衡。
  3. 兄弟不够借:若左、右兄弟结点的关键字个数都不足以被借,则将关键字删除后与左(或右)兄弟结点及父结点的关键字进行合并。

由于B树的删除用代码实现非常复杂,就不多讲了。

5.B树的遍历

B树的遍历就不难了,与查找的过程类比即可。

void _InOrder(Node* cur)
{if (cur == nullptr)return;// 左 根  左 根  ...  右size_t i = 0;for (; i < cur->_n; ++i){_InOrder(cur->_subs[i]); // 左子树cout << cur->_keys[i] << " "; // 根}_InOrder(cur->_subs[i]); // 最后的那个右子树
}void InOrder()
{_InOrder(_root);
}

6.B树的高度

B树的效率取决于B树的高度,因为磁盘存取次数与高度成正比。
若n>=1,则对任意一颗包含n个关键字、高度为h、阶数为m的B树:

  1. 若让每个结点中的关键字个数达到最多,则容纳同样多关键字的B树的高度达到最小。因为B树种每个结点最多有m棵子树,m-1个关键字,所以在一颗高度为h的m阶B树中关键字的个数应满足 n < = ( m − 1 ) ( 1 + m + m 2 + . . . + m h − 1 ) = m h − 1 n<=(m-1)(1+m+m^2+...+m^{h-1})=m^h-1 n<=(m1)(1+m+m2+...+mh1)=mh1,因此有 h > = l o g m ( n + 1 ) h>=log_m(n+1) h>=logm(n+1)
  2. 若让每个结点中的关键字个数达到最少,则容纳同样多关键字的B树高度达到最大。第一层至少有1个结点;第二层至少有两个结点;除根结点外的每个非叶结点至少有ceil(m/2)棵子树,则第三层至少有2ceil(m/2)个结点……第h+1层至少有 2 ( c e i l ( m / 2 ) ) h − 1 2(ceil(m/2))^{h-1} 2(ceil(m/2))h1个结点,注意到第h+1层是不包含任何信息的叶结点。对于关键字个数为n的B树,叶结点即查找不成功的结点为n+1,由此有 n + 1 > = 2 ( c e i l ( m / 2 ) ) h − 1 n+1>=2(ceil(m/2))^{h-1} n+1>=2(ceil(m/2))h1,即 h < = l o g c e i l ( m / 2 ) ( ( n + 1 ) / 2 ) + 1 h<=log_{ceil(m/2)}((n+1)/2)+1 h<=logceil(m/2)((n+1)/2)+1

7.整体代码

#include <iostream>
using namespace std;template<class K, size_t M>
struct BTreeNode
{K _keys[M];BTreeNode<K, M>* _subs[M + 1];BTreeNode<K, M>* _parent;size_t _n;BTreeNode(){for (size_t i = 0; i < M; ++i){_keys[i] = K();_subs[i] = nullptr;}_subs[M] = nullptr;_parent = nullptr;_n = 0;}
};template<class K, size_t M>
class BTree
{typedef BTreeNode<K, M> Node;
public:pair<Node*, int> Find(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){size_t i = 0;while (i < cur->_n)		// 在当前结点进行查找{if (key < cur->_keys[i])	// 如果小于则进入与当前下标相同的孩子{break;}else if (key > cur->_keys[i])	//如果大于则++i{++i;}else{return make_pair(cur, i);	// 找到了就返回当前结点以及该关键字所在的位置}}parent = cur;cur = cur->_subs[i];}return make_pair(parent, -1);	//找不到返回父结点}void InsertKey(Node* node, const K& key, Node* child)	// 插入新关键字{int end = node->_n - 1;while (end >= 0){if (key < node->_keys[end]){node->_keys[end + 1] = node->_keys[end];node->_subs[end + 2] = node->_subs[end + 1];--end;}else{break;}}node->_keys[end + 1] = key;node->_subs[end + 2] = child;if (child){child->_parent = node;}++node->_n;}bool Insert(const K& key){if (_root == nullptr)	//如果根结点为空{_root = new Node;_root->_keys[0] = key;++_root->_n;return true;}pair<Node*, int> ret = Find(key);	// 借助查找操作来判定是否存在要插入的值if (ret.second >= 0)				// 如果不存在则可以找到要进行插入的位置{return false;}Node* parent = ret.first;K newKey = key;Node* child = nullptr;while (1){InsertKey(parent, newKey, child);	// 先利用插入排序的方法进行插入if (parent->_n < M)					// 如果没有破坏B树的性质则返回true{return true;}else								// 否则进行分裂操作{size_t mid = M / 2;Node* brother = new Node;size_t i = mid + 1;size_t j = 0;for (; i < M; ++i)				// 把一半的数据分给新建的兄弟结点{brother->_keys[j] = parent->_keys[i];brother->_subs[j] = parent->_subs[i];if (parent->_subs[i]){parent->_subs[i]->_parent = brother;}++j;// 拷走重置一下方便观察parent->_keys[i] = K();parent->_subs[i] = nullptr;}brother->_subs[j] = parent->_subs[i];if (parent->_subs[i]){parent->_subs[i]->_parent = brother;}parent->_subs[i] = nullptr;brother->_n = j;parent->_n -= (brother->_n + 1);K midKey = parent->_keys[mid];parent->_keys[mid] = K();if (parent->_parent == nullptr)	// 如果没有父结点,则新建{_root = new Node;_root->_keys[0] = midKey;_root->_subs[0] = parent;_root->_subs[1] = brother;_root->_n = 1;parent->_parent = _root;brother->_parent = _root;break;}else	// 有父结点则将插入排序的参数修改,然后回到循环开始插入{newKey = midKey;child = brother;parent = parent->_parent;}}}return true;}void _InOrder(Node* cur){if (cur == nullptr)return;// 左 根  左 根  ...  右size_t i = 0;for (; i < cur->_n; ++i){_InOrder(cur->_subs[i]); // 左子树cout << cur->_keys[i] << " "; // 根}_InOrder(cur->_subs[i]); // 最后的那个右子树}void InOrder(){_InOrder(_root);}
private:Node* _root = nullptr;
};void TestBtree()
{int a[] = { 53, 139, 75, 49, 145, 36, 101 };BTree<int, 3> t;for (auto e : a){t.Insert(e);}t.InOrder();
}

三、B+树和B*树

1.B+树

B+树是B树的变形,是在B树基础上优化的多路平衡搜索树,B树的规则跟B树基本类似,但是又在B树的基础上做了以下几点改进优化:

  1. 分支节点的子树指针与关键字个数相同
  2. 分支节点的子树指针p[i]指向关键字值大小在[k[i],k[i+1])区间之间
  3. 所有叶子节点增加一个链接指针链接在一起
  4. 所有关键字及其映射数据都在叶子节点出现

在这里插入图片描述

B+树的特性

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

B+树的分裂
当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增
加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向
兄弟的指针。

2.B*树

B*树是B+树的变形,在B+树的非根和非叶子节点再增加指向兄弟节点的指针。
在这里插入图片描述

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

3.总结

通过以上介绍,大致将B树,B+树,B*树总结如下:

  1. B树:有序数组+平衡多叉树;
  2. B+树:有序数组链表+平衡多叉树;
  3. B*树:一棵更丰满的,空间利用率更高的B+树。

相关文章:

【C++】B树及其实现

写目录 一、B树的基本概念1.引入2.B树的概念 二、B树的实现1.B树的定义2.B树的查找3.B树的插入操作4.B树的删除5.B树的遍历6.B树的高度7.整体代码 三、B树和B*树1.B树2.B*树3.总结 一、B树的基本概念 1.引入 我们已经学习过二叉排序树、AVL树和红黑树三种树形查找结构&#x…...

C++(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例

C(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例 文章目录 C(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例1、概述2、实现效果3、主要代码4、源码地址 更多精彩内容&#x1f449;个人内容分类汇总 &#x1f448;&#x1f449;GIS开发 &#x1f448; 1、概述 支持多线程加…...

CTFShow的RE题(三)

数学不及格 strtol 函数 long strtol(char str, char **endptr, int base); 将字符串转换为长整型 就是解这个方程组了 主要就是 v4, v9的关系&#xff0c; 3v9-(v10v11v12)62d10d4673 v4 v12 v11 v10 0x13A31412F8C 得到 3*v9v419D024E75FF(1773860189695) 重点&…...

WordPress主题开发进群付费主题v1.1.2 多种引流方式

全新前端UI界面&#xff0c;多种前端交互特效让页面不再单调&#xff0c;进群页面群成员数&#xff0c;群成员头像名称&#xff0c;每次刷新页面随机更新不重复&#xff0c;最下面评论和点赞也是如此随机刷新不重复 进群页面简介&#xff0c;群聊名称&#xff0c;群内展示&…...

SAP中的 UPDATA TASK 和 BACKGROUND TASK

前言&#xff1a; 记录这篇文章起因是调查生产订单报工问题引申出来的一个问题&#xff0c;后来再次调查后了解了其中缘由&#xff0c;大概记录以下&#xff0c;如有不对&#xff0c;欢迎指正。问题原贴如下&#xff1a; SAP CO11N BAPI_PRODORDCONF_CREATE_TT连续报工异步更…...

UDP协议:独特之处及其在网络通信中的应用

在网络通信领域&#xff0c;UDP&#xff08;用户数据报协议&#xff0c;User Datagram Protocol&#xff09;是一种广泛使用的传输层协议。与TCP&#xff08;传输控制协议&#xff0c;Transmission Control Protocol&#xff09;相比&#xff0c;UDP具有其独特的特点和适用场景…...

支持向量机(Support Vector Machine,SVM)及Python和MATLAB实现

支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是一种经典的机器学习算法&#xff0c;广泛应用于模式识别、数据分类和回归分析等领域。SVM的背景可以追溯到1990s年代&#xff0c;由Vladimir Vapnik等人提出&#xff0c;并在之后不断发展和完善。 …...

【RT-thread studio 下使用STM32F103-学习sem-信号量-初步使用-线程之间控制-基础样例】

【RT-thread studio 下使用STM32F103-学习sem-信号量-初步使用-线程之间控制-基础样例】 1、前言2、环境3、事项了解&#xff08;1&#xff09;了解sem概念-了解官网消息&#xff08;2&#xff09;根据自己理解&#xff0c;设计几个使用方式&#xff08;3&#xff09;不建议运行…...

使用nodejs输出著作权申请所需的word版源码

使用nodejs输出著作权申请所需的word版源码 背景 软件著作权申请需要提供一份80页的word版源代码&#xff0c;如果手工复制源码到word文档中&#xff0c;工作量将无聊到让任何一个DAO人员血压爆表&#xff0c;因此我们不得不编写一个简单的文本处理代码&#xff0c;通过自动方…...

[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解

[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解 共同的作用 JSX 支持&#xff1a;插件为 React 应用程序中的 JSX 语法提供支持&#xff0c;确保它可以被正确地转换为 JavaScript。Fast Refresh&#xff1a;提供热更新功能&#xff0c;当应用程序在开发服务器上…...

记一次使用“try-with-resources“的语法导致的BUG

背景描述 最近使用try-catch的时候遇到了一个问题&#xff0c;背景是这样的&#xff1a;当第一次与数据库建立连接以后执行查询完毕并没有手动关闭连接&#xff0c;但是当我第二次获取连接的时候报错了&#xff0c;显示数据库连接失败&#xff0c;连接已经关闭。 org.postgres…...

用Excel处理数据图像,出现交叉怎么办?

一、问题描述 用excel制作X-Y散点图&#xff0c;意外的出现了4个交叉点&#xff0c;而实际上的图表数据是没有交叉的。 二、模拟图表 模拟部分数据&#xff0c;并创建X-Y散点图&#xff0c;数据区域&#xff0c;X轴数据是依次增加的&#xff0c;因此散点图应该是没有交叉的。…...

SpringBoot | 大新闻项目后端(redis优化登录)

该项目的前篇内容的使用jwt令牌实现登录认证&#xff0c;使用Md5加密实现注册&#xff0c;在上一篇&#xff1a;http://t.csdnimg.cn/vn3rB 该篇主要内容&#xff1a;redis优化登录和ThreadLocal提供线程局部变量&#xff0c;以及该大新闻项目的主要代码。 redis优化登录 其实…...

ESP32——物联网小项目汇总

商品级ESP32智能手表 [文章链接] 用ESP32&#xff0c;做了个siri&#xff1f;&#xff01;开源了&#xff01; [文章链接]...

flutter:监听路由的变化

问题 当从路由B页面返回路由A页面后&#xff0c;A页面需要进行数据刷新。因此需要监听路由变化 解决 使用RouteObserver进行录音监听 创建全局变量&#xff0c;不在任何类中 final RouteObserver<PageRoute> routeObserver RouteObserver<PageRoute>();在mai…...

Linux多进程和多线程(六)进程间通信-共享内存

多进程(六) 共享内存共享内存的创建 示例: 共享内存删除 共享内存映射 共享内存映射的创建解除共享内存映射示例:写入和读取共享内存中的数据 写入: ### 读取: 大致操作流程: 多进程(六) 共享内存 共享内存是将分配的物理空间直接映射到进程的⽤户虚拟地址空间中, 减少数据在…...

ruoyi后台修改

一、日志文件过大分包 \ruoyi-admin\src\main\resources\logback.xml <!-- 系统日志输出 --> <appender name"file_info" class"ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/sys-info.log</file><!…...

macOS查看系统日志的方法

1、command空格键打开搜索框&#xff0c;输入‘控制台’并打开 2、选择日志报告&#xff0c;根据日期打开自己需要的文件就可以...

数字信号处理及MATLAB仿真(3)——采样与量化

今天写主要来编的程序就是咱们AD变换的两个步骤。一个是采样&#xff0c;还有一个是量化。大家可以先看看&#xff0c;这一过程当中的信号是如何变化的。信号的变换图如下。 先说说采样&#xff0c;采样是将连续时间信号转换为离散时间信号的过程。在采样过程中&#xff0c;连续…...

云端AI大模型群体智慧后台架构思考

1 大模型的调研 1.1 主流的大模型 openai-chatgpt 阿里巴巴-通义千问 一个专门响应人类指令的大模型。我是效率助手&#xff0c;也是点子生成机&#xff0c;我服务于人类&#xff0c;致力于让生活更美好。 百度-文心一言&#xff08;千帆大模型&#xff09; 文心一言"…...

算法系列--分治排序|再谈快速排序|快速排序的优化|快速选择算法

前言:本文就前期学习快速排序算法的一些疑惑点进行详细解答,并且给出基础快速排序算法的优化版本 一.再谈快速排序 快速排序算法的核心是分治思想,分治策略分为以下三步: 分解:将原问题分解为若干相似,规模较小的子问题解决:如果子问题规模较小,直接解决;否则递归解决子问题合…...

强化学习编程实战-1-一个及其简单的强化学习实例(多臂赌博机)

1.1 多臂赌博机 一台拥有K个臂的机器&#xff0c;玩家每次可以摇动K个臂中的一个&#xff0c;摇动后&#xff0c;会吐出数量不等的金币&#xff0c;吐出金币的数量服从一定的概率分布&#xff0c;而且不同臂的概率分布不同。 多臂赌博机的问题是&#xff1a;假设玩家共有N次摇地…...

Golang语法规范和风格指南(一)——简单指南

1. 前引 一个语言的规范的学习是重要的&#xff0c;直接关系到你的代码是否易于维护和理解&#xff0c;同时学习好对应的语言规范可以在前期学习阶段有效规避该语言语法和未知编程风格的冲突。 这里是 Google 提供的规范&#xff0c;有助于大家在开始学习阶段对 Golang 进行一…...

数据机构记录顺序表-笔记1

一、线性表的基本概念 数据元素&#xff1a;线性表中的基本单位&#xff0c;每个元素都是线性表的一部分。 数据项&#xff1a;数据元素的具体值。 存储位置&#xff1a;线性表中的元素在内存中的具体存储位置。 线性表按存储结构可以分为顺序表和链表两大类&#xff1a; 1.1…...

考研必备~总结严蔚敏教授《数据结构》课程的重要知识点及考点

作者主页&#xff1a;知孤云出岫 目录 1. 基本概念1.1 数据结构的定义1.2 抽象数据类型 (ADT) 2. 线性表2.1 顺序表2.2 链表 3. 栈和队列3.1 栈3.2 队列 4. 树和二叉树4.1 树的基本概念4.2 二叉树 5. 图5.1 图的基本概念5.2 图的遍历 6. 查找和排序6.1 查找6.2 排序 7. 重点考…...

【数据分享】国家级旅游休闲街区数据(Excel/Shp格式/免费获取)

之前我们分享过从我国文化和旅游部官网整理的2018-2023年我国50个重点旅游城市星级饭店季度经营状况数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01;文化和旅游部官网上也分享有很多与旅游相关的常用数据&#xff0c;我们基于官网发布的名单文件整理得到全国…...

Linux开发:进程间通过Unix Domain Socket传递数据

进程间传递数据的方式有很多种,Linux还提供一种特殊的Socket用于在多进程间传递数据,就是Unix Domain Socket(UDS)。 虽然通过普通的Socket也能做到在多进程间传递数据,不过这样需要通过协议栈层的打包与拆包,未免有些浪费效率,通过UDS,数据仅仅通过一个特殊的sock文件…...

Redis基础教程(九):redis有序集合

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…...

Servlet与Servlet容器

什么是Servlet? Servlet是Java EE&#xff08;现称Jakarta EE&#xff09;中的一个组件&#xff0c;通常用于创建动态Web内容。Servlet是运行在Web服务器上的Java程序&#xff0c;它处理客户端的请求并生成响应。Servlet的核心功能是处理HTTP请求和响应。下面是一个servlet例…...

腾讯centos mysql安装

腾讯centos mysql安装 腾讯云提供了一系列的云计算服务&#xff0c;包括操作系统、数据库、服务器等。在腾讯云上安装CentOS操作系统和MySQL数据库可以按照以下步骤进行&#xff1a; 登录腾讯云控制台&#xff08;登录 - 腾讯云&#xff09;。在控制台页面上方的搜索框中输入…...

c_各个unsigned int 和 int的取值范围

bool, uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t 取值范围分别是什么&#xff1f; 定义形式&#xff1a; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long uint64_…...

C#/WPF 自制截图工具

在日常使用电脑办公时&#xff0c;我们经常遇到需要截图然后保存图片&#xff0c;我们往往需要借助安装截图工具才能实现&#xff0c;现在我们通过C#自制截图工具&#xff0c;也能够轻松进行截图。 我们可以通过C#调用WindousAPI来实现截图&#xff0c;实例代码如下&#xff1a…...

以腾讯为例,手把手教你搭建产品帮助中心

一个精心设计的产品帮助中心对于提高用户满意度和体验至关重要。腾讯&#xff0c;作为全球领先的互联网企业&#xff0c;通过其多样化的产品线&#xff08;包括微信、QQ、腾讯游戏、腾讯视频等&#xff09;吸引了亿万用户。下面将以腾讯为例&#xff0c;向您展示如何搭建一个高…...

计算机网络概述--自我学习用

计算网络体系概述 相关问题 计算机网络为什么要分层&#xff1f;计算机网络是怎么分层的&#xff1f;三种计算机网络模型的关系是什么&#xff1f;每一层分别包含哪些协议&#xff1f;计算机网络中&#xff0c;数据如何在各层中传播&#xff1f;数据在网络各层中的存在形式是…...

超级好用的java http请求工具

kong-http 基于okhttp封装的轻量级http客户端 使用方式 Maven <dependency><groupId>io.github.kongweiguang</groupId><artifactId>kong-http</artifactId><version>0.1</version> </dependency>Gradle implementation …...

在原有的iconfont.css文件中加入新的字体图标

前言&#xff1a;在阿里图标库中&#xff0c;如果你没有这个字体图标的线上项目&#xff0c;那么你怎么在本地项目中的原始图标文件中添加新的图标呢&#xff1f; 背景&#xff1a;现有一个vue项目&#xff0c;下面是这个前端项目的字体图标文件。现在需要新开发功能页&#x…...

使用 ESP32-WROOM + DHT11 做个无屏温湿度计

最近梅雨天&#xff0c;有个房间湿度很大&#xff0c;而我需要远程查看温湿度&#xff0c;所以无所谓有没有显示屏&#xff0c;某宝上的温湿度计都是带屏的&#xff0c;如果连WIFI查看温湿度操作也比较麻烦&#xff0c;还需要换电池&#xff0c;实在不能满足我的需求&#xff0…...

如何使用 SwiftUI 构建 visionOS 应用

文章目录 前言WindowsVolumes沉浸式空间结论 前言 Apple Vision Pro 即将推出&#xff0c;现在是看看 SwiftUI API 的完美时机&#xff0c;这使我们能够将我们的应用程序适应 visionOS 提供的沉浸式世界。苹果表示&#xff0c;构建应用程序的最佳方式是使用 Swift 和 SwiftUI。…...

InspireFace-商用级的跨平台开源人脸分析SDK

InspireFace-商用级的跨平台开源人脸分析SDK InspireFaceSDK是由insightface开发的⼀款⼈脸识别软件开发⼯具包&#xff08;SDK&#xff09;。它提供了⼀系列功能&#xff0c;可以满⾜各种应⽤场景下的⼈脸识别需求&#xff0c;包括但不限于闸机、⼈脸⻔禁、⼈脸验证等。 该S…...

华为HCIP Datacom H12-821 卷24

1.单选题 企业大楼有大量员工通常都在上班时在大厅开始接入到公司的WLAN网络,随着每位员工走到各自的工位过程中,每个人的移动端叶通过漫游的方式漫游到各自的网络覆盖区域。为了尽量保证每个终端的IP地址是固定的,建议的做法是? A、配置VLAN Pool并配置顺序算法 B、…...

TikTok马来西亚直播网络怎么配置?

TikTok是一款全球流行的社交媒体应用&#xff0c;在东南亚地区拥有大量用户。在马来西亚这个多元化的国家&#xff0c;配置高效稳定的直播网络对TikTok的运营至关重要。 配置马来西亚直播网络的必要性 广泛的地理覆盖&#xff1a;马来西亚包括大片陆地和众多岛屿&#xff0c;网…...

基于若依的文件上传、下载

基于若依实现文件上传、下载 文章目录 基于若依实现文件上传、下载1、前端实现-文件上传1.1 通用上传分析1.2 修改实现上传接口 2、后端实现-文件上传3、后端实现-文件下载4、前端实现-文件下载 官网其实也写了&#xff0c;但是我是自己改造封装了一下&#xff0c;再次迈向全栈…...

论文回顾 | CVPR 2021 | How to Calibrate Your Event Camera | 基于图像重建的事件相机校准新方法

论文速览 | CVPR 2021 | How to Calibrate Your Event Camera | 基于图像重建的事件相机校准新方法 1 引言 在计算机视觉和机器人领域,相机校准一直是一个基础而又重要的问题。传统的相机校准方法主要依赖于从已知校准图案中提取角点,然后通过优化算法求解相机的内参和外参。这…...

高级java每日一道面试题-2024年7月1日

题目&#xff1a;请解释 Java 中的内存泄漏&#xff0c;并说明如何检测和避免内存泄漏。 答案&#xff1a; 内存泄漏指的是程序中不再使用的对象&#xff0c;由于某些原因没有被垃圾回收器回收&#xff0c;仍然占据着内存空间&#xff0c;导致可用内存逐渐减少&#xff0c;最…...

当需要对多个表进行联合更新操作时,怎样确保数据的一致性?

文章目录 一、问题分析二、解决方案三、示例代码&#xff08;以 MySQL 为例&#xff09;四、加锁机制示例五、测试和验证六、总结 在数据库管理中&#xff0c;经常会遇到需要对多个表进行联合更新的情况。这种操作带来了一定的复杂性&#xff0c;因为要确保在整个更新过程中数据…...

数据结构-线性表的应用

目录 前言一、有序表的合并1.1 顺序表实现1.2 单链表实现 二、稀疏多项式的相加和相乘2.1 稀疏多项式的相加2.2 稀疏多项式的相乘 总结 前言 本篇文章介绍线性表的应用&#xff0c;分别使用顺序表和单链表实现有序表的合并&#xff0c;最后介绍如何使用单链表实现两个稀疏多项…...

cpp http server/client

httplib 使用httplib库 basedemo server.cpp #include "httplib.h" #include <iostream> using namespace httplib;int main(void) {Server svr;svr.Get("/hello", [](const Request& req, Response& res) {std::cout << "lo…...

昇思25天学习打卡营第2天|MindSpore快速入门

打卡 目录 打卡 快速入门案例&#xff1a;minist图像数据识别任务 案例任务说明 流程 1 加载并处理数据集 2 模型网络构建与定义 3 模型约束定义 4 模型训练 5 模型保存 6 模型推理 相关参考文档入门理解 MindSpore数据处理引擎 模型网络参数初始化 模型优化器 …...

django之url路径

方式一&#xff1a;path 语法&#xff1a;<<转换器类型:自定义>> 作用&#xff1a;若转换器类型匹配到对应类型的数据&#xff0c;则将数据按照关键字传参的方式传递给视图函数 类型&#xff1a; str: 匹配除了”/“之外的非空字符串。 /test/zvxint: 匹配0或任何…...

【OnlyOffice】桌面应用编辑器,插件开发大赛,等你来挑战

OnlyOffice&#xff0c;桌面应用编辑器&#xff0c;最近版本已从8.0升级到了8.1 从PDF、Word、Excel、PPT等全面进行了升级。随着AI应用持续的火热&#xff0c;OnlyOffice也在不断推出AI相关插件。 因此&#xff0c;在此给大家推荐一下OnlyOffice本次的插件开发大赛。 详细信息…...