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

大话数据结构-查找-多路查找树

注:本文同步发布于稀土掘金。

7 多路查找树

  多路查找树(multi-way search tree),其每个结点的孩子可以多于两个,且每一个结点处可以存储多个元素。由于它是查找树,所有元素之间存在某种特定的排序关系。

7.1 2-3树

7.1.1 生成2-3树

  2-3树是这样的一棵多路查找树:其中的每一个结点都有两个孩子(我们称它为2结点)或三个孩子(我们称它为3结点)。

  一个2结点包含一个元素和两个孩子(或没有孩子),且与二叉排序树类似,左子树包含的元素小于该元素,右子树包含的元素大于该元素。不过,与二叉排序树不同,这个2结点要么没有孩子,要么就有两个,不能只有一个孩子。

  一个3结点包含一小一大两个元素和三个孩子(或没有孩子),一个3结点要么没有孩子,要有就有3个孩子,如果某个3结点有孩子的话,左子树包含小于较小元素的元素,右子树包含大于较大元素的元素,中间子树包含介于两元素之间的元素。

  总之,2-3树有以下特性:

  (1) 对于每一个结点有1或者2个元素;

  (2) 当节点有1个元素时,节点有0个子树或2个子树;

  (3) 当节点有2个元素时,节点有0个子树或3个子树。

  (4) 所有叶子点都在树的同一层;

  如下是一棵有效的2-3树:

  来看2-3树的生成,假设我们要根据以下数组生成一棵2-3树:


  由于2-3树的每个结点可以放置1至2个元素,因此我们使用如下结构来表示一个结点,其中x和y分别表示元素值:

  然后我们来按序插入元素,由于一个结点可以存放2个元素,因此前两个元素,直接生成一个根结点:

  对于第三个元素12,因为当前2-3树只有一个结点,因此只需要将元素12插入该结点即可,而由于该结点已有二个元素,因此不能直接插入,而是需要将其中一个元素顶上去作为父结点,我们把被插入的结点设为node,待插入的元素则构建一个只有一个元素的结点,如下所示:

  然后 将 n e w N o d e 中的元素先插入到 n o d e 中,如果 n e w N o d e 有孩子结点,也将之作为 n o d e 的孩子 \color{red}{将newNode中的元素先插入到node中,如果newNode有孩子结点,也将之作为node的孩子} newNode中的元素先插入到node中,如果newNode有孩子结点,也将之作为node的孩子

  这时,根据node元素数量进行不同处理——如果node结点数量小于3个,则不进行任何处理,否则将所有元素按最大结点数量分成多段,然后从第一段开始,将每段的最后一个元素往上提,作为父结点

  然后插入元素1,直接插入即可:

  接下来插入元素13:

  然后是元素9,先将它插入到12、13所在的结点,然后构建子树,然后将被顶上去的元素12,插入到结点8:

  为方便后续描述,我们把要插入的结点设置为newNode,被插入的结点设置为node。

  然后插入元素10:

  然后插入元素15:

  然后插入元素6,首先插入到1、4所在的结点,并生成子树:

  然后把新生成的子树当成新的newNode,把原来1、4所在的结点的父结点,当成新的node,进行处理:

  这时,发现根结点有三个元素,按照规则“如果node结点数量小于3个,则不进行任何处理,否则将所有元素按最大结点数量分成多段,然后从第一段开始,将每段的最后一个元素往上提,作为父结点”进行处理,并加入新的规则“然后将所有孩子结点按每个被拆分的元素个数+1,分配到新生成的子结点上”

  如上图所示,孩子结点有四个,由于第一个新的子结点只有一个元素,因此分配两个孩子给它,另一个新子结点也只有一个元素,因此也分配两个孩子给它。

  然后插入元素7:

  然后插入元素14:

  然后插入元素3:

  然后插入元素5:

  然后插入元素11,参考上述规则,得到如下结果:

  然后插入元素2,参考上述规则,得到如下结果:

  此时2-3树创建完成。

7.1.2 删除2-3树上的结点

  为保证2-3树的特性,在2-3树上删除元素,有以下规则:

  (1) 若删除的是叶子结点上的元素,且叶子结点的元素个数大于1,则直接将该元素删除;

  (2) 若删除的是叶子结点上的元素,且叶子结点的元素个数等于1,则先将此结点删除,然后从parent开始按规则”将parent的所有子结点的元素添加到parent中,将parent的所有子结点的孩子也添加到parent中,然后根据parent重新构建子树“向上迭代,直到parent的元素个数大于最大孩子数量,或者parent为null时,停止迭代;

  (3) 若删除的是非叶子结点上的元素,则找到该元素的下标,然后找到该下标对应的孩子树上的最大值,将孩子树上的最大值与要删除的这个元素替换,然后根据(1)、(2)相同的规则,删除新的这个叶子结点上的元素;

  我们通过示例来看,假设有以下2-3树:

  假设我们要删除元素1,由于要删除的是叶子结点上的元素,且该结点只有一个元素,按照上述规则,我们先删除元素1所在的结点:

  然后找到parent结点,即元素为2的结点,将其所有孩子的元素和孩子的所有孩子加到parent结点上,并删除原来的孩子结点:

  发现parent的元素个数仍小于最大孩子数量(2-3树的最大孩子数量是3个),因此找到parent的parent,继续按规则处理:

  新结点的元素数量仍小于3,于是继续往上迭代,直到迭代到根结点,元素的数量也不超过3,这时停止迭代,树已经变成一棵正常的2-3树,操作结束:


  接下来我们来删除元素20,首先,找到20所在的结点,发现它只有一个元素,因此找到此结点的第一棵树上的最大元素,将之与20替换:


  然后删除替换后的结点上的元素,由于替换后的结点也只有一个元素,因此按照规则,一层层往上迭代:

  迭代到根结点时,发现根结点的元素数量超过了3,于是重新生成树,规则与插入时重新生成树的方法一致——将所有结点合到一起,然后按最大结点数分成多段,然后把每一段的最大元素往上提作为父结点的元素,最后把所有孩子按新生成的结点的元素数量进行分配

  以本例为例,parent有5个元素,而每个结点最大的元素数量是2,因此将它们分成“8、12”、“16、24”和“28”三段,然后将前面两段的最大值往上提作为父结点,即得到如下结构:

  然后将6个孩子结点,根据新孩子的元素数量进行分配:

  而如果要删除元素18,由于它是叶子结点上的元素,且该结点有两个元素,因此直接删除即可:

  我们来删除元素12,发现它不是叶子结点上的元素,且其在该结点上的索引为0,因此我们找到children[0]子树上的最大元素,即11,并将这两个元素替换:

  然后删除当前元素12所在的结点,并按照规则向上迭代:

  其他元素的删除,则不再演示,按规则处理即可。

7.2 2-3-4树

