btree的使用场景
在巨型数据集中,查找是一项非常耗时的操作。我们希望设计一种这样的数据结构,通过简单的4-5指向数据块的引用就能查找到巨型数据集中我们想要的数据。这时,btree就诞生了。我们以前学习的高效查找结构,如红黑树等,适用于能够存放到内存中的数据。btree主要用于对磁盘或者网络上的数据的查找。如mysql 的存储引擎myisam就是btree的实现。
b-树的数据结构
- b树的节点是页。页中存放的数据是一段连续有序的数据。
- 在b树中,我们不会把数据存放到内存中(数据存放到外部磁盘或者网络上)。而是构造一颗由键的副本组成的树。
- 页中键的副本时有序的。
- 对于一颗M阶的树,每个节点最少含有M/2个键和链接,最多含有M-1个键和链接。根节点除外,跟节点可以含有小于M/2的键和链接。
父节点的键值时内部节点的最小值。
对于B-树的内部约定: - 内部节点:含有与页相关联的键的副本。 - 外部节点:含有指向实际数据的引用。
图解:
B -树 官方定义
一颗M阶B-树(M为正偶数)或者仅是一个外部k-节点(或者k个键和相关信息的树),或者由若干内部k-节点(每个节点都含有k个键和k条连接,链接指向的子树表示了键之间的间隔区域)组成。它的结构性质如下:从跟节点到每个外部节点的路径长度均相同(完美平衡);对于跟节点,k在2到M-1之间,对于其他节点k在M/2到M-1之间。
B - 树 java实现
数据页定义
@Data
public static class Page {
/**
* 存放键值对
*/
public Entity[] entitys = new Entity[M];
public int m = 0;
/*定义页的父节点*/
public Page pPage;
public Page() {
}
public Page(int m) {
this.m = m;
}
/**
* 判断节点是否是满的
* @return
*/
public boolean isFull() {
if (m == M) {
return true;
}
return false;
}
/**
* 键在此页中
* @param key
* @return
*/
public boolean contains(Entity entity) {
return binarySearch(entitys,entity.keyName) == -1?false:true;
}
/**
* 获取元素的下一个插入点
* @param entity
* @return
*/
public Page next(Entity entity) {
if (contains(entity)) {
return entity.page;
}
else {
int len = entitys.length;
for (int i = len - 1;i >= 0;i--) {
if (entitys[i] != null) {
if (entity.keyName.compareTo(entitys[i].keyName) > 0) {
return entitys[i].page;
}
}
}
}
return null;
}
public void addEntity(Entity entity) {
entitys[m] = entity;
for (int i = m - 1;i >= 0; i--) {
if (entitys[i].keyName.compareTo(entity.keyName) > 0) {
Entity tempEntity = entitys[i];
entitys[i] = entity;
entitys[i+1] = tempEntity;
} else {
break;
}
}
m = m + 1;
}
public void add(Page page) {
if (page.m > 0) {
String keyName = page.entitys[0].keyName;
addEntity(new Entity(keyName,null,page));
}
}
/**
* 分隔页
* @return
*/
public Page splitPage() {
Page page = new Page();
System.arraycopy(entitys,M/2,page.entitys,0,M-M/2);
page.m = M- (M/2);
for (int i = M/2;i < M;i++) {
entitys[i] = null;
}
this.m = M/2;
return page;
}
/**
* 二分搜索
* @param entitys
* @param keyName
* @return
*/
public int binarySearch(Entity[] entitys,String keyName) {
int start = 0;
int end = entitys.length - 1;
while(start <= end) {
int mid = start + (end - start) / 2;
if (entitys[mid] == null) {
break;
}
if (keyName.equals(entitys[mid].keyName)) {
return mid;
}
else if (keyName.compareTo(entitys[mid].keyName) < 0) {
end = mid -1;
} else if (keyName.compareTo(entitys[mid].keyName) > 0) {
start = mid + 1;
}
}
return -1;
}
}
@Data
public static class Entity {
public String keyName;
/*外部节点*/
public String val;
/*内部节点*/
public Page page;
public Entity(String keyName,String val,Page page) {
this.keyName = keyName;
this.val = val;
this.page = page;
}
}
构建B-树
@Data
public class Btree {
//btree树的界
private static int M = 6;
//根节点
private Page root = new Page(0);
//定义树的高度
private int height;
/**
* 往b树中添加元素
* @param key
* @param value
*/
public void add(String key,String value) {
Entity entity = new Entity(key,value,null);
add(root,entity,height);
/**
* 如果元素已满,则分列
*/
if (root.isFull()) {
Page temp = root;
Page rightPage = root.splitPage();
Page leftPage = root;
rightPage.pPage = temp;
leftPage.pPage = temp;
root = new Page(0);
root.add(leftPage);
root.add(rightPage);
height = height + 1;
}
}
private void add(Page page,Entity key,int hi) {
/*页节点*/
if (hi == 0) {
page.addEntity(key);
} else {
Page nextPage = page.next(key);
add(nextPage,key,hi - 1);
if (nextPage.isFull()) {
Page splitPage = nextPage.splitPage();
page.add(splitPage);
}
}
}
public int height() {
return height;
}
}