• 定义

  一颗m阶B+树满足以下几个条件:

  1.除根节点外的节点的关键字个数最大为m-1,最小为m/2

  2.除叶节点外的每个节点的孩子节点的数目为该节点关键字个数加一,这些孩子节点的的关键字的范围与父节点关键字的大小对应(这个看图才看的清楚)

  3.叶子节点存放着所有的关键字,叶子节点间按关键字的大小用指针相互连接。内部节点以叶子节点的关键字的最小值作为索引

  • B+树的优势

  B+树相较于B树最大的优势在于数据全部都存在于叶子节点,叶子节点间以指针相互连接,这样在进行按照索引的范围查找的时候就只需要遍历前后指针就可以完成,而B树要一个一个索引去进行查找,效率差别很大。

  B+树相较于hash的优势在于B+树不用一次将数据全部加载到内存,而是先确定要查询索引的地址,将对应的地址的索引加载到内存。而hash需要将全部的数据一次性加载到内存才能完成查找。

  • B+树代码实现

  首先定义一个节点类

 import java.util.List;

 /*节点类*/
public class Node { //节点的子节点
private List<Node> nodes;
//节点的键值对
private List<KeyAndValue> keyAndValue;
//节点的后节点
private Node nextNode;
//节点的前节点
private Node previousNode;
//节点的父节点
private Node parantNode; public Node( List<Node> nodes, List<KeyAndValue> keyAndValue, Node nextNode,Node previousNode, Node parantNode) {
this.nodes = nodes;
this.keyAndValue = keyAndValue;
this.nextNode = nextNode;
this.parantNode = parantNode;
this.previousNode = previousNode;
} boolean isLeaf() {
return nodes==null;
} boolean isHead() {
return previousNode == null;
} boolean isTail() {
return nextNode == null;
} boolean isRoot() {
return parantNode == null;
} List<Node> getNodes() {
return nodes;
} void setNodes(List<Node> nodes) {
this.nodes = nodes;
} List<KeyAndValue> getKeyAndValue() {
return keyAndValue;
} // public void setKeyAndValue(List<KeyAndValue> KeyAndValue) {
// this.keyAndValue = KeyAndValue;
// } Node getNextNode() {
return nextNode;
} void setNextNode(Node nextNode) {
this.nextNode = nextNode;
} Node getParantNode() {
return parantNode;
} void setParantNode(Node parantNode) {
this.parantNode = parantNode;
} Node getPreviousNode() {
return previousNode;
} void setPreviousNode(Node previousNode) {
this.previousNode = previousNode;
}
}

  定义一个存储关键字和数据的类

 public class KeyAndValue implements Comparable<KeyAndValue>{
/*存储索引关键字*/
private int key;
/*存储数据*/
private Object value; @Override
public int compareTo(KeyAndValue o) {
//根据key的值升序排列
return this.key - o.key;
} public int getKey() {
return key;
} public void setKey(int key) {
this.key = key;
} public Object getValue() {
return value;
} public void setValue(Object value) {
this.value = value;
} KeyAndValue(int key, Object value) {
this.key = key;
this.value = value;
}
}

  最后是具体的实现代码

 import java.util.*;

 public class Btree {
private static final String NODE = "NODE";
static final String INT = "INT";
private static final String PRENODE = "PRENODE";
private static final String NEXTNODE = "NEXTNODE";
//B+树的阶数
private int rank;
//根节点
private Node root;
//头结点
private Node head; Btree(int rank) {
this.rank = rank;
} public Node getRoot() {
return root;
} public void insert(KeyAndValue entry) {
List<KeyAndValue> keyAndValues1 = new ArrayList<>();
//插入第一个节点
if (head == null) {
keyAndValues1.add(entry);
head = new Node(null, keyAndValues1, null, null, null);
root = new Node(null, keyAndValues1, null, null, null);
} else {
Node node = head;
//遍历链表,找到插入键值对对应的节点
while (node != null) {
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
int exitFlag = 0;
//如果插入的键的值和当前节点键值对集合中的某个键的值相等,则直接替换value
for (KeyAndValue KV : keyAndValues) {
if (KV.getKey() == entry.getKey()) {
KV.setValue(entry.getValue());
exitFlag = 1;
break;
}
}
//如果插入的键已经有了,则退出循环
if (exitFlag == 1) {
break;
}
//如果当前节点是最后一个节点或者要插入的键值对的键的值小于下一个节点的键的最小值,则直接插入当前节点
if (node.getNextNode() == null || node.getNextNode().getKeyAndValue().get(0).getKey() >= entry.getKey()) {
splidNode(node, entry);
break;
}
//移动指针
node = node.getNextNode();
}
}
} //判断是否需要拆分节点
private void splidNode(Node node, KeyAndValue addkeyAndValue) {
List<KeyAndValue> keyAndValues = node.getKeyAndValue(); if (keyAndValues.size() == rank - 1) {
//先插入待添加的节点
keyAndValues.add(addkeyAndValue);
Collections.sort(keyAndValues);
//取出当前节点的键值对集合
//取出原来的key-value集合中间位置的下标
int mid = keyAndValues.size() / 2;
//取出原来的key-value集合中间位置的键
int midKey = keyAndValues.get(mid).getKey();
//构造一个新的键值对,不是叶子节点的节点不存储value的信息
KeyAndValue midKeyAndValue = new KeyAndValue(midKey, "");
//将中间位置左边的键值对封装成集合对象
List<KeyAndValue> leftKeyAndValues = new ArrayList<>();
for (int i = 0; i < mid; i++) {
leftKeyAndValues.add(keyAndValues.get(i));
}
//将中间位置右边边的键值对封装成集合对象
List<KeyAndValue> rightKeyAndValues = new ArrayList<>();
//如果是叶子节点则在原节点中保留上移的key-value,否则原节点删除上移的key-value
int k;
if (node.isLeaf()) {
k = mid;
} else {
k = mid + 1;
}
for (int i = k; i < rank; i++) {
rightKeyAndValues.add(keyAndValues.get(i));
}
//对左右两边的元素重排序
Collections.sort(leftKeyAndValues);
Collections.sort(rightKeyAndValues);
//以mid为界限将当前节点分列成两个节点,维护前指针和后指针
Node rightNode;
Node leftNode;
// if (node.isLeaf()) {
//如果是叶子节点维护前后指针
rightNode = new Node(null, rightKeyAndValues, node.getNextNode(), null, node.getParantNode());
leftNode = new Node(null, leftKeyAndValues, rightNode, node.getPreviousNode(), node.getParantNode());
rightNode.setPreviousNode(leftNode);
// } else {
// //如果不是叶子不维护前后指针
// rightNode = new Node(null, rightKeyAndValues, null, null, node.getParantNode());
// leftNode = new Node(null, leftKeyAndValues, null, null, node.getParantNode());
// }
//如果当前分裂的节点有孩子节点,设置分裂后节点和孩子节点的关系
if (node.getNodes() != null) {
//取得所有地孩子节点
List<Node> nodes = node.getNodes();
List<Node> leftNodes = new ArrayList<>();
List<Node> rightNodes = new ArrayList<>();
for (Node childNode : nodes) {
//取得当前孩子节点的最大键值
int max = childNode.getKeyAndValue().get(childNode.getKeyAndValue().size() - 1).getKey();
if (max < midKeyAndValue.getKey()) {
//小于mid处的键的数是左节点的子节点
leftNodes.add(childNode);
childNode.setParantNode(leftNode);
} else {
//大于mid处的键的数是右节点的子节点
rightNodes.add(childNode);
childNode.setParantNode(rightNode);
}
}
leftNode.setNodes(leftNodes);
rightNode.setNodes(rightNodes);
} //当前节点的前节点
Node preNode = node.getPreviousNode();
//分裂节点后将分裂节点的前节点的后节点设置为左节点
if (preNode != null) {
preNode.setNextNode(leftNode);
} //当前节点的后节点
Node nextNode = node.getNextNode();
//分裂节点后将分裂节点的后节点的前节点设置为右节点
if (nextNode != null) {
nextNode.setPreviousNode(rightNode);
} //如果由头结点分裂,则分裂后左边的节点为头节点
if (node == head) {
head = leftNode;
} //父节点的子节点
List<Node> childNodes = new ArrayList<>();
childNodes.add(rightNode);
childNodes.add(leftNode);
//分裂
//当前节点无父节点
if (node.getParantNode() == null) {
//父节点的键值对
List<KeyAndValue> parentKeyAndValues = new ArrayList<>();
parentKeyAndValues.add(midKeyAndValue);
//构造父节点
Node parentNode = new Node(childNodes, parentKeyAndValues, null, null, null);
//将子节点与父节点关联
rightNode.setParantNode(parentNode);
leftNode.setParantNode(parentNode);
//当前节点为根节点
root = parentNode;
} else {
Node parentNode = node.getParantNode();
//将原来的孩子节点(除了被拆分的节点)和新的孩子节点(左孩子和右孩子)合并之后与父节点关联
childNodes.addAll(parentNode.getNodes());
//移除正在被拆分的节点
childNodes.remove(node);
//将子节点与父节点关联
parentNode.setNodes(childNodes);
rightNode.setParantNode(parentNode);
leftNode.setParantNode(parentNode);
if (parentNode.getParantNode() == null) {
root = parentNode;
}
//当前节点有父节点,递归调用拆分的方法,将父节点拆分
splidNode(parentNode, midKeyAndValue);
}
} else {
keyAndValues.add(addkeyAndValue);
//排序
Collections.sort(keyAndValues);
}
} //打印B+树
void printBtree(Node root) {
if (root == this.root) {
//打印根节点内的元素
printNode(root);
System.out.println();
}
if (root == null) {
return;
} //打印子节点的元素
if (root.getNodes() != null) {
//找到最左边的节点
Node leftNode = null;
Node tmpNode = null;
List<Node> childNodes = root.getNodes();
for (Node node : childNodes) {
if (node.getPreviousNode() == null) {
leftNode = node;
tmpNode = node;
}
} while (leftNode != null) {
//从最左边的节点向右打印
printNode(leftNode);
System.out.print("|");
leftNode = leftNode.getNextNode();
}
System.out.println();
printBtree(tmpNode);
}
} //打印一个节点内的元素
private void printNode(Node node) {
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
for (int i = 0; i < keyAndValues.size(); i++) {
if (i != (keyAndValues.size() - 1)) {
System.out.print(keyAndValues.get(i).getKey() + ",");
} else {
System.out.print(keyAndValues.get(i).getKey());
}
}
} public Object search(int key, Node node, String mode) { //如果是叶子节点则直接取值
if (node.isLeaf()) {
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
for (KeyAndValue keyAndValue : keyAndValues) {
if (keyAndValue.getKey() == key) {
switch (mode) {
case NODE:
return node;
case INT:
return keyAndValue.getValue();
}
}
}
return null;
} List<Node> nodes = node.getNodes();
//如果寻找的key小于节点的键的最小值
int minKey = node.getKeyAndValue().get(0).getKey();
if (key < minKey) {
for (Node n : nodes) {
List<KeyAndValue> keyAndValues = n.getKeyAndValue();
//找到子节点集合中最大键小于父节点最小键节点
if (keyAndValues.get(keyAndValues.size() - 1).getKey() < minKey) {
return search(key, n, mode);
}
}
}
//如果寻找的key大于节点的键的最大值
int maxKey = getMaxKeyInNode(node);
if (key >= maxKey) {
for (Node n : nodes) {
List<KeyAndValue> keyAndValues = n.getKeyAndValue();
//找到子节点集合中最小键大于等于父节点最小大键节点
if (keyAndValues.get(0).getKey() >= maxKey) {
return search(key, n, mode);
}
}
} //如果寻找的key在最大值和最小值之间,首先定位到最窄的区间
int min = getLeftBoundOfKey(node, key);
int max = getRightBoundOfKey(node, key); //去所有的子节点中找键的范围在min和max之间的节点
for (Node n : nodes) {
List<KeyAndValue> kvs = n.getKeyAndValue();
//找到子节点集合中键的范围在min和max之间的节点
if (kvs.get(0).getKey() >= min && kvs.get(kvs.size() - 1).getKey() < max) {
return search(key, n, mode);
}
}
return null;
} public boolean delete(int key) {
System.out.println("delete:" + key);
System.out.println(); //首先找到要删除的key所在的节点
Node deleteNode = (Node) search(key, root, NODE);
//如果没找到则删除失败
if (deleteNode == null) {
return false;
} if (deleteNode == root) {
delKeyAndValue(root.getKeyAndValue(), key);
return true;
} if (deleteNode == head && isNeedMerge(head)) {
head = head.getNextNode();
} return merge(deleteNode, key);
} //平衡当前节点和前节点或者后节点的数量,使两者的数量都满足条件
private boolean balanceNode(Node node, Node bratherNode, String nodeType) {
if (bratherNode == null) {
return false;
}
List<KeyAndValue> delKeyAndValues = node.getKeyAndValue();
if (isMoreElement(bratherNode)) {
List<KeyAndValue> bratherKeyAndValues = bratherNode.getKeyAndValue();
int bratherSize = bratherKeyAndValues.size();
//兄弟节点删除挪走的键值对
KeyAndValue keyAndValue = null;
KeyAndValue keyAndValue1;
switch (nodeType) {
case PRENODE:
keyAndValue = bratherKeyAndValues.remove(bratherSize - 1);
keyAndValue1 = getKeyAndValueinMinAndMax(node.getParantNode(), keyAndValue.getKey(), getMinKeyInNode(node));
keyAndValue1.setKey(keyAndValue.getKey());
break;
case NEXTNODE:
keyAndValue = bratherKeyAndValues.remove(0);
keyAndValue1 = getKeyAndValueinMinAndMax(node.getParantNode(), getMaxKeyInNode(node), keyAndValue.getKey());
keyAndValue1.setKey(bratherKeyAndValues.get(0).getKey());
break;
}
//当前节点添加从前一个节点得来的键值对
delKeyAndValues.add(keyAndValue); //对键值对重排序
Collections.sort(delKeyAndValues);
return true;
}
return false;
} public boolean merge(Node node, int key) {
List<KeyAndValue> delKeyAndValues = node.getKeyAndValue();
//首先删除该key-vaule
delKeyAndValue(delKeyAndValues, key);
//如果要删除的节点的键值对的数目小于节点最大键值对数目*填充因子
if (isNeedMerge(node)) {
Boolean isBalance;
//如果左节点有富余的键值对,则取一个到当前节点
Node preNode = getPreviousNode(node);
isBalance = balanceNode(node, preNode, PRENODE);
//如果此时已经平衡,则已经删除成功
if (isBalance) return true; //如果右兄弟节点有富余的键值对,则取一个到当前节点
Node nextNode = getNextNode(node);
isBalance = balanceNode(node, nextNode, NEXTNODE); return isBalance || mergeNode(node, key);
} else {
return true;
}
} //合并节点
//key 待删除的key
private boolean mergeNode(Node node, int key) {
if (node.isRoot()) {
return false;
}
Node preNode;
Node nextNode;
Node parentNode = node.getParantNode();
List<Node> childNodes = parentNode.getNodes();
List<Node> childNodes1 = node.getNodes();
List<KeyAndValue> parentKeyAndValue = parentNode.getKeyAndValue();
List<KeyAndValue> keyAndValues = node.getKeyAndValue(); if (node.isLeaf()) {
if (parentKeyAndValue.size() == 1 && parentNode != root) {
return true;
}
preNode = getPreviousNode(node);
nextNode = getNextNode(node);
if (preNode != null) {
List<KeyAndValue> preKeyAndValues = preNode.getKeyAndValue();
keyAndValues.addAll(preKeyAndValues);
if (preNode.isHead()) {
head = node;
node.setPreviousNode(null);
} else {
preNode.getPreviousNode().setNextNode(node);
node.setPreviousNode(preNode.getPreviousNode());
}
//将合并后节点的后节点设置为当前节点的后节点
preNode.setNextNode(node.getNextNode());
KeyAndValue keyAndValue = getKeyAndValueinMinAndMax(parentNode, getMinKeyInNode(preNode), key);
delKeyAndValue(parentKeyAndValue, keyAndValue.getKey());
if (parentKeyAndValue.isEmpty()) {
root = node;
} else {
//删除当前节点
childNodes.remove(preNode);
}
Collections.sort(keyAndValues);
merge(parentNode, key);
return true;
} if (nextNode != null) {
List<KeyAndValue> nextKeyAndValues = nextNode.getKeyAndValue();
keyAndValues.addAll(nextKeyAndValues);
if (nextNode.isTail()) {
node.setPreviousNode(null);
} else {
nextNode.getNextNode().setPreviousNode(node);
node.setNextNode(nextNode.getNextNode());
} KeyAndValue keyAndValue = getKeyAndValueinMinAndMax(parentNode, key, getMinKeyInNode(nextNode));
delKeyAndValue(parentKeyAndValue, keyAndValue.getKey());
if (parentKeyAndValue.isEmpty()) {
root = node;
node.setParantNode(null);
} else {
//删除当前节点
childNodes.remove(nextNode);
}
Collections.sort(keyAndValues);
merge(parentNode, key);
return true;
}
//前节点和后节点都等于null那么是root节点
return false;
} else {
preNode = getPreviousNode(node);
nextNode = getNextNode(node);
if (preNode != null) {
//将前一个节点和当前节点还有父节点中的相应Key-value合并
List<KeyAndValue> preKeyAndValues = preNode.getKeyAndValue();
preKeyAndValues.addAll(keyAndValues);
int min = getMaxKeyInNode(preNode);
int max = getMinKeyInNode(node);
//父节点中移除这个key-value
KeyAndValue keyAndValue = getKeyAndValueinMinAndMax(parentNode, min, max);
parentKeyAndValue.remove(keyAndValue);
if (parentKeyAndValue.isEmpty()) {
root = preNode;
node.setParantNode(null);
preNode.setParantNode(null);
} else {
childNodes.remove(node);
}
assert nextNode != null;
preNode.setNextNode(nextNode.getNextNode());
//前节点加上一个当前节点的所有子节点中最小key的key-value
KeyAndValue minKeyAndValue = getMinKeyAndValueInChildNode(node);
assert minKeyAndValue != null;
KeyAndValue keyAndValue1 = new KeyAndValue(minKeyAndValue.getKey(), minKeyAndValue.getValue());
preKeyAndValues.add(keyAndValue1);
List<Node> preChildNodes = preNode.getNodes();
preChildNodes.addAll(node.getNodes());
//将当前节点的孩子节点的父节点设为当前节点的后节点
for (Node node1 : childNodes1) {
node1.setParantNode(preNode);
}
Collections.sort(preKeyAndValues);
merge(parentNode, key);
return true;
} if (nextNode != null) {
//将后一个节点和当前节点还有父节点中的相应Key-value合并
List<KeyAndValue> nextKeyAndValues = nextNode.getKeyAndValue();
nextKeyAndValues.addAll(keyAndValues); int min = getMaxKeyInNode(node);
int max = getMinKeyInNode(nextNode);
//父节点中移除这个key-value
KeyAndValue keyAndValue = getKeyAndValueinMinAndMax(parentNode, min, max);
parentKeyAndValue.remove(keyAndValue);
childNodes.remove(node);
if (parentKeyAndValue.isEmpty()) {
root = nextNode;
nextNode.setParantNode(null);
} else {
childNodes.remove(node);
}
nextNode.setPreviousNode(node.getPreviousNode());
//后节点加上一个当后节点的所有子节点中最小key的key-value
KeyAndValue minKeyAndValue = getMinKeyAndValueInChildNode(nextNode);
assert minKeyAndValue != null;
KeyAndValue keyAndValue1 = new KeyAndValue(minKeyAndValue.getKey(), minKeyAndValue.getValue());
nextKeyAndValues.add(keyAndValue1);
List<Node> nextChildNodes = nextNode.getNodes();
nextChildNodes.addAll(node.getNodes());
//将当前节点的孩子节点的父节点设为当前节点的后节点
for (Node node1 : childNodes1) {
node1.setParantNode(nextNode);
}
Collections.sort(nextKeyAndValues);
merge(parentNode, key);
return true;
}
return false;
}
} //得到当前节点的前节点
private Node getPreviousNode(Node node) {
if (node.isRoot()) {
return null;
} Node parentNode = node.getParantNode();
//得到兄弟节点
List<Node> nodes = parentNode.getNodes();
List<KeyAndValue> keyAndValues = new ArrayList<>();
for (Node n : nodes) {
List<KeyAndValue> list = n.getKeyAndValue();
int maxKeyAndValue = list.get(list.size() - 1).getKey();
if (maxKeyAndValue < getMinKeyInNode(node)) {
keyAndValues.add(new KeyAndValue(maxKeyAndValue, n));
}
}
Collections.sort(keyAndValues);
if (keyAndValues.isEmpty()) {
return null;
}
return (Node) keyAndValues.get(keyAndValues.size() - 1).getValue();
} //得到当前节点的后节点
private Node getNextNode(Node node) {
if (node.isRoot()) {
return null;
} Node parentNode = node.getParantNode();
//得到兄弟节点
List<Node> nodes = parentNode.getNodes();
List<KeyAndValue> keyAndValues = new ArrayList<>();
for (Node n : nodes) {
List<KeyAndValue> list = n.getKeyAndValue();
int minKeyAndValue = list.get(0).getKey();
if (minKeyAndValue > getMaxKeyInNode(node)) {
keyAndValues.add(new KeyAndValue(minKeyAndValue, n));
}
}
Collections.sort(keyAndValues);
if (keyAndValues.isEmpty()) {
return null;
}
return (Node) keyAndValues.get(0).getValue();
} private int getMinKeyInNode(Node node) {
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
return keyAndValues.get(0).getKey();
} private int getMaxKeyInNode(Node node) {
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
return keyAndValues.get(keyAndValues.size() - 1).getKey();
} private int getLeftBoundOfKey(Node node, int key) {
int left = 0;
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
for (int i = 0; i < keyAndValues.size(); i++) {
if (keyAndValues.get(i).getKey() <= key && keyAndValues.get(i + 1).getKey() > key) {
left = keyAndValues.get(i).getKey();
break;
}
}
return left;
} private int getRightBoundOfKey(Node node, int key) {
int right = 0;
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
for (int i = 0; i < keyAndValues.size(); i++) {
if (keyAndValues.get(i).getKey() <= key && keyAndValues.get(i + 1).getKey() > key) {
right = keyAndValues.get(i + 1).getKey();
break;
}
}
return right;
} private void delKeyAndValue(List<KeyAndValue> keyAndValues, int key) {
for (KeyAndValue keyAndValue : keyAndValues) {
if (keyAndValue.getKey() == key) {
keyAndValues.remove(keyAndValue);
break;
}
}
} //找到node的键值对中在min和max中的键值对
private KeyAndValue getKeyAndValueinMinAndMax(Node node, int min, int max) {
if (node == null) {
return null;
}
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
KeyAndValue keyAndValue = null;
for (KeyAndValue k : keyAndValues) {
if (k.getKey() > min && k.getKey() <= max) {
keyAndValue = k;
break;
}
}
return keyAndValue;
} // private KeyAndValue getMaxKeyAndValueInChildNode(Node node) {
// if (node.getNodes() == null || node.getNodes().isEmpty()) {
// return null;
// }
// List<KeyAndValue> sortKeyAndValues = new ArrayList<>();
// List<Node> childNodes = node.getNodes();
// for (Node childNode : childNodes) {
// List<KeyAndValue> keyAndValues = childNode.getKeyAndValue();
// KeyAndValue maxKeyAndValue = keyAndValues.get(keyAndValues.size() - 1);
// sortKeyAndValues.add(maxKeyAndValue);
// }
// Collections.sort(sortKeyAndValues);
// return sortKeyAndValues.get(sortKeyAndValues.size() - 1);
// } private KeyAndValue getMinKeyAndValueInChildNode(Node node) {
if (node.getNodes() == null || node.getNodes().isEmpty()) {
return null;
}
List<KeyAndValue> sortKeyAndValues = new ArrayList<>();
List<Node> childNodes = node.getNodes();
for (Node childNode : childNodes) {
List<KeyAndValue> keyAndValues = childNode.getKeyAndValue();
KeyAndValue minKeyAndValue = keyAndValues.get(0);
sortKeyAndValues.add(minKeyAndValue);
}
Collections.sort(sortKeyAndValues);
return sortKeyAndValues.get(0);
} private boolean isNeedMerge(Node node) {
if (node == null) {
return false;
}
List<KeyAndValue> keyAndValues = node.getKeyAndValue();
return keyAndValues.size() < rank / 2;
} //判断一个节点是否有富余的键值对
private boolean isMoreElement(Node node) {
return node != null && (node.getKeyAndValue().size() > rank / 2);
}
}

  测试代码:

 public class Main {

     public static void main(String[] args) {
Btree btree = new Btree(4 );
KeyAndValue keyAndValue = new KeyAndValue(1,"123");
KeyAndValue keyAndValue1 = new KeyAndValue(2,"123");
KeyAndValue keyAndValue2 = new KeyAndValue(3,"123");
KeyAndValue keyAndValue3 = new KeyAndValue(4,"123");
KeyAndValue keyAndValue4 = new KeyAndValue(5,"123");
KeyAndValue keyAndValue5 = new KeyAndValue(6,"123");
KeyAndValue keyAndValue6 = new KeyAndValue(7,"12300");
KeyAndValue keyAndValue7 = new KeyAndValue(8,"546");
KeyAndValue keyAndValue8 = new KeyAndValue(9,"123");
KeyAndValue keyAndValue9 = new KeyAndValue(10,"123");
KeyAndValue keyAndValue10 = new KeyAndValue(11,"123");
KeyAndValue keyAndValue11 = new KeyAndValue(12,"123");
KeyAndValue keyAndValue12 = new KeyAndValue(13,"123");
KeyAndValue keyAndValue14 = new KeyAndValue(15,"12345");
KeyAndValue keyAndValue15 = new KeyAndValue(16,"12345");
KeyAndValue keyAndValue16 = new KeyAndValue(17,"12345");
KeyAndValue keyAndValue17 = new KeyAndValue(18,"12345");
KeyAndValue keyAndValue18 = new KeyAndValue(19,"12345");
KeyAndValue keyAndValue19 = new KeyAndValue(20,"12345");
KeyAndValue keyAndValue20 = new KeyAndValue(21,"12345"); btree.insert(keyAndValue);
btree.insert(keyAndValue5);
btree.insert(keyAndValue9);
btree.insert(keyAndValue1);
btree.insert(keyAndValue7);
btree.insert(keyAndValue10);
btree.insert(keyAndValue17);
btree.insert(keyAndValue2);
btree.insert(keyAndValue14);
btree.insert(keyAndValue16);
btree.insert(keyAndValue11);
btree.insert(keyAndValue12);
btree.insert(keyAndValue3);
btree.insert(keyAndValue8);
btree.insert(keyAndValue18);
btree.insert(keyAndValue15);
btree.insert(keyAndValue4);
btree.insert(keyAndValue19);
btree.insert(keyAndValue6);
btree.insert(keyAndValue20); btree.printBtree(btree.getRoot()); btree.delete(1);
btree.printBtree(btree.getRoot()); btree.delete(0);
btree.printBtree(btree.getRoot()); btree.delete(2);
btree.printBtree(btree.getRoot()); btree.delete(11);
btree.printBtree(btree.getRoot()); btree.delete(3);
btree.printBtree(btree.getRoot()); btree.delete(4);
btree.printBtree(btree.getRoot()); btree.delete(5);
btree.printBtree(btree.getRoot()); btree.delete(9);
btree.printBtree(btree.getRoot()); btree.delete(6);
btree.printBtree(btree.getRoot()); btree.delete(13);
btree.printBtree(btree.getRoot()); btree.delete(7);
btree.printBtree(btree.getRoot()); btree.delete(10);
btree.printBtree(btree.getRoot()); btree.delete(18);
btree.printBtree(btree.getRoot()); btree.delete(8);
btree.printBtree(btree.getRoot()); btree.delete(12);
btree.printBtree(btree.getRoot()); btree.delete(20);
btree.printBtree(btree.getRoot()); btree.delete(19);
btree.printBtree(btree.getRoot()); btree.delete(15);
btree.printBtree(btree.getRoot()); btree.delete(17);
btree.printBtree(btree.getRoot()); }
}

  测试结果:

8,12
3,6|10|15,18,20|
1,2|3,4,5|6,7|8,9|10,11|12,13|15,16,17|18,19|20,21|
delete:1 8,12
4,6|10|15,18,20|
2,3|4,5|6,7|8,9|10,11|12,13|15,16,17|18,19|20,21|
delete:0 8,12
4,6|10|15,18,20|
2,3|4,5|6,7|8,9|10,11|12,13|15,16,17|18,19|20,21|
delete:2 12
6,8,10|15,18,20|
3,4,5|6,7|8,9|10,11|12,13|15,16,17|18,19|20,21|
delete:11 12
6,8|15,18,20|
3,4,5|6,7|8,9,10|12,13|15,16,17|18,19|20,21|
delete:3 12
6,8|15,18,20|
4,5|6,7|8,9,10|12,13|15,16,17|18,19|20,21|
delete:4 18
8,15|18,20|
5,6,7|8,9,10|12,13|15,16,17|18,19|20,21|
delete:5 18
8,15|18,20|
6,7|8,9,10|12,13|15,16,17|18,19|20,21|
delete:9 18
8,15|18,20|
6,7|8,10|12,13|15,16,17|18,19|20,21|
delete:6 12,15,18,20
7,8,10|12,13|15,16,17|18,19|20,21|
delete:13 10,15,18,20
7,8|10,12|15,16,17|18,19|20,21|
delete:7 15,18,20
8,10,12|15,16,17|18,19|20,21|
delete:10 15,18,20
8,12|15,16,17|18,19|20,21|
delete:18 15,17,20
8,12|15,16|17,19|20,21|
delete:8 17,20
12,15,16|17,19|20,21|
delete:12 17,20
15,16|17,19|20,21|
delete:20 17
15,16|17,19,21|
delete:19 17
15,16|17,21|
delete:15 16,17,21
delete:17 16,21