7.2.1 生成2-3-4树

  2-3-4树是2-3树的扩展,因此2-3-4树有以下特性:

  (1) 对于每一个结点有1或者2或者3个元素;

  (2) 当节点有1个元素时,节点有0个子树或2个子树;

  (3) 当节点有2个元素时,节点有0个子树或3个子树。

  (4) 当节点有3个元素时,节点有0个子树或4个子树。

  (4) 所有叶子点都在树的同一层;

  我们同样用以下数组来构建2-3-4树:

  对于前三个元素,因为一个结点最多可以放置三个元素,因此直接将它们依序放置到一个结点即可:

  对于第四个元素1,因为当前2-3-4树只有一个结点,因此只需要将元素1插入该结点即可,而由于该结点已有三个元素,因此不能直接插入,而是需要将其中一个元素顶上去作为父结点,我们把被插入的结点设为node,待插入的元素则构建一个只有一个元素的结点,如下所示:

  然后 将 n e w N o d e 中的元素先插入到 n o d e 中,如果 n e w N o d e 有孩子结点,也将之作为 n o d e 的孩子 \color{red}{将newNode中的元素先插入到node中,如果newNode有孩子结点,也将之作为node的孩子} newNode中的元素先插入到node中,如果newNode有孩子结点,也将之作为node的孩子

  这时,根据node元素数量进行不同处理——如果node结点数量小于4个,则不进行任何处理,否则将所有元素按每组3个,分成多组,处理方式与2-3树的插入一致

  插入元素13,根据上述规则,直接插入到12所在的结点即可:

  接下来插入元素9:

  接下来插入元素10,首先将元素10与元素9、12、13所在的结点,生成一棵新的子树:

  接下来处理新的子树的原子树,即以下两棵子树:

  我们先将原子树上,9、12、13元素所在的结点从树上去除:

  然后仍按上述规则,设置node和newNode,然后把newNode的元素加入node,并把newNode的所有孩子都加入node的孩子,因新的根结点8、12的元素数量小于3,因此插入结束:

  接下来插入元素15,直接插入即可:

  接下来插入元素6,仍是直接插入:

  接下来插入元素7,先处理元素7和元素1、4、6所在的结点,生成一棵新子树:

  然后将原树中的1、4、6所在的结点删除,生成新子树,并将两棵子树合并处理:

  接下来插入元素14,直接插入即可:

  接下来插入元素3,直接插入即可:

  接下来插入元素5,先处理元素5和元素1、3、4所在的结点,生成一棵子树:

  然后将原树中的1、3、4所在的结点去除,生成新子树,将上一步生成的子树与此子树合并:

  发现根结点的元素数量超过3个,于是按规则处理,即——将所有元素按每组3个分组,然后将除最后一组外的其他组的最后一个元素,提升为父结点,然后将所有孩子(现在是孙子)按新子结点元素个数进行分配,即“4、6、8”一组,“12”一组,然后将8往上提,再将五个孩子分配——“4、6”结点有两个元素,因此分配3个孩子,“12”只有一个孩子,因此分配两个:

  然后插入元素11,直接插入即可:

  然后插入元素2,直接插入即可:

  2-3-4树创建的代码与2-3树创建代码一致。

7.2.2 删除2-3-4树上的元素

  删除逻辑与2-3树一致,不再赘述。

7.3 B树

  B树(B tree)是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例,结点最大的孩子数目称为B树的阶(order),因此,2-3树是3阶的B树,2-3-4树是4阶的B树。

  一个m阶的B树具有如下属性:

  (1) 每个结点要么是叶子结点,要么拥有2~n个孩子,孩子数量是结点中元素数量+1,即2元素的结点拥有3个孩子,5元素的结点拥有6个孩子;

  (2) 所有叶子结点都在同一层次;

  (3) 若结点有孩子,则结点中的每个元素,都会有对应的一个小于该结点的孩子,和一个大于该结点的孩子;

  B树的元素插入和删除,与2-3树、2-3-4树一致。

7.4 B+树

  B+树是应文件系统所需而出的一种B树的变形树,在B树中,每一个元素在该树中只出现一次,有可能在叶子结点上,也有可能在分支结点上,而在B+树中,出现在分支结点中的元素会被当作它们在该分支结点位置的中序后继者(叶子结点)中再次列出,另外,每一个叶子结点都会保存一个指向后一叶子结点的指针。

  这样一来,一棵B+树就被分成了两部分,主要部分是叶子结点,保存了所有数据信息和指针,而除叶子结点外的其他结点,均为索引。

  如下是一棵B树和其对应的B+树:

  可以看到,B+树有很明确的特征:

  (1) 非叶子结点与B树相同;

  (2) 叶子结点除了存储元素外,还存放着中遍历的下一个结点索引,例如结点1,如果进行中序遍历的话,接下来的元素是2,因此将2的索引放在结点1上;

  (3) 叶子结点除了存储元素外,还存放着后一叶子结点的指针,即其右兄弟或堂兄弟的指针;

