B+树和B树的定义类似,但是B+树的非叶子结点只存储着索引,所有的数据信息都存储在叶子结点。
①B+树的特性使得在一个磁盘区域中存放更多的索引值,因此可
让一次IO操作读取到更多的索引进行定位,减少IO读取磁盘的操作
,从而提升效率。
②由于B+树的叶子结点都存储着下一个叶子结点的信息,所以,并不需要遍历整个树,只需找到最左边的孩子即可输出所有信息。
③对于B树,一般要比B+树矮一些,对于一些频率查找往往使用B树,让频率较大的结点离树根近些,在启动的时候,让上面几层直接进入内存,并不会出现磁盘IO。
④B+树的优势是在磁盘,对于内存并没有优势
对于B+树的结构,有两种说法:
①孩子数和key的数目相同
②同B树一样,孩子树比key数多一
Ps:我此处使用的是第二种说法
①BPTree(存储B树,及相关信息)
class BPTree{
//最大存储关键字
public int max;
//最小存储关键字
public int min;
//跟结点(可能为叶子结点。也可能为索引结点)
public Object root;
}
②IndexNode(索引结点,存储索引)
class IndexNode{
//关键字个数
public int num;
//结点数据
public int[] index;
//结点孩子(可能为索引,也可能为数据)
public Object[] child;
//父结点(只可能为索引)
public IndexNode parent;
}
③LeafNode(叶子结点,存储索引及数据)
class LeafNode{
//关键字个数
public int num;
//索引
public int[] index;
// //结点数据和添加索引同步操作
// public Data[] data;
//父结点
public IndexNode parent;
//同层下一个叶子结点
public LeafNode nextLeaf;
}
Ps:我之后的操作只是演示B+树的建立,所以未存储数据
/**
* 定位并添加数据
* @param root 根结点
* @return 添加数据的结点
*/
private LeafNode locateData(Object root,int index) {
//判断根结点是索引结点还是叶子结点
IndexNode iNode = null;
LeafNode lNode = null;
if(root instanceof IndexNode) {
//索引结点,需定位,然后找下一个结点
iNode = (IndexNode)root;
//定位
int locate = 0;
for(; locate<iNode.num; locate++) {
if(index < iNode.index[locate]) {
break;
}
}
return locateData(iNode.child[locate], index);
}else if(root instanceof LeafNode) {
//叶子结点,可添加数据
lNode = (LeafNode)root;
//定位
int locate = 0;
for(; locate<lNode.num; locate++) {
if(index < lNode.index[locate]) {
break;
}
}
//移动数据
for(int j=lNode.num-1; j>= locate; j--) {
lNode.index[j+1] = lNode.index[j];
}
//添加数据
lNode.index[locate] = index;
lNode.num++;
return lNode;
}
return null;
}
/**
* 分裂叶子结点
* @param lNode 叶子结点
*/
private void spiltLeafNode(LeafNode lNode) {
int spiltLocate = lNode.num/2;
int valueIndex = lNode.index[spiltLocate];
IndexNode parent = lNode.parent;
//若根结点是叶子结点,则进行更新
if(parent == null) {
parent = new IndexNode(this.bpTree.max);
this.bpTree.root = parent;
System.out.print("\t层数加一");
}
//新建一个叶子结点,将数据分为两部分
LeafNode newlNode = new LeafNode(this.bpTree.max);
int insert = 0;
for(int i = spiltLocate; i < lNode.num; i++) {
newlNode.index[insert] = lNode.index[i];
lNode.index[i] = 0;
// newlNode.data[insert] = lNode.data[i];
// lNode.data[i] = null;
insert++;
}
//更新结点相关信息
lNode.num = spiltLocate;
newlNode.num = insert;
lNode.parent = parent;
newlNode.parent = parent;
//连接叶子结点
newlNode.nextLeaf = lNode.nextLeaf;
lNode.nextLeaf = newlNode;
//在父结点中定位索引位置
int parlocate = 0;
for(; parlocate < parent.num; parlocate++) {
if(valueIndex < parent.index[parlocate]) {
break;
}
}
//移动数据和孩子
for(int i=parent.num-1; i >= parlocate; i--) {
parent.index[i+1] = parent.index[i];
parent.child[i+2] = parent.child[i+1];
}
//加入父结点,调整相关结构
parent.index[parlocate] = valueIndex;
parent.num++;
parent.child[parlocate] = lNode;
parent.child[parlocate+1] = newlNode;
//检测父结点(索引结点)
if(parent.num > this.bpTree.max) {
System.out.print("\t再次分裂");
spiltIndexNode(parent);
}
}
②索引结点分裂:找到中间值index,新建一个索引结点,将其(不包含自己)右边部分迁移到新结点中(此处不要忘了迁移孩子及更改孩子的父结点),再将该结点上升到父结点,原结点作为左孩子,新结点作为右孩子。
/**
* 分裂索引结点
* @param iNode 索引结点
*/
private void spiltIndexNode(IndexNode iNode) {
int spiltLocate = iNode.num/2;
int valueIndex = iNode.index[spiltLocate];
IndexNode parent = iNode.parent;
//当分裂到根结点
if(parent == null) {
parent = new IndexNode(this.bpTree.max);
this.bpTree.root = parent;
System.out.print("\t层数加一");
}
//新建一个结点,存储分裂的结点
IndexNode newInNode = new IndexNode(this.bpTree.max);
//跳过中间结点,将后半部分放入新结点中(调整,索引,孩子,孩子结点的父结点)
int insert = 0;
for(int i = spiltLocate+1; i < iNode.num; i++) {
newInNode.index[insert] = iNode.index[i];
iNode.index[i] = 0;
newInNode.child[insert] = iNode.child[i];
iNode.child[i] = null;
//此处孩子节点可能为两种对象,需进行转换
if(newInNode.child[insert] instanceof LeafNode) {
LeafNode midNode = (LeafNode)newInNode.child[insert];
midNode.parent = newInNode;
}else {
IndexNode midNode = (IndexNode)newInNode.child[insert];
midNode.parent = newInNode;
}
insert++;
}
//调整最后一个孩子
newInNode.child[insert] = iNode.child[iNode.num];
if(newInNode.child[insert] instanceof LeafNode) {
LeafNode midNode = (LeafNode)newInNode.child[insert];
midNode.parent = newInNode;
}else {
IndexNode midNode = (IndexNode)newInNode.child[insert];
midNode.parent = newInNode;
}
iNode.child[iNode.num] = null;
//舍去上升到父结点的结点
iNode.index[spiltLocate] = 0;
newInNode.num = insert;
iNode.num = spiltLocate;
newInNode.parent = parent;
iNode.parent = parent;
//定位父结点中的位置
int parLocate = 0;
for(; parLocate < parent.num; parLocate++) {
if(valueIndex < parent.index[parLocate]) {
break;
}
}
//调整父结点索引及孩子
for(int i = parent.num-1; i >= parLocate; i--) {
parent.index[i+1] = parent.index[i];
parent.child[i+2] = parent.child[i+1];
}
//加入中间数据
parent.index[parLocate] = valueIndex;
parent.child[parLocate] = iNode;
parent.child[parLocate+1] = newInNode;
parent.num++;
//检测父结点(索引结点)
if(parent.num > this.bpTree.max) {
System.out.print("\t再次分裂");
spiltIndexNode(parent);
}
}
/**
* 构建一棵B+树
* @param file 文件名
* @param m 树的阶树
*/
public void CreateBPTree(String file,int m) {
//先构建根结点
if(this.bpTree == null) {
bpTree = new BPTree(m);
bpTree.root = new LeafNode(bpTree.max);
}
//读取数据,加入树
BufferedReader bReader = null;
try {
bReader = new BufferedReader(new FileReader(file));
String data = null;
while((data=bReader.readLine()) != null) {
int index = Integer.parseInt(data);
//寻找位置,进行插入
LeafNode addNode = this.locateData(this.bpTree.root, index);
//判断是否需要进行分裂
if(addNode != null && addNode.num > this.bpTree.max) {
System.out.print("加入"+index+"时,溢出需要分裂");
spiltLeafNode(addNode);
System.out.println();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(bReader != null) {
bReader.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
public static void main(String[] args) {
BPlusTree bPlusTree = new BPlusTree();
bPlusTree.CreateBPTree("Test/BPlusTree.txt", 4);
//遍历叶子结点
BPTree bpTree = bPlusTree.getBpTree();
IndexNode root = (IndexNode)bpTree.root;
Object move = root.child[0];
while(move != null) {
if( move instanceof IndexNode) {
IndexNode iNode = (IndexNode)move;
move = iNode.child[0];
}else {
break;
}
}
LeafNode lNode = (LeafNode)move;
while(lNode != null) {
for(int i=0; i< lNode.num; i++) {
System.out.print(lNode.index[i]+" ");
}
lNode = lNode.nextLeaf;
}
}
结果
①4阶
因篇幅问题不能全部显示,请点此查看更多更全内容