1.节点的结构(如下图)
(1)键值对--key是标识;value是存储的具体数据
(2)节点的子节点--存储的是具体的子节点
(3)节点的后节点--标记后一个节点
(4)节点的前节点--标记前一个节点
(5)节点的父节点--标记父节点是哪个
2.存储的规则
2.1存储第一条数据,并标记根节点和头结点。
2.2单个节点
2.2.1.没有超过设定的阶数
当存储第二和三个数据时,首先判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值。如果是则再判断有没有超过设定的阶数,没有则将将数据直接插入到当前结点。当前的结点是最后一个结点并且没有超过设定的阶数,因此直接将二三个数据直接插入到当前的结点当中 。
2.2.2.超过阶数(分裂)
在存储第四个数时,首先判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值。如果是再判断是否超过设定的阶数了,超过了则取出原来key-value 集合中间位置的下标mid并获得中间位置的键midKey。构造一个新的键值对midKeyAndValue存储(中间位置的键,空串),然后分别将中间位置的左边封装成集合对象leftKeyAndValue,并将左边的数存储到leftKeyAndValue中;中间位置的右边封装成集合对象rightKeyAndValue,再判断当前节点是否有叶子结点,如果有则将中间位置后的数据(不包含中间位置的数据)存储到rightKeyAndValue中;如果没有则将从中间位置开始右边的数据保存到rightKeyAndValue中。分别对左右两个集合对象进行排序处理。以mid为界限将当前结点分裂成两个结点分别是:前节点leftNode,后节点rightNode;前指针的节点的结构为:数据(leftKeyAndValue),子节点(null),前指针(当前节点的左节点),后指针(rightNode),父节点(当前结点的父节点);此时将头节点重置为前节点leftNode;新建一个子节点childNode并将前节点leftNode和后节点rightNode添加进去,然后构造一个父节点parentNode结构为:子节点(childNode),键值对(midKeyAndValue),前节点(null),后节点(null),父节点(null)。并将子节点与父节点进行关联。将当前父节点设置为根节点。此时就转变成“一父二子”了
2.3一夫二子
2.3.1.没有超过设定的阶数
存储第五个数的时候,首先以L1节点为head判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值。如果是再判断是否超过设定的阶数了,根据上图可以看出无论数据存储到哪个子节点都不会超过设定的阶数,因此可以将数据直接存储到L1节点;如果不是最后一个结点或者要插入的键值对的键的值大于下一个结点的的键的最小值,此时移动指针,将R1作为依据再进行判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值,此时肯定符合条件的,再判断是否超过设定的阶数了,此时的场景不会超过设定的阶数,因此将数据直接添加到R1中,并排序处理。
因此会有下图这两种情况
存储第六个数据的时候有百分之五十的几率不会超过设定的阶数存储的过程跟存储第五个数的过程一样。
结果如下图两种情况
2.3.2超过设定的阶数
存储第六个数据的时候有百分之五十的可能性会超过阶数,如下图(2.3-5和2.3-6),在添加数据的时候首先以L1节点为head判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值。
1.如果是再判断是否超过设定的阶数了,此时设定的场景是要超过设定的阶数,先取出L1节点的key-value 集合中间位置的下标mid并获得中间位置的键midKey。构造一个新的键值对midKeyAndValue存储(中间位置的键,空串),然后分别将中间位置的左边封装成集合对象leftKeyAndValue,并将左边的数存储到leftKeyAndValue中;中间位置的右边封装成集合对象rightKeyAndValue,再判断当前节点是否有叶子结点,如果有则将中间位置后的数据(不包含中间位置的数据)存储到rightKeyAndValue中;如果没有则将从中间位置开始右边的数据保存到rightKeyAndValue中。分别对左右两个集合对象进行排序处理。以mid为界限将当前结点分裂成两个结点分别是:前节点leftNode,后节点rightNode;前指针的节点的结构为:数据(leftKeyAndValue),子节点(null),前指针(当前节点的左节点),后指针(rightNode),父节点(当前结点的父节点),新建一个子节点集合childNodes并分别将前节点leftNode,后节点rightNode添加进去;如果头结点是当前要分隔的节点则将头节点重置为前节点leftNode,获取到当前L1节点的父节点parentNode,并获取到父节点的所有子节点,将这些子节点全部添加到子节点集合childNodes中,然后删除当前的L1节点,然后将子节点重置成新的子节点集合childNodes;继续以父节点为依据判断是否超过设定的阶数了,此时没有超过阶数,将键值对midKeyAndValue直接保存到当前的父节点中,并进行排序操作,此时就变成“一夫三子”了如图(2.3-7);
2.如果不是最后一个结点或者要插入的键值对的键的值大于下一个结点的的键的最小值,此时移动指针,将R1作为依据再进行判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值,此时肯定符合条件的,再判断是否超过设定的阶数了,此时设定的场景是要超过设定的阶数,先取出R1节点的key-value 集合中间位置的下标mid并获得中间位置的键midKey。构造一个新的键值对midKeyAndValue存储(中间位置的键,空串),然后分别将中间位置的左边封装成集合对象leftKeyAndValue,并将左边的数存储到leftKeyAndValue中;中间位置的右边封装成集合对象rightKeyAndValue,再判断当前节点是否有叶子结点,如果有则将中间位置后的数据(不包含中间位置的数据)存储到rightKeyAndValue中;如果没有则将从中间位置开始右边的数据保存到rightKeyAndValue中。分别对左右两个集合对象进行排序处理。以mid为界限将当前结点分裂成两个结点分别是:前节点leftNode,后节点rightNode;前指针的节点的结构为:数据(leftKeyAndValue),子节点(null),前指针(当前节点的左节点),后指针(rightNode),父节点(当前结点的父节点),新建一个子节点集合childNodes并分别将前节点leftNode,后节点rightNode添加进去;如果头结点是当前要分隔的节点则将头节点重置为前节点leftNode,获取到当前L1节点的父节点parentNode,并获取到父节点的所有子节点,将这些子节点全部添加到子节点集合childNodes中,然后删除当前的R1节点,然后将子节点重置成新的子节点集合childNodes;继续以父节点为依据判断是否超过设定的阶数了,此时没有超过阶数,将键值对midKeyAndValue直接保存到当前的父节点中,并进行排序操作,此时就变成“一夫三子”了如图(2.3-8)
存储第七个数据时有两种情况
1.如果存储第六个数据时没有分裂,则此时存储定会分裂如下图(2.3.9-2.3.12);分裂过程与第六次存储数据需要分裂的过程一样,结果如下图
2.如果存储第六个数据时分裂了,则此时定不会分裂结果入下图(2.3.13-2.3.16),
首先分别以图2.3-7的L2节点和2.3-8的L1节点为head判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值。
(1).如果是再判断是否超过设定的阶数了,根据上图可以看出无论数据存储到哪个子节点都不会超过设定的阶数,因此可以将数据直接存储到第一个节点;
(2).如果不是最后一个结点或者要插入的键值对的键的值大于下一个结点的的键的最小值,此时移动指针,将第二个作为依据再进行判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值,如果是则判断是否超过设定的阶数,此场景不会超过阶数,直接将数据存储到第二个节点的键值对当中;
(3).如果还不是最后一个结点或者要插入的键值对的键的值大于下一个结点的的键的最小值,此时移动指针,将第三个作为依据再进行判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值, 再判断是否超过设定的阶数了,此时的场景不会超过设定的阶数,因此将数据直接添加到第三个节点中,并排序处理。
2.4一父三子
2.4.1 三个子节点的数,要么都满,要么部分满
存储更多数据的时候,三子节点的键值对的数据有以下两种情况
1.都满,最少再添加四个数据才会进行分裂(如图2.4.1)分裂成一父四子(如图 2.4-3)
2.部分满,再上面的基础上最少添加两个数据,就会出现部分满的情况(如图2.4-2),此时的情况是需要将满的节点进行分裂,过程与前面分裂过程一样,变成一父四子(如图2.4-4)
2.5一父四子
2.5.1 四个子节点的数,要么都满,要么部分满
部分满跟都满都会使得节点再次分裂,分裂成一父二子的情况
在添加数据的时候首先以左边第一个节点为head判断是否是最后一个结点或者要插入的键值对的键的值是否小于下一个结点的的键的最小值。
1.如果是再判断是否超过设定的阶数了,此时设定的场景是要超过设定的阶数,先取出当前节点的key-value 集合中间位置的下标mid并获得中间位置的键midKey。构造一个新的键值对midKeyAndValue存储(中间位置的键,空串),然后分别将中间位置的左边封装成集合对象leftKeyAndValue,并将左边的数存储到leftKeyAndValue中;中间位置的右边封装成集合对象rightKeyAndValue,再判断当前节点是否有叶子结点,如果有则将中间位置后的数据(不包含中间位置的数据)存储到rightKeyAndValue中;如果没有则将从中间位置开始右边的数据保存到rightKeyAndValue中。分别对左右两个集合对象进行排序处理。以mid为界限将当前结点分裂成两个结点分别是:前节点leftNode,后节点rightNode;前指针的节点的结构为:数据(leftKeyAndValue),子节点(null),前指针(当前节点的左节点),后指针(rightNode),父节点(当前结点的父节点),新建一个子节点集合childNodes并分别将前节点leftNode,后节点rightNode添加进去;如果头结点是当前要分隔的节点则将头节点重置为前节点leftNode,获取到当前节点的父节点parentNode,并获取到父节点的所有子节点,将这些子节点全部添加到子节点集合childNodes中,然后删除当前节点,然后将子节点重置成新的子节点集合childNodes;
2.继续以父节点为依据判断是否超过设定的阶数了,此时超过阶数,超过了则取出原来key-value 集合中间位置的下标mid并获得中间位置的键midKey。构造一个新的键值对midKeyAndValue存储(中间位置的键,空串),然后分别将中间位置的左边封装成集合对象leftKeyAndValue,并将左边的数存储到leftKeyAndValue中;中间位置的右边封装成集合对象rightKeyAndValue,再判断当前节点是否有叶子结点,如果有则将中间位置后的数据(不包含中间位置的数据)存储到rightKeyAndValue中;如果没有则将从中间位置开始右边的数据保存到rightKeyAndValue中。分别对左右两个集合对象进行排序处理。以mid为界限将当前结点分裂成两个结点分别是:前节点leftNode,后节点rightNode;前指针的节点的结构为:数据(leftKeyAndValue),子节点(null),前指针(当前节点的左节点),后指针(rightNode),父节点(当前结点的父节点);判断当前结点是否有孩子节点,此场景是有孩子节点的,获取到所有孩子节点存储在nodes集合中,并新建两个集合leftNodes与rightNodes分别存储左节点的子节点与右节点的子节点,通过遍历取得当前孩子节点的最大键值,小于mid的键的数是左节点的子节点;大于mid的键的数是右节点的子节点,将leftNodes添加为leftNode的子节点;将rightNodes添加为rightNode的子节点。此时将头节点重置为前节点leftNode;新建一个子节点childNode并将前节点leftNode和后节点rightNode添加进去;然后判断当前结点是否有父节点,此时有父节点,获取到当前节点的父节点parentNode,并获取到父节点的所有子节点,将这些子节点全部添加到子节点集合childNodes中,然后删除当前的节点,然后将子节点重置成新的子节点集合childNodes;继续以父节点为依据判断是否超过设定的阶数了,此时没有超过阶数,将键值对midKeyAndValue直接保存到当前的父节点中,并进行排序操作。最终效果如下图所示()