7.4.1 B+树的插入

  我们仍然按下述数组来生成B+树:

  首先是第1、2个元素,直接插入:

  然后是第3个元素,按照2-3树的插入方法,按如下插入:

  注意看,与普通的2-3树相比,结点4上多了一个指针,指向元素8,8是如何来的呢?是其parent上,比4大的第一个元素。

  另外,结点4不仅多了一个索引8,还有一个指针,指向了其兄弟结点,这个索引怎么来的呢?规则是——如果不需要重新生成树,则一般直接插入即可,若需要生成树,则:

  (1) 在重新生成树前,记住被插入结点的leftBrother(记为originalLeftBrother)、rightBrother(记为originalRightBrother)和nextElement(记为originalNextElement);

  (2) 重新生成树后,再重新迭代新的孩子,若originalLeftBrother不为null,则令originalLeftBrother的rightBrother为第一个新孩子;

  (3) 重新生成树后,若最后一个新孩子没有孩子,则令其nextElement和rightBrother为前面记住的originalNextElement和originalRightBrother,若最后一个新孩子有孩子,则nextElement和rightBrother都为null;

  (4) 然后迭代新孩子,若这些新孩子都没有孩子结点,则除最后一个新孩子外,每一个新孩子的nextElement为新的parent的elements的同索引元素,每一个新孩子的rightBrother为下一个新孩子;若这些新孩子有孩子结点,则nextElement和rightBrother都为null;

  (5) 最后设置新生成树的根结点的rightBrother和nextElement为null

  在本例中,由于原结点没有孩子,因此:

  (1) 被插入的结点是4和8所在的结点,originalLeftBrother=null,originalRightBrother=null,originalNextElement=null;

  (2) 重新生成树后,第一个孩子是4所在的结点,因originalLeftBrother为null,因此不用处理;

  (3) 重新生成树后,最后一个孩子是12,因originalRightBrother和originalNextElement为null,直接设置结点12的nextElement为null,rightBrother为null;

  (4) 除最后一个孩子12外,只有一个新孩子,即结点4,它在所有孩子中索引是0,因此设置其nextElement为parent.elements[0],设置其rightBrother为下一个孩子,即结点12这个孩子;

  (5) 新生成的树的根结点是结点[8],令其nextElement和rightBrother都为null;

  接下来插入元素1和元素13,直接插入,结点1、4对应的索引和指针不需要变更:

  接下来插入元素9,按照2-3树的插入规则,首先把9插入元素12、13所在的结点,进行变换后,加上nextElement索引和rightBrother索引,按以下步骤执行:

  (1) 被插入的结点是12、13所在的结点,originalLeftBrother=[1,4]结点,originalRightBrother=null,originalNextElement=null;

  (2) 重新生成树后,第一个孩子是9所在的结点,因此令originalLeftBrother,即[1,4]结点的rightBrother为结点9;

  (3) 重新生成树后,最后一个孩子是13,因originalRightBrother和originalNextElement为null,直接设置结点13的nextElement为null,rightBrother为null;

  (4) 除最后一个孩子13外,只有一个新孩子,即结点9,它在所有孩子中索引是0,因此设置其nextElement为parent.elements[0],即12,设置其rightBrother为下一个孩子,即结点13这个孩子;

  (5) 新生成的树的根结点是结点[12],令其nextElement和rightBrother都为null;

  然后进行二次变形,把元素12插入结点8,同样的规则:

  (1) 被插入的结点是8所在的结点,originalLeftBrother=null,originalRightBrother=null,originalNextElement=null;

  (2) 重新生成树后,第一个孩子是[1,4]结点,因originalLeftBrother为null,因此不需要处理;

  (3) 重新生成树后,最后一个孩子是13,因originalRightBrother和originalNextElement为null,直接设置结点13的nextElement为null,rightBrother为null;

  (4) 除最后一个孩子13外,有两个孩子[1,4]和[9],在孩子数组中索引分别是0和1,因此child[1,4].nextElement=parent.elements[0]=8,child[1,4].rightBrother=parent.children[0+1]=child[9],child[9].nextElement=parent.elements[1]=12,child[9].rightBrother=parent.children[1+1]=child[13];

  (5) 新生成的树的根结点是结点[8,12],令其nextElement和rightBrother都为null;

  元素10和15是直接插入,无需进行任何变更:

  来看元素6的插入,首先按插入规则进行处理:

  (1) 被插入的结点是[1,4]结点,originalLeftBrother=null,originalRightBrother=[9,10]结点,originalNextElement=8;

  (2) 重新生成树后,第一个孩子是[1]结点,因originalLeftBrother为null,因此不需要处理;

  (3) 重新生成树后,最后一个孩子是[6]结点,令其nextElement=originalNextElement=8,令其rightBrother=originalRightBrother=[9,10]结点;

  (4) 除最后一个孩子6外,只有一个孩子[1],在孩子数组中索引是0,因此child[1].nextElement=parent.elements[0]=4,child[1].rightBrother=parent.children[0+1]=child[6];

  (5) 新生成的树的根结点是结点[4],令其nextElement和rightBrother都为null;

  然后继续插入,被插入的结点为[8,12]:

  (1) 被插入的结点是[8,12]结点,originalLeftBrother=null,originalRightBrother=null,originalNextElement=null;

  (2) 重新生成树后,第一个孩子是[4]结点,因originalLeftBrother为null,因此不需要处理;

  (3) 重新生成树后,最后一个孩子是[12]结点,令其nextElement=originalNextElement=null,令其rightBrother=originalRightBrother=null;

  (4) 除最后一个孩子[12]外,只有一个孩子[4],由于其有孩子结点,因此设置nextElement和rightBrother都为null;

  (5) 新生成的树的根结点是结点[8],令其nextElement和rightBrother都为null;

  其他元素的插入则按规则进行,不再赘述。

7.4.1 B+树的元素删除

  B+树的删除逻辑与2-3树或2-3-4树的删除逻辑一致,但需要加上leftBrother、rightBrother和nextElement的处理,我们以以下B+树为例进行处理:

  首先来删除元素7,由于该结点只有一个元素,相关结点的nextElement和rightElement需要进行调整,规则与新增时类似,如果不需要重新生成树,则一般直接删除元素即可,如果需要重新生成树,则

  (1) 在重新生成树前,记住被删除结点父结点的第一个孩子的leftBrother(记为originalLeftBrother),和最后一个孩子的rightBrother(记为originalRightBrother)和nextElement(记为originalNextElement);

  (2) 重新生成树后,若重新生成的树有孩子,则迭代新的孩子,若originalLeftBrother不为null,则令originalLeftBrother的rightBrother为第一个新孩子;而若重新生成的树没有孩子,且originalLeftBrother不为null,则令originalLeftBrother的rightBrother为重新生成的树的根结点;

  (3) 重新生成树后,若重新生成的树有孩子,且最后一个新孩子没有孩子,则令其nextElement和rightBrother为前面记住的originalNextElement和originalRightBrother,若最后一个新孩子有孩子,则nextElement和rightBrother都为null;若重新生成的树没有孩子,则直接令重新生成的树的根结点的nextElement为originalNextElement、rightBrother为originalRightBrother;

  (4) 若重新生成的树有孩子,则迭代新孩子,若这些新孩子都没有孩子结点,则除最后一个新孩子外,每一个新孩子的nextElement为新的parent的elements的同索引元素,每一个新孩子的rightBrother为下一个新孩子;若这些新孩子有孩子结点,则nextElement和rightBrother都为null;

  (5) 若重新生成的树有孩子,则设置新生成树的根结点的rightBrother和nextElement为null;

  对于本例,则是如下:

  (1) 被删除的结点是结点[7],其父是[4,6]结点,因此originalLeftBrother=null,originalNextElement=8,originalRightBrother=[9,10]结点;

  (2) 重新生成树后,第一个孩子结点是结点[1,2],因originalLeftBrother=null,因此不进行处理;

  (3) 重新生成树后,最后一个孩子结点是[4,5,6],因此令其nextElement=originalNextElement=8,令其rightBrother=originalRightBrother=[9,10]结点;

  (4) 然后迭代新孩子,除最后一个新孩子外,只有一个孩子[1,2],于是child[1,2].nextElement=child[1,2].parent.elements[0]=3,child[1,2].rightBrother=child[1,2].parent.children[0+1]=child[4,5,6];

  (5) 最后设置新生成树的根结点的rightBrother和nextElement为null。

  然后删除元素21,根据规则,直接与元素20替换即可,不需要进行其他变更:

  然后删除结点20,用元素19替换,然后按上述规则处理:

  (1) 被删除的结点是结点[20],其父是[19]结点,因此originalLeftBrother=[16,17]结点,originalNextElement=null,originalRightBrother=null;

  (2) 重新生成树后,该树没有孩子,因此令originalLeftBrother.rightBrother=重新生成的树的根结点,即结点[19,22];

  (3) 重新生成树后,该树没有孩子,因此令node[19,22].nextElement=originalNextElement=null,node[19,22].rightBrother=originalRightBrother=null;

  (4) 由于重新生成的树没有孩子,因此不做第四步处理;

  (5) 由于新生成的树没有孩子,因此不做第五步处理。

  然后继续处理,这时被插入的结点变为结点[8,18],它没有父结点,因此只需要重新构建树即可,所有结点的nextElement和rightBrother都不需要调整:

  其他元素的删除都依上述规则处理即可。

7.5 代码实现

  根据上文描述,结点定义代码如下所示:

