数据结构和算法(10):B-树
B-树:大数据
现代电子计算机发展速度空前,就存储能力而言,情况似乎也是如此:如今容量以TB计的硬盘也不过数百元,内存的常规容量也已达到GB量级。
然而从实际应用的需求来看,问题规模的膨胀却远远快于存储能力的增长。
在同等成本下,存储器的容量越大(小)则访问速度越慢(快)。
实践证明,分级存储是行之有效的方法。在由内存与外存(磁盘)组成的二级存储系统中,数据全集往往存放于外存中,计算过程中则可将内存作为外存的高速缓存,存放最常用数据项的复本。借助高效的调度算法,如此便可将内存的“高速度”与外存的“大容量”结合起来。
两个相邻存储级别之间的数据传输,统称I/O操作。(各级存储器的访问速度相差悬殊,故应尽可能地减少I/O操作)
以内存与磁盘为例,其单次访问延迟大致分别在纳秒(ns)和毫秒(ms)级别,相差5至6个数量级。对内存而言的一秒/一天,相当于磁盘的一星期/两千年。
多路搜索树(二叉搜索树与四路搜索树):将通常的二叉搜索树,改造为多路搜索树——在中序遍历的意义下,这也是一种等价变换。
以两层为间隔,将各节点与其左、右孩子合并为“大节点”:原节点及其孩子的共三个关键码予以保留;孩子节点原有的四个分支也予以保留并按中序遍历次序排列;节点到左、右孩子的分支转化为“大节点”内部的搜索,在图中表示为水平分支。如此改造之后,每个“大节点”拥有四个分支,故称作四路搜索树。
B-树:结构
所谓 m m m 阶 B-树,即 m m m 路平衡搜索树( m ≥ 2 m \geq 2 m≥2)
所有外部节点的深度统一相等。同时,每个内部节点都存有不超过m - 1
个关键码,以及用以指示对应分支的不超过 m
个引用。
具体地,存有 n ≤ m − 1 n \leq m - 1 n≤m−1 个关键码: K 1 < K 2 < K 3 < K 4 < . . . < K n K_1 < K_2 <K_3 < K_4 < ... < K_n K1<K2<K3<K4<...<Kn 的内部节点,同时还配有 n + 1 ≤ m n+1 \leq m n+1≤m 个引用: A 0 < A 1 < A 2 < A 3 < A 4 < . . . < A n A_0 < A_1 <A_2<A_3 < A_4 <...<A_n A0<A1<A2<A3<A4<...<An。
反过来,各内部节点的分支数也不能太少。具体地,除根以外的所有内部节点,都应满足: n + 1 ≥ [ m / 2 ] n+1 \geq [m /2] n+1≥[m/2],而在非空的 B-树中,根节点应满足: n + 1 ≥ 2 n+1 \geq 2 n+1≥2。
由于各节点的分支数介于 [ m / 2 ] [m/2] [m/2] 至 m m m 之间,故 m m m 阶B-树也称作 ( m / 2 m/2 m/2, m)-树。
所有叶节点的深度统一相等。
计算B-树高度时,还需要计入其最底层的外部节点:树高 h = 外部节点的深度
实现
B-树节点
#include "../vector/vector.h"
#define BTNodePosi(T) BTNode<T>* //B-树节点位置template <typename T> struct BTNode { //B-树节点模板类
// 成员(为简化描述起见统一开放,读者可根据需要迕一步封装)BTNodePosi(T) parent; //父节点Vector<T> key; //关键码向量Vector<BTNodePosi(T)> child; //孩子向量(其长度总比key多一)
// 构造函数(注意:BTNode叧能作为根节点创建,而且初始时有0个关键码和1个空孩子指针)BTNode() { parent = NULL; child.insert ( 0, NULL ); }BTNode ( T e, BTNodePosi(T) lc = NULL, BTNodePosi(T) rc = NULL ) {parent = NULL; //作为根节点,而且初始时key.insert ( 0, e ); //叧有一个关键码,以及child.insert ( 0, lc ); child.insert ( 1, rc ); //两个孩子if ( lc ) lc->parent = this; if ( rc ) rc->parent = this;}
};
B-树
#include“BTNode.h” //引入B-树节点类template <typename T> class BTree [ //B-树模板类
protected:int _size;//存放的关键码总数int _order; //B-树的阶次,至少为3--创建时指定,一般不能修改BTNodePosi(T)_root; //根节点BTNodePosi(T)_hot; //BTree::search()最后访问的非空( 除非树空)的节点位置void solveOverflow ( BTNodePosi(T) ); //因插入而上溢之后的分裂处理void solveUnderflow ( BTNodePosi(T) ); //因删除而下溢之后的合并处理
public:BTree ( int order = 3 ): _order ( order ),_size ( ) //构造函数:默认为最低的3阶{_root = new BTNode<T>();}~BTree(){ if ( _root ) release ( _root );} //析构函数:释放所有节点int const order(){ return _order; } //阶次int const size(){ return _size; } //规模BTNodePosi(T) & root() { return_root;} //树根bool empty() const { return !_root;} //判空BTNodePosi(T) search ( const T& e ); //查找bool insert ( const T& e ); //插入bool remove ( const T& e ); //删除
}; //BTree
B-树:查找
只载入必需的节点,尽可能减少 I/O 操作。
可以将大数据集组织为 B-树并存放于外存。对于活跃的B-树,其根节点会常驻于内存;此外,任何时刻通常只有另一节点(称作当前节点)留驻于内存。
过程:
先以根节点作为当前节点,然后再逐层深入。若在当前节点(所包含的一组关键码)中能够找到目标关键码,则成功返回。否则(在当前节点中查找“失败”),则必可在当前节点中确定某一个引用(“失败”位置),并通过它转至逻辑上处于下一层的另一节点。若该节点不是外部节点,则将其载入内存,并更新为当前节点,然后继续重复上述过程。
实现
template<typename T> BTNodePosi(T) BTree<T>::search ( const T& e ) //在B-树中查找关键码eBTNodePosi(T) v =_root; _hot = NULL; //从根节点出发while ( v ){ //逐层查找Rank r = v->key.search ( e ); //在当前节点中,找到不大于e的最大关键码if ( ( 0 <= r ) && ( e == v->key[r] ) ) return v; //成功:在当前节点中命中目标关键码_hot = v; v = v->hild[r + 1]; //否则,转入对应子树(_hot指向其父)--需做I/0,最费时间}//这里在向量内是二分查找,但对通常的_order可直接顺序查找return NULL; //失败:最终抵达外部节点
}
成功时返回目标关键码所在的节点,上层调用过程可在该节点内进一步查找以确定准确的命中位置;失败时返回对应外部节点,其父节点则由变量_hot
指代。
性能分析
B-树查找操作所需的时间消耗于两个方面:1.将某一节点载入内存;2.在内存中对当前节点进行查找。
对于高度为h
的B-树,外存访问不超过O(h - 1)
次.
若存有N
个关键码的m
阶B-树高度为h
,则必有: log m ( N + 1 ) ≤ h ≤ l o g [ m / 2 ] [ ( N + 1 ) / 2 ] + 1 \log_m(N+1) \leq h \leq log_{[m/2]}[(N+1)/2]+1 logm(N+1)≤h≤log[m/2][(N+1)/2]+1
也就是说,存有N
个关键码的m
阶B-树的高度 h = Θ ( log m N ) h = \Theta (\log_m N) h=Θ(logmN)。
每次查找过程共需访问 O ( log m N ) O(\log_m N) O(logmN) 个节点,相应地需要做 O ( log m N ) O(\log_m N) O(logmN) 次外存读取操作。由此可知,对存有 N N N 个关键码的 m m m 阶B-树的每次查找操作,耗时不超过 O ( l o g m N ) O(log_m N) O(logmN)。
尽管没有渐进意义上的改进,但相对而言极其耗时的I/O操作的次数,却已大致缩减为原先的1/log2 m
。
B-树:插入
关键码插入
template <typename T> boo BTree<T>::insert ( const T& e ){ //将关键码e插入B树中BTNodePosi(T) v = search ( e ); if ( v )return false; //确认目标节点不存在Rank r = _hot->key.search ( e ); //在节点_hot的有序关键码向量中查找合适的插入位置_hot->key.insert ( r + 1,e ); //将新关键码插至对应的位置_hot->child.insert ( r + 2,NULL ); //创建一个空子树指针_size++;//更新全树规模solveOverflow (_hot); //如有必要,需做分裂return true; //插入成功
}
为在B-树中插入一个新的关键码 e
,首先调用search(e)
在树中查找该关键码。若查找成功,则按照“禁止重复关键码”的约定不予插入,操作即告完成并返回false
。
查找过程必然终止于某一外部节点v
,且其父节点由变量_hot
指示。当然,此时的_hot
必然指向某一叶节点(可能同时也是根节点)。接下来,在该节点中再次查找目标关键码e
。尽管这次查找注定失败,却可以确定e
在其中的正确插入位置r
。最后,只需将e
插至这一位置。
至此,_hot
所指的节点中增加了一个关键码。若该节点内关键码的总数依然合法(即不超过m - 1
个),则插入操作随即完成。否则,称该节点发生了一次上溢(overflow),此时需要通过适当的处理,使该节点以及整树重新满足B-树的条件。
上溢与分裂
一般地,刚发生上溢的节点,应恰好含有 m m m 个关键码。若取 s = [ m / 2 ] s = [m/2] s=[m/2],则它们依次为: { k 0 , . . . , k s − 1 ; k s ; k s + 1 , . . . , k m − 1 } \{ k_0,...,k_{s-1};k_s;k_{s+1},...,k_{m-1} \} {k0,...,ks−1;ks;ks+1,...,km−1}。可见,以 k s k_s ks 为界,可将该节点分前、后两个子节点,且二者大致等长。于是,可令关键码 k s k_s ks上升一层,归入其父节点(若存在)中的适当位置,并分别以这两个子节点作为其左、右孩子。这一过程,称作节点的分裂。
实现
template<typename T> //关键码插入后若节点上溢,则做节点分裂处理
void BTree<T>::solveOverflow ( BTNodePosi(T) v ) {if (_order >= v->child.size() ) return; //递归基:当前节点并未上溢Rank s = _order / 2; //轴点(此时应有_order = key.size() = child.size() - 1)BTNodePosi(T) u = new BTNode<T>();//注意:新节点已有一个空孩子for ( Rank j= 0;j< _order - s - 1; j++ ) {//v右侧_order-s-1个孩子及关键码分裂为右侧节点uu->child.insert ( j,v->child.remove ( s +1));//逐个移动效率低u->key.insert ( j,v->key.remove ( s +1));//此策略可改进}u->child[_order - - 1] = V->child.remove ( s + );//移动v最靠右的孩子if ( u->child[e] ) //若u的孩子们非空,则for ( Rank j= ;j< _order - s; j++ ) //令它们的父节点统一u->child[j]->parent = u; //指向uBTNodePosi(T) p = v->parent; //v当前的父节点pif ( !p )[_root = p = new BTNode<T>(); p->child[0] = v; v->parent = p;} //若p空则创建之Rank r = 1 + p->key.search ( v->key[0] ); //p中指向u的指针的秩p->key.insert ( r,v->key.remove ( s ) ); //轴点关键码上升p->child.insert (r + 1,u );u->parent = p;//新节点u与父节点p互联solveOverflow ( p ); //上升一层,如有必要则继续分裂——至多递归O(logn)层
}
上溢持续传播至根的情况:原树根分裂之后,新创建的树根仅含单关键码。由此也可看出,就B-树节点分支数的下限要求而言,树根节点的确应该作为例外。
复杂度
若将B-树的阶次m
视作为常数,则关键码的移动和复制操作所需的时间都可以忽略。至于solveOverflow()算法,其每一递归实例均只需常数时间,递归层数不超过B-树高度。由此可知,对于存有N
个关键码的m
阶B-树,每次插入操作都可在O(logm N)
时间内完成。
B-树:删除
template <typename T> bool BTree<T>::remove ( const T& e ) { //从BTree树中删除关键码eBTNodePosi(T) v = search ( e ); if ( !v ) return false; //确认目标关键码存在Rank r = v->key.search ( e );//确定目标关键码在节点v中的秩(由上,肯定合法)if ( v->child[0] ) { //若v非叶子,则e的后继必属于某叶节点BTNodePosi(T) u = v->child[r+1]; //在右子树中一直向左,即可while ( u->child[0] ) u = u->child[0]; //找出e的后继v->key[r] = u->key[e]; v = u; r = 0; //并与之交换位置} //至此,v必然位于最底层,且其中第r个关键码就是待删除者v->key. remove ( r ); v->child.remove ( r + 1 ); _size--; //删除e,以及其下两个外部节点之一solveUnderflow ( v );// 如有必要,需做旋转或合并
return true;
为从 B-树中删除关键码 e
,也首先需要调用 search(e)
查找 e
所属的节点。倘若查找失败,则说明关键码 e
尚不存在,删除操作即告完成:否则按照代码的出口约定,目标关键码所在的节点必由返回的位置v
指示。此时,通过顺序查找,即可进一步确定e
在节点v
中的秩r
。
不妨假定v
是叶节点——否则,e
的直接前驱(后继)在其左(右)子树中必然存在,而且可在O(height(v))
时间内确定它们的位置,其中height(v)
为节点v
的高度。此处不妨选用直接后继。于是,e
的直接后继关键码所属的节点u
必为叶节点,且该关键码就是其中的最小者u[0]
。既然如此,只要令e
与u[0]
互换位置,即可确保待删除的关键码e
所属的节点v
是叶节点。
接下来可直接将e
(及其左侧的外部空节点)从v
中删去。如此,节点v
中所含的关键码以及(空)分支将分别减少一个。若该节点所含关键码的总数依然合法(即不少于[m/2] - 1
),则删除操作随即完成。否则,称该节点发生了下溢,并需要通过适当的处置,使该节点以及整树重新满足 B-树的条件。
下溢与合并
在m
阶B-树中,刚发生下溢的节点V
必恰好包含[m/2] - 2
个关键码和[m/2] - 1
个分支。
1.V
的左兄弟 L
存在,且至少包含 [m/2]
个关键码
将
y
从节点P
转移至节点V
中(作为最小关键码),再将x
从L
转移至P
中(取代原关键码y
)
2.V
的右兄弟 R
存在,且至少包含 [m/2]
个
3.V
的左、右兄弟 L
和R
或者不存在,或者其包含的关键码均不足 [m/2]
个
从父节点P
中抽出介于L
和V
之间的关键码y
,并通过该关键码将节点L
和V
“粘接”成一个节点——这一过程称作节点的合并。
在经如此合并而得新节点中,关键码总数应为:([m/2] - 1) + 1 + ([m/2] - 2) = 2*[m/2] - 2 <= m - 1
故原节点V
的下溢缺陷得以修复,而且同时也不致于反过来引发上溢。
修复之后仍可能导致祖父节点以及更高层节点的下溢——这种现象称作下溢的传递。特别地,当下溢传递至根节点且其中不再含有任何关键码时,即可将其删除并代之以其唯一的孩子节点,全树高度也随之下降一层。
与上溢传递类似地,每经过一次下溢修复,新下溢节点的高度都必然上升一层。整个下溢修复的过程中至多需做O(log m N)
次节点合并操作。
复杂度
与插入操作同理,在存有N
个关键码的m
阶 B-树中的每次关键码删除操作,都可以在O(logm N)
时间内完成。另外同样地,因某一关键码的删除而导致 O ( l o g m N ) \mathcal O(log m N) O(logmN) 次合并操作的情况也极为罕见,单次删除操作过程中平均只需做常数次节点的合并。
相关文章:

数据结构和算法(10):B-树
B-树:大数据 现代电子计算机发展速度空前,就存储能力而言,情况似乎也是如此:如今容量以TB计的硬盘也不过数百元,内存的常规容量也已达到GB量级。 然而从实际应用的需求来看,问题规模的膨胀却远远快于存储能…...

VR会议:远程带看功能,专为沉浸式云洽谈而生
随着科技的不断发展,VR技术已经成为当今市场上较为热门的新型技术之一了,而VR会议远程带看功能,更是为用户提供更加真实、自然的沉浸式体验。 随着5G技术的发展,传统的图文、视频这种展示形式已经无法满足消费者对信息真实性的需求…...

实验室管理系统LIMS
在数字化浪潮中,越来越多的企业开始有数字化转型的意识。对于实验室而言,数字化转型是指运用新一代数字技术,促进实验室业务、生产、研发、管理、服务、供应链等方面的转型与升级,实现实验室业务“人、机、料、法、环”的多维度发…...

开源ERP和CRM套件Dolibarr
什么是 Dolibarr ? Dolibarr ERP & CRM 是一个现代软件包,用于管理您组织的活动(联系人、供应商、发票、订单、库存、议程…)。它是开源软件(用 PHP 编写),专为中小型企业、基金会和自由职业…...

视频号双11激励政策,快来看一看
双十一即将来临,不少平台都公布了自己的双十一政策。这篇文章,我们来看看视频号推出的激励政策,看有哪些需要准备的。...

Maven最新版本安装及配置
Maven是一个Java项目管理和构建工具,它可以定义项目结构、项目依赖,并使用统一的方式进行自动化构建,是Java项目不可缺少的工具。 本章我们详细介绍如何使用Maven。 一、Maven是什么? 如果每一个项目都自己搞一套配置…...

探索ClickHouse——使用MaterializedPostgreSQL同步PostgreSQL数据库
安装PostgreSQL sudo apt install postgresql修改配置 sudo vim /etc/postgresql/14/main/postgresql.conf 解开并修改wal_level 的配置项 wal_level logical 重启服务 /etc/init.d/postgresql restartRestarting postgresql (via systemctl): postgresql.service AUTHENTI…...

《向量数据库指南》——向量数据库 有必要走向专业化吗?
向量数据库 有必要走向专业化吗? 向量数据库系统的诞生,来源于具体业务需求——想要高效处理海量的向量数据,就需要更细分、更专业的数据基础设施,为向量构建专门的数据库处理系统。 但这种路径是必须的吗? 从产品层面讲,如果传统数据库厂商不单独研发向量数据库,那么…...

你必须知道的数据查询途径!!
在当今信息爆炸的时代,我们每天都会面临海量的数据和信息。如何在这些繁杂的信息中快速、准确地找到自己需要的内容,也是当代一个非常重要的技能。下面,我将介绍几种你必须知道的企业数据信息查找途径。 1. 搜索引擎 搜索引擎是我们日常中…...

火焰原子吸收光谱法、容量法和电感耦合等离子体发射光谱法
声明 本文是学习GB-T 1871.5-2022 磷矿石和磷精矿中氧化镁含量的测定 火焰原子吸收光谱法、容量法和电感耦合等离子体发射光谱法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件描述了在磷矿石和磷精矿中测定氧化镁含量的火焰原子吸收…...

亚马逊云科技 2023 柏林峰会主题演讲总结
欢迎来到我们的亚马逊云科技2023柏林峰会主题演讲全面总结!在这篇文章中,我们将深入探讨在活动期间分享的主要公告、亮点和故事。通过这里的视频格式,展示了亚马逊云科技技术如何转化为商业和行业。 每年,亚马逊云科技峰会都会汇…...

CentOS Stream9 安装远程桌面服务 Xrdp
1. 安装 XRDP 若服务器本身没有桌面则首先需要安装本地桌面: yum -y groups install "GNOME Desktop" startx配置源: dnf install epel-release安装 xrdp dnf install xrdp 2. 配置 Xrdp Xrdp 配置文件位于 /etc/xrdp 目录中。对于常规 X…...

实施运维01
一.运维实施工程师所具备的知识 1.运维工程师,实施工程师是啥? 运维工程师负责服务的稳定性,确保服务无间断的为客户提供服务. 实施工程师负责工程的实施工作,负责现场培训,一般都要出差,哪里有项目就去…...

MySQL大表直接复制文件的copy方式
看腻了就来听听视频演示吧:https://www.bilibili.com/video/BV1Bp4y1F7kd/ MyISAM引擎可单独将 *.MYD和 *.MYI 拷贝到远程服务器上InnoDB引擎受限于版本(MySQL5.5)无法直接拷贝.ibd文件,因为在ibdata1文件保存有表的字典信息&…...

Redis-集群
Redis-集群 主从复制和哨兵只能在主节点进行写数据,从节点读取数据,因此本质上,是进行了读写的分离,每个节点都保存了所有的数据,并不能实现一个很好的分布式效果。 1.哈希求余算法 假设有N台主机,对每台…...

使用CrawlSpider爬取全站数据。
CrawpSpider和Spider的区别 CrawlSpider使用基于规则的方式来定义如何跟踪链接和提取数据。它支持定义规则来自动跟踪链接,并可以根据链接的特征来确定如何爬取和提取数据。CrawlSpider可以对多个页面进行同样的操作,所以可以爬取全站的数据。CrawlSpid…...

【JUC】Java并发编程从挖坑到入土全解(4-一文讲通LockSupport与线程中断->长图预警)
目录 LockSupport与线程中断 线程中断机制 什么是中断机制? 与中断相关的3个API 如何停止中断运行中的线程? 当前线程的中断标识为true,是不是线程就会立刻停止? 如何理解静态方法Thread.interrupted() LockSupport是什么…...

Springboot学习笔记——3
Springboot学习笔记——3 一、热部署1.1、手动启动热部署1.2、自动启动热部署1.3、热部署范围配置1.4、关闭热部署 二、配置高级2.1、第三方bean属性绑定2.2、松散绑定2.3、常用计量单位应用2.4、bean属性校验2.5、进制数据转换规则 三、测试3.1、加载测试专用属性3.2、加载测试…...

jupyter 切换虚拟环境
当前只有两个环kernel 我已经创建了很多虚拟环境,如何在notebook中使用这些虚拟环境呢?请看下面 比如说我要添加nlp 这个虚拟环境到notebook中 1. 切换到nlp环境 2. 安装如下模块 pip install ipykernel 3. 执行如下命令 python -m ipykernel install …...

如何在Apache和Resin环境中实现HTTP到HTTPS的自动跳转:一次全面的探讨与实践
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...

安全防御—密码学
1. 什么是APT? APT(Advanced Persistent Threat)是指高级持续性威胁,本质是针对性攻击。 利用先进的攻击手段对特定目标进行长期持续性网络攻击的攻击形式,APT攻击的原理相对于其他攻击形式更为高级和先进,…...

灯具从深圳寄国际物流到墨西哥
在国际贸易的日益频繁的今天,越来越多的企业开始将产品销往海外市场。然而,如何将这些产品安全、快速地送达目的地,成为了每个企业都需要面对的问题。对于灯具这种重量大、体积大的物品来说,如何选择合适的国际物流方式࿰…...

spark3使用hive zstd压缩格式总结
ZSTD(全称为Zstandard)是一种开源的无损数据压缩算法,其压缩性能和压缩比均优于当前Hadoop支持的其他压缩格式,本特性使得Hive支持ZSTD压缩格式的表。Hive支持基于ZSTD压缩的存储格式有常见的ORC,RCFile,Te…...

直线导轨精度等级在设备中有什么影响?
直线导轨的精度选择是直线导轨应用中的重要环节,需要根据具体的应用场景和设备要求来选择合适的精度等级(常见分3个等级:N/H/P)。下面我们来详细了解一下直线导轨的精度选择。 1、精度等级的概念:直线导轨的精度等级是…...

windows平台FairMOT的实现
环境:python3.6pytorch1.1.0torchvision0.3.0cuda9.2vs2015 该项目需要装3个c库(dcn_v2,apex,cython_bbox)特别坑,各种环境不匹配,各种bug。本人c小白,但是一路摸索总算成功了。下面…...

系统架构设计:12 论软件维护方法及其应用
目录 一 软件维护方法 1 影响软件维护工作的因素 2 软件维护类型 (1)正确性维护...

SS命令使用介绍
ss 一. 命令介绍 先使用手册查看命令介绍信息 NAME ss - another utility to investigate sockets DESCRIPTION ss is used to dump socket statistics. It allows showing information similar to netstat. It can display more TCP and state informations than other tools.…...

让你的对象变得拗口:JSON.stringify(),我把对象夹进了 JSON 魔法帽!
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 引言 1. JSON.stringify() 属性 replacer …...

TDengine时序数据库学习使用
数据库要求: 1.目前服务器只能在linux运行,先安装服务器版本v1 2.下载与v1完全匹配的客户端版本v1(客户端与服务器的版本号不匹配可能访问不了服务器) 第一步 安装 安装服务器注意,安装教程: 使用安装…...

算法通过村第十三关-术数|青铜笔记|数字与数学
文章目录 前言数字统计专题符号统计阶乘0的个数 溢出问题整数反转字符串转整数回文数 进制专题七进制数进制转换 总结 前言 提示:生活是正着来生活,倒着去理解。 --戴维迈尔斯《社会心理学》 数学是学生时代掉头发的学科,那算法是毕业后掉头发…...