B+树的算法(java实现)的更多相关文章

  1. 堆排序算法 java 实现

    堆排序算法 java 实现 白话经典算法系列之七 堆与堆排序 Java排序算法(三):堆排序 算法概念 堆排序(HeapSort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,可以利用数组的特 ...

  2. AVL树(三)之 Java的实现

    概要 前面分别介绍了AVL树"C语言版本"和"C++版本",本章介绍AVL树的Java实现版本,它的算法与C语言和C++版本一样.内容包括:1. AVL树的介绍 ...

  3. 伸展树(三)之 Java的实现

    概要 前面分别通过C和C++实现了伸展树,本章给出伸展树的Java版本.基本算法和原理都与前两章一样.1. 伸展树的介绍2. 伸展树的Java实现(完整源码)3. 伸展树的Java测试程序 转载请注明 ...

  4. 基于FP-Tree的关联规则FP-Growth推荐算法Java实现

    基于FP-Tree的关联规则FP-Growth推荐算法Java实现 package edu.test.ch8; import java.util.ArrayList; import java.util ...

  5. 树状结构Java模型、层级关系Java模型、上下级关系Java模型与html页面展示

    树状结构Java模型.层级关系Java模型.上下级关系Java模型与html页面展示 一.业务原型:公司的组织结构.传销关系网 二.数据库模型 很简单,创建 id 与 pid 关系即可.(pid:pa ...

  6. 归并排序算法 java 实现

    归并排序算法 java 实现 可视化对比十多种排序算法(C#版) [直观学习排序算法] 视觉直观感受若干常用排序算法 算法概念 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Di ...

  7. 快速排序算法 java 实现

    快速排序算法 java 实现 快速排序算法Java实现 白话经典算法系列之六 快速排序 快速搞定 各种排序算法的分析及java实现 算法概念 快速排序是C.R.A.Hoare于1962年提出的一种划分 ...

  8. Atitit 电子商务订单号码算法(java c# php js 微信

    Atitit 电子商务订单号码算法(java c# php js  微信 1.1. Js版本的居然钱三爷里面没有..只好自己实现了. 1.2. 订单号标准化...长度16位 1.3. 订单号的结构 前 ...

  9. 无向图的最短路径算法JAVA实现

    一,问题描述 给出一个无向图,指定无向图中某个顶点作为源点.求出图中所有顶点到源点的最短路径. 无向图的最短路径其实是源点到该顶点的最少边的数目. 本文假设图的信息保存在文件中,通过读取文件来构造图. ...

  10. 无向图的最短路径算法JAVA实现(转)

    一,问题描述 给出一个无向图,指定无向图中某个顶点作为源点.求出图中所有顶点到源点的最短路径. 无向图的最短路径其实是源点到该顶点的最少边的数目. 本文假设图的信息保存在文件中,通过读取文件来构造图. ...

随机推荐

  1. Android native进程间通信实例-binder篇之——解决实际问题inputreader内建类清楚缓存

    我在实际开发中,遇到一个问题,在电容屏驱动中没有发送input_sync 给上层,导致电容屏有的数据缓存在inputreader 中,会导致系统一系列奇怪问题发生, 至于为什么驱动不发送input_s ...

  2. 移动端底部导航固定配合vue-router实现组件切换

    在我们平时练习或者实际项目中也好,我们常常遇到这么一个需求:移动端中的导航并不是在顶部也不是在底部,而是在最底部且是固定的,当我们点击该导航项时会切换到对应的组件.相信对于很多朋友而言,这是一个很简单 ...

  3. CodeForces - 1251B (思维+贪心)

    题意 https://vjudge.net/problem/CodeForces-1251B n个01串,可以任意交换任意两个字符串的字符任意次,问最多能有多少个回文串. 思路 分类讨论可以发现规律: ...

  4. xposed 原理分析

    1.添加hook方法 首先是init进程打开 app_process,然后进入XposedInit.java main() - > initForZygote() 加入对ActivityThre ...

  5. 6. Go语言—字符串操作

    一.字符串支持的转义字符 \r 回车符(返回行首) \n 换行符(直接跳到下一行的同列位置) \t 制表符 \' 单引号 \" 双引号 \\ 反斜杠 \uXXXX Unicode字符码值转义 ...

  6. Vue移动端报错[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.

    解决方法如下 项目方案: 在最外侧添加样式操作 .

  7. 201271050130-滕江南-《面向对象程序设计(java)》第十三周学习总结

      201271050130-滕江南-<面向对象程序设计(java)>第十三周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daiz ...

  8. 接口是用get还是post,如何选择?

    为了保证信息安全性,注册.登录等操作通常都会使用POST请求,GET请求一般用来获取信息 GET与POST的区别可以参考下方链接 https://www.cnblogs.com/hyddd/archi ...

  9. Pwnable-leg

    Download : http://pwnable.kr/bin/leg.c Download :http://pwnable.kr/bin/leg.asm 友链 https://blog.csdn. ...

  10. 攻防世界web之PHP2

    题目 御剑扫描无果,源码也无发现 但是在url后面添加index.phps发现代码泄露 右击查看源码 审计代码 首先不能admin等于get传过来的值 又要使admin等于get传值 在get传值之前 ...