import lombok.Data;import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** multi way search tree node** @author Korbin* @date 2023-11-21 17:42:56**/
@Data
public class MultiWaySearchTreeNode<T extends Comparable<T>> {/*** children, the array length must be 2 or 3 for 2-3 tree, and must be 2 or 3 or 4 for 2-3-4 tree**/private MultiWaySearchTreeNode<T>[] children;/*** elements, the array length must be 1 or 2 for 2-3 tree, and must be 1 or 2 or 3 for 2-3-4 tree**/private T[] elements;/*** left brother**/private MultiWaySearchTreeNode<T> leftBrother;/*** next element value for inorder traversal**/private T nextElement;/*** parent node**/private MultiWaySearchTreeNode<T> parent;/*** right brother**/private MultiWaySearchTreeNode<T> rightBrother;/*** add an child**/@SuppressWarnings("unchecked")public void addChild(MultiWaySearchTreeNode<T> child) {if (null == children) {children = (MultiWaySearchTreeNode<T>[]) Array.newInstance(child.getClass(), 0);}MultiWaySearchTreeNode<T>[] newChildren = Arrays.copyOf(children, children.length + 1);newChildren[newChildren.length - 1] = child;Arrays.sort(newChildren, (o1, o2) -> o1.getMaxElement().compareTo(o2.getMinElement()));this.children = newChildren;child.setParent(this);}/*** add an element**/@SuppressWarnings("unchecked")public void addElement(T element) {if (null == elements) {elements = (T[]) Array.newInstance(element.getClass(), 0);}T[] newE = Arrays.copyOf(elements, elements.length + 1);newE[newE.length - 1] = element;Arrays.sort(newE);this.elements = newE;}/*** get all element**/public String getData() {if (null == elements) {return "null";} else {StringBuilder builder = new StringBuilder();builder.append("[");for (int i = 0; i < elements.length - 1; i++) {builder.append(elements[i]).append(",");}builder.append(elements[elements.length - 1]).append("]");return builder.toString();}}/*** get max element**/public T getMaxElement() {return elements[elements.length - 1];}/*** get min element**/public T getMinElement() {return elements[0];}/*** remove an child**/public void removeChild(MultiWaySearchTreeNode<T> child) {if (this.children.length == 1) {this.children = null;} else {MultiWaySearchTreeNode<T>[] newChildren = Arrays.copyOf(children, children.length - 1);List<MultiWaySearchTreeNode<T>> childList = new ArrayList<>();for (MultiWaySearchTreeNode<T> c : children) {boolean contains = false;for (T t : child.getElements()) {if (Arrays.asList(c.getElements()).contains(t)) {contains = true;break;}}if (!contains) {childList.add(c);}}childList.sort((o1, o2) -> o1.getMaxElement().compareTo(o2.getMinElement()));this.children = childList.toArray(newChildren);}}/*** remove an element**/@SuppressWarnings("unchecked")public void removeElement(T element) {if (this.elements.length == 1) {this.elements = null;} else {T[] newE = (T[]) Array.newInstance(elements[0].getClass(), elements.length - 1);List<T> newArray = new ArrayList<>();for (T t : elements) {if (t.compareTo(element) != 0) {newArray.add(t);}}this.elements = newArray.toArray(newE);}}/*** set rightBrother** @param rightBrother right brother* @author Korbin* @date 2023-11-29 10:19:26**/public void setRightBrother(MultiWaySearchTreeNode<T> rightBrother) {if (null != rightBrother) {rightBrother.setLeftBrother(this);}this.rightBrother = rightBrother;}@Overridepublic String toString() {StringBuilder builder = new StringBuilder();builder.append("Data is ").append(getData());if (null != parent) {builder.append(", parent is ").append(parent.getData());} else {builder.append(", parent is null");}if (null != children) {builder.append(", children is {");for (int i = 0; i < children.length - 1; i++) {builder.append(children[i].getData()).append(", ");}builder.append(children[children.length - 1].getData()).append("}");} else {builder.append(", children is null");}if (null != nextElement) {builder.append(", next element is ").append(nextElement);} else {builder.append(", next element is null");}if (null != rightBrother) {builder.append(", next brother is ").append(rightBrother.getData());} else {builder.append(", next brother is null");}return builder.toString();}}

  而树的生成、元素的删除、元素的查找代码如下所示:

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** multi way search way** @author Korbin* @date 2023-11-21 08:57:24**/
public class MultiWaySearchTree<T extends Comparable<T>> {/*** root node**/private MultiWaySearchTreeNode<T> root = new MultiWaySearchTreeNode<>();/*** insert newNode into node** @param node              node to insert* @param newNode           node to be inserted* @param isBPlusTree       is to create a B plus tree* @param maxChildrenNumber max child number, for 2-3 tree is 3, for 2-3-4 tree is 4* @author Korbin* @date 2023-11-22 17:58:59**/private void checkAndMoveUp(MultiWaySearchTreeNode<T> node, MultiWaySearchTreeNode<T> newNode,int maxChildrenNumber, boolean isBPlusTree) {MultiWaySearchTreeNode<T> parent = node.getParent();node.addElement(newNode.getElements()[0]);// if new node's elements' length little or equals than maxChildrenNumber - 1, then just only add newNode's// children into nodeif (null != newNode.getChildren()) {for (MultiWaySearchTreeNode<T> child : newNode.getChildren()) {node.addChild(child);}}if (node.getElements().length > maxChildrenNumber - 1) {// if new node's elements' length greater than maxChildrenNumber - 1// 会影响parent的第一个孩子的leftBrother的leftBrother,和最后一个孩子的rightBrotherMultiWaySearchTreeNode<T> originalLeftBrother = node.getLeftBrother();MultiWaySearchTreeNode<T> originalRightBrother = node.getRightBrother();T originalNextElement = node.getNextElement();rebuildTree(node, maxChildrenNumber);if (isBPlusTree) {resetIndex(node, originalLeftBrother, originalRightBrother, originalNextElement);}if (null == parent) {root = node;} else {// remove old nodeparent.removeChild(node);// check and move upcheckAndMoveUp(parent, node, maxChildrenNumber, isBPlusTree);}}}/*** create tree from array** @param array             array* @param maxChildrenNumber max child number, for 2-3 tree is 3, for 2-3-4 tree is 4* @param isBPlusTree       is to create a B plus tree* @author Korbin* @date 2023-11-22 17:59:48**/@SuppressWarnings("unchecked")public void createTreeFromArray(T[] array, int maxChildrenNumber, boolean isBPlusTree) {int length = array.length;if (length < maxChildrenNumber) {Arrays.sort(array);root.setElements(array);return;}// if length little than maxElementNumber(maxChildrenNumber - 1), then copy to rootT[] elements = (T[]) Array.newInstance(array[0].getClass(), maxChildrenNumber - 1);if (maxChildrenNumber - 1 >= 0) {System.arraycopy(array, 0, elements, 0, maxChildrenNumber - 1);}Arrays.sort(elements);root.setElements(elements);for (int i = maxChildrenNumber - 1; i < length; i++) {MultiWaySearchTreeNode<T> node = root;while (null != node) {MultiWaySearchTreeNode<T>[] children = node.getChildren();elements = node.getElements();if (null == children) {// has to move up// find the element which index is length - 2MultiWaySearchTreeNode<T> newNode = new MultiWaySearchTreeNode<>();newNode.addElement(array[i]);checkAndMoveUp(node, newNode, maxChildrenNumber, isBPlusTree);break;} else {// if element to be inserted little than elements[0], then use elements[0]if (array[i].compareTo(elements[0]) < 0) {node = node.getChildren()[0];} else {// find from last to first, if element to be inserted greater than elements[j], then use// children[j + 1]for (int j = elements.length - 1; j >= 0; j--) {if (array[i].compareTo(elements[j]) > 0) {node = node.getChildren()[j + 1];break;}}}}}}}/*** delete an element from tree** @param element           element to be deleted* @param maxChildrenNumber max children number* @param isBPlusTree       is a B plus tree* @author Korbin* @date 2023-11-23 17:07:04**/public void deleteElement(T element, int maxChildrenNumber, boolean isBPlusTree) {MultiWaySearchTreeNode<T> node = root;while (null != node && null != node.getElements()) {List<T> elementList = Arrays.asList(node.getElements());if (elementList.contains(element)) {int elementsLength = elementList.size();MultiWaySearchTreeNode<T>[] children = node.getChildren();if (null == children) {// if this node is leaf nodeif (elementsLength > 1) {// if node's elements' length greater than 1node.removeElement(element);} else {// if node's elements' length equals 1MultiWaySearchTreeNode<T> parent = node.getParent();if (null == parent) {// if this node is root nodenode.removeElement(element);} else {// if this node is not root node// delete this nodemoveAndMerge(node, maxChildrenNumber, isBPlusTree);}}} else {// find the index of elementT[] elements = node.getElements();int delIndex = -1;for (int i = 0; i < elementsLength; i++) {if (element.compareTo(elements[i]) == 0) {delIndex = i;break;}}// find the max value of the number delIndex child treeMultiWaySearchTreeNode<T> toReplaceNode = children[delIndex];while (null != toReplaceNode) {if (null != toReplaceNode.getChildren()) {toReplaceNode = toReplaceNode.getChildren()[toReplaceNode.getChildren().length - 1];} else {break;}}if (null == toReplaceNode) {// child must not be nullbreak;}T toReplaceElement = toReplaceNode.getMaxElement();// use the max value of the number delIndex child tree to replace this elementnode.removeElement(element);node.addElement(toReplaceElement);if (isBPlusTree) {toReplaceNode.setNextElement(toReplaceElement);}// if child's elements' length greater than 1if (toReplaceNode.getElements().length > 1) {toReplaceNode.removeElement(toReplaceElement);} else {// if child's elements' length equals 1moveAndMerge(toReplaceNode, maxChildrenNumber, isBPlusTree);}}break;} else if (element.compareTo(node.getMinElement()) < 0) {// if element little than node's first element, than node change to node's first childif (null == node.getChildren()) {break;} else {node = node.getChildren()[0];}} else if (null != node.getChildren()) {// find from last element to first elementfor (int j = elementList.size() - 1; j >= 0; j--) {// if element greater than element[j], than node change to node's j+1 childif (element.compareTo(node.getElements()[j]) > 0) {node = node.getChildren()[j + 1];break;}}} else {break;}}}/*** find node by element** @param element element* @return node* @author Korbin* @date 2023-11-28 15:08:35**/public MultiWaySearchTreeNode<T> findNodeByElement(T element) {MultiWaySearchTreeNode<T> node = root;while (null != node) {T[] elements = node.getElements();if (null == elements) {return null;}for (T e : elements) {if (element.compareTo(e) == 0) {return node;}}MultiWaySearchTreeNode<T>[] children = node.getChildren();if (null != children) {if (element.compareTo(node.getMinElement()) < 0) {node = children[0];} else {// find from last element to first elementfor (int j = elements.length - 1; j >= 0; j--) {// if element greater than element[j], than node change to node's j+1 childif (element.compareTo(node.getElements()[j]) > 0) {node = node.getChildren()[j + 1];break;}}}}}return null;}/*** reset left brother's nextElement and rightBrother and move and merge** @param node              node to move* @param maxChildrenNumber max children number* @param isBPlusTree       is a B+ tree* @author Korbin* @date 2023-11-30 22:53:36**/private void moveAndMerge(MultiWaySearchTreeNode<T> node, int maxChildrenNumber, boolean isBPlusTree) {MultiWaySearchTreeNode<T> parent = node.getParent();// if is a B+ tree, then parent's first child's leftBrother's rightBrother has to reset, rebuild tree's new// last child's nextElement and rightBrother has to resetMultiWaySearchTreeNode<T>[] parentChildren = parent.getChildren();MultiWaySearchTreeNode<T> originalLeftBrother = parentChildren[0].getLeftBrother();MultiWaySearchTreeNode<T> originalRightBrother = parentChildren[parentChildren.length - 1].getRightBrother();T originalNextElement = parentChildren[parentChildren.length - 1].getNextElement();parent.removeChild(node);moveAndMerge(parent, null, maxChildrenNumber);if (isBPlusTree) {resetIndex(parent, originalLeftBrother, originalRightBrother, originalNextElement);}}/*** move and merge,add all elements of parent's all children but not node into parent, add all children of parent's* all children but not node into parent, and then rebuild the tree** @param parent            parent* @param node              one child of parent or null* @param maxChildrenNumber max children number* @author Korbin* @date 2023-11-27 16:17:39**/private void moveAndMerge(MultiWaySearchTreeNode<T> parent, MultiWaySearchTreeNode<T> node, int maxChildrenNumber) {MultiWaySearchTreeNode<T>[] children = parent.getChildren();if (null != children) {for (MultiWaySearchTreeNode<T> child : children) {if (null == node || child.getMinElement().compareTo(node.getMinElement()) != 0) {for (T element : child.getElements()) {// add all children's element into parentparent.addElement(element);}if (null != child.getChildren()) {// add all grand children into parentfor (MultiWaySearchTreeNode<T> grandChild : child.getChildren()) {parent.addChild(grandChild);}}// remove this childparent.removeChild(child);}}T[] elements = parent.getElements();int elementsLength = elements.length;// rebuild this treeif (elementsLength <= maxChildrenNumber - 1) {if (elementsLength == 3) {// if elements' length is 3, just need to build tree use these three elementsrebuildTree(parent, 3);} else {// iterate grand parentMultiWaySearchTreeNode<T> grandParent = parent.getParent();if (null != grandParent) {// if grand parent is not nullmoveAndMerge(grandParent, parent, maxChildrenNumber);}}} else {rebuildTree(parent, maxChildrenNumber);}}}/*** rebuild a tree** @param node              to rebuild node* @param maxChildrenNumber max children number* @author Korbin* @date 2023-11-28 20:53:34**/private void rebuildTree(MultiWaySearchTreeNode<T> node, int maxChildrenNumber) {MultiWaySearchTreeNode<T>[] children = node.getChildren();T[] elements = node.getElements();int elementsLength = elements.length;int shardNumber = (elementsLength % (maxChildrenNumber - 1) == 0 ? elementsLength / (maxChildrenNumber - 1) :elementsLength / (maxChildrenNumber - 1) + 1);List<MultiWaySearchTreeNode<T>> rebuildNodeList = new ArrayList<>();for (int i = 0; i < shardNumber; i++) {MultiWaySearchTreeNode<T> newNode = new MultiWaySearchTreeNode<>();for (int j = 0; j < maxChildrenNumber - 1; j++) {int index = i * (maxChildrenNumber - 1) + j;if (index < elementsLength) {newNode.addElement(elements[index]);node.removeElement(elements[index]);}}rebuildNodeList.add(newNode);}for (int i = 0; i < rebuildNodeList.size() - 1; i++) {MultiWaySearchTreeNode<T> newNode = rebuildNodeList.get(i);node.addElement(newNode.getMaxElement());newNode.removeElement(newNode.getMaxElement());node.addChild(newNode);}node.addChild(rebuildNodeList.get(rebuildNodeList.size() - 1));int startChildIndex = 0;// rebuild childif (null != children) {for (MultiWaySearchTreeNode<T> newNode : rebuildNodeList) {int childrenSize = startChildIndex + newNode.getElements().length + 1;for (int i = startChildIndex; i < childrenSize && i < children.length; i++) {newNode.addChild(children[i]);node.removeChild(children[i]);startChildIndex++;}}}}/*** reset next element and right brother** @param node                 to reset node's parent* @param originalLeftBrother  node's or first child of it's parent's left brother* @param originalRightBrother node's or last child of it's parent's right brother* @param originalNextElement  node's or last child of it's parent's next element* @author Korbin* @date 2023-12-04 15:40:09**/private void resetIndex(MultiWaySearchTreeNode<T> node, MultiWaySearchTreeNode<T> originalLeftBrother,MultiWaySearchTreeNode<T> originalRightBrother, T originalNextElement) {MultiWaySearchTreeNode<T>[] newParentChildren = node.getChildren();if (null != newParentChildren) {// rebuild tree has childrenif (null != originalLeftBrother) {// set right brother of original first child's left brother as new parent children's first nodeoriginalLeftBrother.setRightBrother(newParentChildren[0]);}if (null == newParentChildren[newParentChildren.length - 1].getChildren()) {// set right brother of new parent children's last child as original last child's right brothernewParentChildren[newParentChildren.length - 1].setRightBrother(originalRightBrother);// set next element of new parent children's last child as original last child's next elementnewParentChildren[newParentChildren.length - 1].setNextElement(originalNextElement);} else {newParentChildren[newParentChildren.length - 1].setNextElement(null);newParentChildren[newParentChildren.length - 1].setRightBrother(null);}// set all but last child of new parent's children's next element and right brotherT[] newElements = node.getElements();for (int i = 0; i < newParentChildren.length - 1; i++) {if (null == newParentChildren[i].getChildren()) {newParentChildren[i].setRightBrother(newParentChildren[i + 1]);newParentChildren[i].setNextElement(newElements[i]);} else {newParentChildren[i].setNextElement(null);newParentChildren[i].setRightBrother(null);}}node.setNextElement(null);node.setRightBrother(null);} else {// rebuild tree does not have childrenif (null != originalLeftBrother) {originalLeftBrother.setRightBrother(node);}// set right brother of new parent as original last child's right brothernode.setRightBrother(originalRightBrother);// set next element of new parent as original last child's next elementnode.setNextElement(originalNextElement);}}/*** set root** @param root root node* @author Korbin* @date 2023-11-27 16:20:42**/public void setRoot(MultiWaySearchTreeNode<T> root) {this.root = root;}}

相关文章:

大话数据结构-查找-多路查找树

注&#xff1a;本文同步发布于稀土掘金。 7 多路查找树 多路查找树&#xff08;multi-way search tree&#xff09;&#xff0c;其每个结点的孩子可以多于两个&#xff0c;且每一个结点处可以存储多个元素。由于它是查找树&#xff0c;所有元素之间存在某种特定的排序关系。 …...

unity 2d 入门 飞翔小鸟 飞翔脚本(五)

新建c#脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Fly : MonoBehaviour {//获取小鸟&#xff08;刚体&#xff09;private Rigidbody2D bird;//速度public float speed;// Start is called before the first frame up…...

Linux系统调试课:I2C tools调试工具

文章目录 一、如何使用I2C tools测试I2C外设1、I2C tools概述: 2、下载I2C tools源码:3、编译I2C tools源码: 4、i2cdetect 5、i2cget 6、i2cdump...

uniapp中解决swiper高度自适应内容高度

起因&#xff1a;uniapp中swiper组件swiper 标签存在默认高度是 height: 150px &#xff1b;高度无法实现由内容撑开&#xff0c;在默认情况下&#xff0c;swiper盒子高度显示总是 150px 解决办法思路&#xff1a; 动态设置swiper盒子的高度&#xff0c;故需要获取swiper-item盒…...

Contrast and Generation Make BART a Good Dialogue Emotion Recognizer

摘要 在对话系统中&#xff0c;具有相似语义的话语在不同的语境下可能具有不同的情感。因此&#xff0c;用说话者依赖来建模长期情境情绪关系在对话情绪识别中起着至关重要的作用。同时&#xff0c;区分不同的情绪类别也不是很简单的&#xff0c;因为它们通常具有语义上相似的…...

图的深度优先搜索(数据结构实训)

题目&#xff1a; 图的深度优先搜索 描述&#xff1a; 图的深度优先搜索类似于树的先根遍历&#xff0c;是树的先根遍历的推广。即从某个结点开始&#xff0c;先访问该结点&#xff0c;然后深度访问该结点的第一棵子树&#xff0c;依次为第二顶子树。如此进行下去&#xff0c;直…...

VUEX使用总结

1、Store 使用 文件内容大概就是这三个。通俗来讲actions负责向后端获取数据的&#xff0c;内部执行异步操作分发 Action&#xff0c;调用commit提交一个 mutation。 mutations通过Action提交commit的数据进行提交荷载&#xff0c;使state有数据。 vuex的数据是共享的&#xf…...

指定分隔符对字符串进行分割 numpy.char.split()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 指定分隔符对字符串进行分割 numpy.char.split() 选择题 请问下列程序运行的的结果是&#xff1a; import numpy as np print("【执行】np.char.split(I.Love.China, sep .)") p…...

Android12蓝牙框架

参考&#xff1a; https://evilpan.com/2021/07/11/android-bt/ https://source.android.com/docs/core/connect/bluetooth?hlzh-cn https://developer.android.com/guide/topics/connectivity/bluetooth?hlzh-cn https://developer.android.com/guide/components/intents-fi…...

python文件docx转pdf

centos部署的django项目&#xff0c;使用libreoffice做文件转换&#xff0c;官网给环境安装好libreoffice后&#xff0c;可使用命令行来进行转化 还可转换其他的各种格式&#xff0c;本文只做了pdf转换 import subprocess import os def convert_to_pdf(input_file, o…...

9.基于SpringBoot3+I18N实现国际化

1. 新建资源文件 在resources目录下新建目录i18n, 然后 新建messages_en.properties文件 user.login.erroraccount or password error&#xff01;新建messages_zh_CN.properties文件 user.login.error帐户或密码错误&#xff01;2. 新建LocaleConfig.java文件 Configurati…...

27. 深度学习进阶 - 为什么RNN

文章目录 一个柯基的例子为什么RNN or CNN Hi&#xff0c;你好。我是茶桁。 这节课开始&#xff0c;我们将会讲一个比较重要的一种神经网络&#xff0c;它对应了咱们整个生活中很多类型的一种问题结构&#xff0c;它就是咱们的RNN网络。 咱们首先回忆一下&#xff0c;上节课咱…...

谈一谈柔性数组

文章目录 什么是柔性数组柔性数组有什么用 什么是柔性数组 柔性数组是一种动态可变的数组&#xff0c;也许你从来没有听说过这个概念&#xff0c;但是它确实是存在的&#xff0c;是在C99标准底下支持的一种语法。想要使用柔性数组需要满足3个条件&#xff1a; 柔性数组只能存在…...

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之Linux文件管理(1)》(25)

《Linux操作系统原理分析之Linux文件管理&#xff08;1&#xff09;》&#xff08;25&#xff09; 8 Linux文件管理8.1 Linux 文件系统概述8.2 EXT2 文件系统8.2.1 EXT2 文件系统的构造8.2.2 EXT2 超级块&#xff08;super block&#xff09;8.2.3 组描述符8.2.4 块位图 8.3 EX…...

算能PCIe开发环境搭建-一些记录

开发环境与运行环境&#xff1a; 开发环境是指用于模型转换或验证以及程序编译等开发过程的环境&#xff1b;运行环境是指在具备Sophon设备的平台上实际使用设备进行算法应用部署的运行环境。 开发环境与运行环境可能是统一的&#xff08;如插有SC5加速卡的x86主机&#xff0c;…...

使用C#和HtmlAgilityPack打造强大的Snapchat视频爬虫

概述 Snapchat作为一款备受欢迎的社交媒体应用&#xff0c;允许用户分享照片和视频。然而&#xff0c;由于其特有的内容自动消失特性&#xff0c;爬虫开发面临一些挑战。本文将详细介绍如何巧妙运用C#和HtmlAgilityPack库&#xff0c;构建一个高效的Snapchat视频爬虫。该爬虫能…...

c/c++的字符和字符串输入输出

注&#xff1a; 1.下面这些为本人大学四年所用过的处理办法&#xff0c; 至今为止遇到的所有编程题都能够使用。如果需要了解更多关于putchar,cin.get,cin.getline等的请自行搜索。 2.getchar相当于获取一个字符&#xff0c;可以实现单个字符的输入以及通过循环实现多个字符输…...

学习设计模式的网站

Refactoring and Design Patternshttps://refactoring.guru/...

Hadoop学习笔记(HDP)-Part.08 部署Ambari集群

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …...

IDEA加载阿里Java规范插件

IDEA加载阿里巴巴Java开发手册插件&#xff0c;在写代码的时候会自动扫描代码规范。 1、打开Settings 2、打开Plugins 3、搜索Alibaba Java Code Guidelines&#xff08;XenoAmess TPM&#xff09;插件&#xff0c;点击Install进行安装&#xff0c;然后重启IDE生效。 4、鼠标右…...

【CSP】202305-1_重复局面Python实现

文章目录 [toc]试题编号试题名称时间限制内存限制题目背景问题描述输入格式输出格式样例输入样例输出样例说明子任务提示Python实现 试题编号 202305-1 试题名称 重复局面 时间限制 1.0s 内存限制 512.0MB 题目背景 国际象棋在对局时&#xff0c;同一局面连续或间断出现3次或3…...

html5各行各业官网模板源码下载(1)

文章目录 1.来源2.源码模板2.1 HTML5白色简洁设计师网站模板2.2 HTML5保护野生动物响应式网站模板 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134682321 html5各行各业官网模板源码下载&#xff0c;这个主题覆盖各行…...

6 Redis缓存设计与性能优化

缓存穿透 缓存穿透是指查询一个根本不存在的数据&#xff0c; 缓存层和存储层都不会命中&#xff0c; 通常出于容错的考虑&#xff0c; 如果从存储层查不到数据则不写入缓存层。缓存穿透将导致不存在的数据每次请求都要到存储层去查询&#xff0c; 失去了缓存保护后端存储的意义…...

SpringCloud常见问题

1、什么是Spring Cloud&#xff1f; Spring Cloud是一款基于Spring Boot框架开发的微服务框架&#xff0c;它为开发人员提供了一系列的组件和工具&#xff0c;可以帮助开发人员快速构建和部署微服务&#xff0c;提高开发效率和项目可维护性。Spring Cloud提供了包括服务注册与…...

实战演练 | 在 Navicat 中格式化日期和时间

Navicat 支持团队收到来自用户常问的一个问题是&#xff0c;如何将网格和表单视图中的日期和时间进行格式化。其实这个很简单。今天&#xff0c;我们将介绍在 Navicat Premium 中进行全局修改日期和时间格式的步骤。 如果你想边学边用&#xff0c;欢迎点击 这里 下载免费全功能…...

mysql面试题分享带答案

数据库索引的原理&#xff0c;为什么要用B树&#xff0c;为什么不用二叉树&#xff1f; 可以从几个维度去看这个问题&#xff0c;查询是否够快&#xff0c;效率是否稳定&#xff0c;存储数据多少&#xff0c;以及查找磁盘次数&#xff0c;为什么不是二叉树&#xff0c;为什么不…...

利用 Python进行数据分析实验(一)

一、实验目的 使用Python解决简单问题 二、实验要求 自主编写并运行代码&#xff0c;按照模板要求撰写实验报告 三、实验步骤 本次实验共有5题&#xff1a; 有四个数字&#xff1a;1、2、3、4&#xff0c;能组成多少个互不相同且无重复数字的三位数&#xff1f;各是多少&…...

Jupyter Notebook工具

Jupyter Notebook 是一个交互式的笔记本环境&#xff0c;允许用户以网页形式编写和分享代码、文本、图像以及其它多媒体内容。它支持超过 40 种编程语言&#xff0c;最常用的是 Python。 以下是 Jupyter Notebook 工具的一些特点和用法&#xff1a; 1. 特点&#xff1a; 交互式…...

c语言上机小练(有点难)

1.题目 用指向数组的指针编程实现&#xff1a;输入一个字符串&#xff0c;内有数字和非数字符号&#xff0c;如&#xff1a;a123x456&#xff08;此处一个空格&#xff09;17960?302tab5876。将其中连续的数字作为一个十进制整数&#xff0c;依次存放到一个数组a中。例如&…...

<JavaEE> 什么是线程安全?产生线程不安全的原因和处理方式

目录 一、线程安全的概念 二、线程不安全经典示例 三、线程不安全的原因和处理方式 3.1 线程的随机调度和抢占式执行 3.2 修改共享数据 3.3 关键代码或指令不是“原子”的 3.4 内存可见性和指令重排序 四、Java标准库自带的线程安全类 一、线程安全的概念 线程安全是指…...

Kotlin 中的 also 和 run:选择正确的作用域函数

在 Kotlin 中&#xff0c;also 和 run 是两个十分有用的作用域函数。 虽然它们在功能上相似&#xff0c;但各自有独特的用途和适用场景。 一、分析&#xff1a; also&#xff1a;在对象的上下文中执行给定的代码块&#xff0c;并返回对象本身。它的参数是一个接收对象并返回…...

ZKP Understanding Nova (1): MinRoot Example

Understanding Nova Kothapalli, Abhiram, Srinath Setty, and Ioanna Tzialla. “Nova: Recursive zero-knowledge arguments from folding schemes.” Annual International Cryptology Conference. Cham: Springer Nature Switzerland, 2022. Nova: Paper Code 1. Unders…...

0基础学java-day14

一、集合 前面我们保存多个数据使用的是数组&#xff0c;那么数组有不足的地方&#xff0c;我们分析一下 1.数组 2 集合 数据类型也可以不一样 3.集合的框架体系 Java 的集合类很多&#xff0c;主要分为两大类&#xff0c;如图 &#xff1a;[背下来] package com.hspedu.c…...

创建conan包-工具链

创建conan包-工具链 1 Toolchains 本文是基于对conan官方文档Toolchains翻译而来&#xff0c; 更详细的信息可以去查阅conan官方文档。 1 Toolchains Toolchains are the new way to integrate with build systems in Conan. Recipes can define a generate() method that wi…...

IntelliJ IDE 插件开发 | (二)UI 界面与数据持久化

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门 前言 在上一篇文章中介绍了在IDEA下开发、运行和安装插件的基本步骤&#xff0c;因此创建项目等基础步骤不再赘述&#xff0c;本文则开始介绍如何进行 UI 界面的开发以及相关数据的持久化存储&#xff…...

使用vue UI安装路由插件

1.使用vue创建项目 vue create vue-appvue ui 2.使用vue ui界面创建管理项目 终端页面输入&#xff1a;vue ui 创建项目 安装完成。可以直接在ui界面运行&#xff0c;也可以在编辑器中使用命令运行 安装路由&#xff0c;安装状态 选择插件 - 添加vue-router、添加vuex 安装…...

RPG项目01_脚本代码

基于“RPG项目01_场景及人物动画管理器”&#xff0c;我们创建一个XML文档 在资源文件夹下创建一个文件夹&#xff0c; 命名为Xml 将Xnl文档拖拽至文件夹中&#xff0c; 再在文件夹的Manager下新建脚本LoadManager 写代码&#xff1a; using System.Collections; using System…...

目标检测YOLO实战应用案例100讲-交通目标数据集构建及高性能检测算法研究与应用

目录 前言 国内外研究现状 目标检测研究现状 目标检测数据集研究现状...

浅谈Vue.js的计算属性computed

什么是computed属性 computed 属性用于声明计算属性&#xff0c;这些属性的值是基于其他响应式属性计算而来的&#xff0c;当依赖的响应式属性发生变化时&#xff0c;计算属性会自动重新计算。 与Vue.js 2相比&#xff0c;Vue.js 3的 computed 属性语法稍有变化&#xff0c;不…...

Linux常用指令详解

目录 前言&#xff1a; Linux的目录结构 Linux常用指令简介 whoami指令 ls指令 pwd指令 cd指令 tree指令 touch指令 mkdir指令 rmdir指令与rm指令 man指令 cp&#xff08;copy&#xff09;指令 mv&#xff08;move&#xff09;指令 cat指令 重定向及重定向的类型…...

Nginx(性能优化)

到这里文章的篇幅较长了&#xff0c;最后再来聊一下关于Nginx的性能优化&#xff0c;主要就简单说说收益最高的几个优化项&#xff0c;在这块就不再展开叙述了&#xff0c;毕竟影响性能都有多方面原因导致的&#xff0c;比如网络、服务器硬件、操作系统、后端服务、程序自身、数…...

机器学习笔记 - 如何在Python中对网格和点云进行体素化?

一、简述 本文主要是为了了解如何生成体素表示,体素之于3D就像像素之于2D。体素本质上是 3D 像素,但它们不是正方形,而是完美的立方体。 理论上,体素是复制现实的完美建模技术。 这里我们要了解四个广泛流行的 Python 库(Open3D、Trimesh、PyVista、pyntcloud )生成点云…...

冒个泡!OceanBase亮相 2023 新加坡金融科技节

近日&#xff0c;OceanBase 亮相 Singapore Fintech Festival 2023&#xff08;2023 新加坡金融科技节&#xff09;&#xff01;本届新加坡金融科技节于 2023 年 11 月 15 日至 17 日在新加坡博览展览中心举行&#xff0c;展会期间&#xff0c;OceanBase 得到了众多金融科技机构…...

正则表达式(5):常用符号

正则表达式&#xff08;5&#xff09;&#xff1a;常用符号 小结 本博文转载自 在本博客中&#xff0c;”正则表达式”为一系列文章&#xff0c;如果你想要从头学习怎样在Linux中使用正则&#xff0c;可以参考此系列文章&#xff0c;直达链接如下&#xff1a; 在Linux中使用正…...

Web安全漏洞分析-XSS(下)

随着互联网的迅猛发展&#xff0c;Web应用的普及程度也愈发广泛。然而&#xff0c;随之而来的是各种安全威胁的不断涌现&#xff0c;其中最为常见而危险的之一就是跨站脚本攻击&#xff08;Cross-Site Scripting&#xff0c;简称XSS&#xff09;。XSS攻击一直以来都是Web安全领…...

金南瓜SECS/GEM C# SDK 快速使用指南

本文对如何使用金南瓜SECS/GEM C# SDK 快速创建一个满足SECS/GEM通信要求的应用程序&#xff0c;只需简单3步完成。 第一步&#xff1a;创建C# .NET程序 示例使用Visual Studio 2010&#xff0c;使用者可以选择更高级版本 Visual Studio 第二步&#xff1a;添加DLL库引用&am…...

在一个没有超级用户的mongodb 生产库上如何添加超级用户

说来这个问题&#xff0c;都觉得不可思议&#xff0c;一个数据库怎么没有超级用户呢&#xff0c;我们知道&#xff0c;MYSQL&#xff0c;PG&#xff0c;ORACLE等&#xff0c;创建好后&#xff0c;都有一个默认的超级用户&#xff0c;MONGODB也有超级用户&#xff0c;但需要自己…...

排序算法之二:冒泡排序

冒泡排序的思路 冒泡排序是交换排序 基本思想&#xff1a;所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键值较大的记录向序列的尾部移动&#xff0c;键值较小的记录向序列的前部移动…...

一键搭建你的hnust请假条

hnust请假条 湖南科技大学请假条生成器 https://hnust.rick.icu/new &#xff08;直接使用&#xff09; Hnust Leave Note 去github https://github.com/rickhqh/hnust_leave_note 效果展示 界面展示效果图 v2.0 更新 vant和vue重构了整个源码同步学校新版请假条样式修复了…...

C练习题13

单项选择题(本大题共20小题,每小题2分,共40分。在每小题给出的四个备选项中,选出一个正确的答案,并将所选项前的字母填写在答题纸的相应位置上。) 1.结构化程序由三种基本结构组成、三种基本结构组成的算法是() A.可以完成任何复杂的任务 B. 只能完成部分复杂的任务 C. 只能完…...