Skip to content

Commit ef3420d

Browse files
committed
b+树
1 parent 461b641 commit ef3420d

File tree

4 files changed

+506
-0
lines changed

4 files changed

+506
-0
lines changed
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
package code.collection.tree.btree;
2+
3+
import com.google.common.collect.Lists;
4+
import org.apache.commons.lang3.tuple.Pair;
5+
6+
import java.util.ArrayList;
7+
import java.util.Collections;
8+
import java.util.List;
9+
10+
/**
11+
* 〈B+树〉<p>
12+
* 〈功能详细描述〉
13+
*
14+
* @author zixiao
15+
* @date 2019/12/20
16+
* @see https://blog.csdn.net/Fmuma/article/details/80287924
17+
*/
18+
public class BPlusTree {
19+
20+
private TreeNode root;
21+
22+
public static int m = 5;
23+
24+
public BPlusTree(int m) {
25+
BPlusTree.m = m;
26+
}
27+
28+
public BPlusTree() {
29+
this(5);
30+
}
31+
32+
public boolean insert(int key, long dataAddr) {
33+
LeafNode leaf = null;
34+
List<IndexNode> parents = null;
35+
if (root == null) {
36+
leaf = new LeafNode();
37+
root = leaf;
38+
parents = Collections.EMPTY_LIST;
39+
} else {
40+
Pair<LeafNode, List<IndexNode>> pair = findLeaf(key);
41+
leaf = pair.getKey();
42+
parents = pair.getRight();
43+
}
44+
TreeNode highNode = insertLeaf(parents, leaf, key, dataAddr);
45+
if (highNode != null) {
46+
root = highNode;
47+
}
48+
return true;
49+
}
50+
51+
/**
52+
* 插入数据到叶子
53+
*
54+
* @param parents
55+
* @param leaf
56+
* @param key
57+
* @param dataAddr
58+
* @return 新生成的最高节点
59+
*/
60+
private TreeNode insertLeaf(List<IndexNode> parents, LeafNode leaf, int key, long dataAddr) {
61+
leaf.insert(key, dataAddr);
62+
if (leaf.keySize() < leaf.m()) {
63+
return null;
64+
}
65+
// 若当前结点key的个数小于等于m-1,叶子结点分裂成左右两个叶子结点
66+
return split(parents, leaf);
67+
}
68+
69+
/**
70+
* 节点node分裂
71+
*
72+
* @param parents
73+
* @param node
74+
* @return
75+
*/
76+
private TreeNode split(List<IndexNode> parents, TreeNode node) {
77+
Pair<TreeNode, Integer> pair = node.split();
78+
TreeNode node2 = pair.getKey();
79+
int newIndexKey = pair.getValue();
80+
81+
if (parents.isEmpty()) {
82+
return new IndexNode(newIndexKey, node, node2);
83+
}
84+
return insertIndex(parents.subList(1, parents.size()), parents.get(0), newIndexKey, node, node2);
85+
}
86+
87+
/**
88+
* 插入key到索引节点
89+
*
90+
* @param parents
91+
* @param node
92+
* @param key
93+
* @param left
94+
* @param right
95+
* @return 新生成的最高节点
96+
*/
97+
private TreeNode insertIndex(List<IndexNode> parents, IndexNode node, int key, TreeNode left, TreeNode right) {
98+
node.insert(key, left, right);
99+
if (node.keySize() < node.m()) {
100+
return null;
101+
}
102+
// 若当前结点key的个数小于等于m-1,索引结点分裂成左右两个索引结点
103+
return split(parents, node);
104+
}
105+
106+
/**
107+
* 查找叶关键字对应的叶子节点,及索引父节点
108+
*
109+
* @param key
110+
* @return
111+
*/
112+
private Pair<LeafNode, List<IndexNode>> findLeaf(int key) {
113+
TreeNode node = root;
114+
List<IndexNode> parents = new ArrayList<>();
115+
while (!node.isLeaf()) {
116+
parents.add((IndexNode) node);
117+
node = ((IndexNode) node).findChild(key);
118+
}
119+
Collections.reverse(parents);
120+
return Pair.of((LeafNode) node, parents);
121+
}
122+
123+
public Long get(int key) {
124+
LeafNode node = findLeafNode(key);
125+
for (int i = 0; i < node.keySize(); i++) {
126+
if (node.keys()[i].equals(key)) {
127+
return node.getDataAddr()[i];
128+
}
129+
}
130+
return null;
131+
}
132+
133+
/**
134+
* 查找叶关键字对应的叶子节点
135+
*
136+
* @param key
137+
* @return
138+
*/
139+
private LeafNode findLeafNode(int key) {
140+
TreeNode node = root;
141+
while (!node.isLeaf()) {
142+
node = ((IndexNode) node).findChild(key);
143+
}
144+
return (LeafNode) node;
145+
}
146+
147+
public List<Long> range(int fromKey, int toKey) {
148+
List<Long> dataList = new ArrayList<>();
149+
LeafNode from = findLeafNode(fromKey);
150+
LeafNode to = null;
151+
Integer fromLast = from.keys[from.keySize - 1];
152+
153+
//1 数据在一个节点上
154+
if (fromLast > toKey || (to = findLeafNode(toKey)) == from) {
155+
for (int i = 0; i < from.keySize; i++) {
156+
if (from.keys[i] >= fromKey && from.keys[i] <= toKey) {
157+
dataList.add(from.getDataAddr()[i]);
158+
}
159+
}
160+
return dataList;
161+
}
162+
163+
//2 数据分布在多个节点上
164+
LeafNode node = from;
165+
while (node != null) {
166+
if (node != from && node != to) {
167+
//a 既不是开始又不是结束节点,则所有数据都加上
168+
for (int i = 0; i < node.keySize; i++) {
169+
dataList.add(node.getDataAddr()[i]);
170+
}
171+
} else if (node == from) {
172+
//b 开始节点,只取大于fromKey的数据
173+
for (int i = 0; i < node.keySize; i++) {
174+
if (node.keys[i] >= fromKey) {
175+
dataList.add(node.getDataAddr()[i]);
176+
}
177+
}
178+
} else if (node == to) {
179+
//c 结束节点,只取小于toKey的数据
180+
for (int i = 0; i < node.keySize; i++) {
181+
if (node.keys[i] <= toKey) {
182+
dataList.add(node.getDataAddr()[i]);
183+
}
184+
}
185+
break;
186+
}
187+
node = node.getNext();
188+
}
189+
return dataList;
190+
}
191+
192+
public void print() {
193+
StringBuilder sb = new StringBuilder();
194+
append(sb, Lists.newArrayList(root));
195+
System.out.println(sb);
196+
}
197+
198+
private void append(StringBuilder sb, List<TreeNode> nodeList) {
199+
List<TreeNode> children = new ArrayList<>();
200+
for (TreeNode node : nodeList) {
201+
if (node == null) {
202+
continue;
203+
} else if (node.isLeaf()) {
204+
sb.append("[");
205+
joinKeys(sb, ",", node.keys());
206+
sb.append("]").append("->");
207+
} else {
208+
children.addAll(Lists.newArrayList(((IndexNode) node).children()));
209+
sb.append("[");
210+
joinKeys(sb, " ", node.keys());
211+
sb.append("]");
212+
sb.append("\t");
213+
}
214+
}
215+
sb.append("\n\r");
216+
if (!children.isEmpty()) {
217+
append(sb, children);
218+
}
219+
}
220+
221+
private void joinKeys(StringBuilder sb, CharSequence delimiter, Integer[] keys) {
222+
boolean first = true;
223+
for (Integer key : keys) {
224+
if (key == null) {
225+
break;
226+
}
227+
if (first) {
228+
first = false;
229+
} else {
230+
sb.append(delimiter);
231+
}
232+
sb.append(key);
233+
}
234+
}
235+
236+
public static void main(String[] args) {
237+
BPlusTree bPlusTree = new BPlusTree(5);
238+
/**
239+
* m=5
240+
* [11]
241+
* [7 9] [13 15]
242+
* [5,6]->[7,8]->[9,10]->[11,12]->[13,14]->[15,16,17]
243+
*/
244+
bPlusTree.insert(5, 1);
245+
bPlusTree.insert(6, 2);
246+
bPlusTree.insert(7, 3);
247+
bPlusTree.insert(8, 4);
248+
bPlusTree.insert(9, 5);
249+
bPlusTree.insert(10, 6);
250+
bPlusTree.insert(11, 7);
251+
bPlusTree.insert(12, 8);
252+
bPlusTree.insert(13, 9);
253+
bPlusTree.insert(14, 10);
254+
bPlusTree.insert(15, 11);
255+
bPlusTree.insert(16, 12);
256+
bPlusTree.insert(17, 13);
257+
bPlusTree.print();
258+
259+
/**
260+
* [11 , 17]
261+
* [7, 9,] [13, 15] [19, 21]
262+
* [5,6]->[7,8] ->[9,10]->[11,12]->[13,14]->[15,16]->[17,18]->[19,20]->[21,22,23]
263+
*/
264+
bPlusTree.insert(18, 14);
265+
bPlusTree.insert(19, 15);
266+
bPlusTree.insert(20, 16);
267+
bPlusTree.insert(21, 17);
268+
bPlusTree.insert(22, 18);
269+
bPlusTree.insert(23, 19);
270+
bPlusTree.print();
271+
272+
System.out.println("Get key " + 4 + ": " + bPlusTree.get(4));
273+
System.out.println("Get key " + 9 + ": " + bPlusTree.get(9));
274+
System.out.println("Get key " + 23 + ": " + bPlusTree.get(23));
275+
276+
System.out.print("Range key [" + 5 + ", " + 6 + "]: ");
277+
bPlusTree.range(5, 6).forEach(l -> {
278+
System.out.print(l + " ");
279+
});
280+
System.out.println();
281+
282+
System.out.print("Range key [" + 10 + ", " + 13 + "]: ");
283+
bPlusTree.range(10, 13).forEach(l -> {
284+
System.out.print(l + " ");
285+
});
286+
System.out.println();
287+
288+
}
289+
290+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package code.collection.tree.btree;
2+
3+
import org.apache.commons.lang3.tuple.Pair;
4+
5+
/**
6+
* 非叶子节点,索引节点
7+
* 假设keywords=[3, 5, 8, 10]
8+
* 4个键值将数据分为5个区间:(-INF,3), [3,5), [5,8), [8,10), [10,INF)
9+
* 5个区间分别对应:children[0]...children[4]
10+
* m值是事先计算得到的,计算的依据是让所有信息的大小正好等于页的大小:
11+
* PAGE_SIZE = (m-1)*4*[keywords大小] + m*8*[children大小]
12+
*
13+
* @author zixiao
14+
* @date 2019/12/20
15+
*/
16+
public class IndexNode extends TreeNode {
17+
18+
/**
19+
* 保存子节点指针
20+
*/
21+
private TreeNode[] children;
22+
23+
public IndexNode() {
24+
keys = new Integer[m];
25+
children = new TreeNode[m+1];
26+
}
27+
28+
public IndexNode(int key, TreeNode left, TreeNode right) {
29+
this();
30+
insert(key, left, right);
31+
}
32+
33+
public TreeNode[] children(){
34+
return children;
35+
}
36+
37+
public TreeNode findChild(int key){
38+
for (int i = 0; i < keySize; i++) {
39+
if(key < keys[i]){
40+
return children[i];
41+
}else if(key == keys[i]){
42+
return children[i+1];
43+
}
44+
}
45+
return children[keySize];
46+
}
47+
48+
public boolean insert(int key, TreeNode left, TreeNode right){
49+
keys[keySize] = key;
50+
children[keySize] = left;
51+
children[keySize+1] = right;
52+
keySize++;
53+
return true;
54+
}
55+
56+
public boolean delete(int idx){
57+
keys[idx] = null;
58+
children[idx+1] = null;
59+
keySize--;
60+
return true;
61+
}
62+
63+
/**
64+
* 将这个索引类型结点分裂成两个索引结点,
65+
* 以中间位置的key划分两半,左索引结点包含一半key,右索引结点包后一半的key,
66+
* 将中间位置的key进位到父结点中, 并把进位节点的左孩子指向左结点, 进位到父结点的key右孩子指向右结点。
67+
* 将当前结点的指针指向父结点,然后重复本步骤。
68+
* m=5
69+
* 0 1 2 3 4
70+
* 左索引结点:0, 1; 右索引结点 3,4; 2进位到父节点
71+
*
72+
* @return
73+
*/
74+
public Pair<TreeNode, Integer> split(){
75+
IndexNode newNode = new IndexNode();
76+
//提取中间节点的key
77+
int indexKey = keys[m/2];
78+
//从中间节点后一个节点开始拷贝
79+
for (int i = m/2 + 1; i < keys.length; i++) {
80+
newNode.insert(keys[i], children[i], children[i+1]);
81+
}
82+
//从中间节点(包括)开始删除
83+
for (int i = m/2; i < keys.length; i++) {
84+
delete(i);
85+
}
86+
return Pair.of(newNode, indexKey);
87+
}
88+
89+
@Override
90+
public boolean isLeaf() {
91+
return false;
92+
}
93+
}

0 commit comments

Comments
 (0)