目录
- 2-3-4树
- B树
2-3-4树的特点:
插入数据规则:
效率:
和红黑树、二叉树的查找效率差不多;
代码:
/**
* @ClassName DataItem
* @Descriptionn 数据项类
* @Author lzq
* @Date 2019/6/20 14:43
* @Version 1.0
**/
public class DataItem {
public long data;
public DataItem(long dd) {
this.data = dd;
}
public void show() {
System.out.print("/"+data);
}
}
/**
* @ClassName TreeNode
* @Description 2-3-4树节点类
* @Author lzq
* @Date 2019/6/20 14:45
* @Version 1.0
**/
public class TreeNode {
private static final int ORDER = 4; //最大字节点个数
private int numitems; //当前节点中数据项的个数
private TreeNode parent; //记录父节点
private TreeNode[] childArray = new TreeNode[ORDER]; //字节点的引用集合
private DataItem[] itemsArray = new DataItem[ORDER-1]; //数据项集合
/**
* 取子节点
* @param child
* @return
*/
public TreeNode getChild(int child) {
return childArray[child];
}
/**
* 获取父节点
* @return
*/
public TreeNode getParent() {
return parent;
}
/**
* 判断节点是否是叶节点
* @return
*/
public boolean isLeaf() {
//只需要判断子节点集合的第一个是否为空即可
return childArray[0] == null;
}
/**
* 获取数据项个数
* @return
*/
public int getNumitems() {
return numitems;
}
/**
* 获取指定数据项
* @param index
* @return
*/
public DataItem getItem(int index) {
return itemsArray[index];
}
/**
* 判断节点数据项是否已满
* @return
*/
public boolean isFull() {
return numitems == (ORDER-1);
}
/**
* 连接一个子节点
* @param childNum 需要添加数据项的位置
* @param child 需要添加的子节点
*/
public void connectChild(int childNum,TreeNode child) {
childArray[childNum] = child;
if(child != null) {
child.parent = this;
}
}
/**
* 拆分子节点
* @param childNum
* @return
*/
public TreeNode disConnectChild(int childNum){
TreeNode temp = childArray[childNum];
childArray[childNum] = null;
return temp;
}
/**
* 从当前节点中找一个数据项的位置
* @param key
* @return
*/
public int findItem(long key) {
for (int i = 0; i < ORDER-1; i++) {
if(itemsArray[i] == null) { //如果第一个数据项都为空,就不用找了
break;
}else if(itemsArray[i].data == key){
return i; //找到了对应位置,直接返回
}
}
return -1; //没找到
}
/**
* 插入数据项
* @param newIteam
* @return
*/
public int insertItem(DataItem newIteam) {
numitems++;
long newKey = newIteam.data; //拿到新增加的数据项
for (int i = ORDER-2; i >= 0;i--) {
if(itemsArray[i] == null) {
continue;
}else {
long temp = itemsArray[i].data; //拿到原来的数据项
if(newKey < temp) {
itemsArray[i+1] = itemsArray[i]; //往后挪动
}else {
itemsArray[i+1] = newIteam;
return i+1;
}
}
}
itemsArray[0] = newIteam;
return 0;
}
/**
* 删除一个数据项
* 删除的是最大的那个
* @return
*/
public DataItem removeIteam() {
DataItem temp = itemsArray[numitems-1]; //拿到最后一个数据项(即要删除的数据项)
itemsArray[numitems-1] = null;
numitems--;
return temp;
}
/**
* 打印节点
*/
public void show() {
for (int i = 0; i < numitems; i++) {
itemsArray[i].show();
}
System.out.println("/");
}
}
/**
* @ClassName Tree234
* @Description 2-3-4树
* @Author lzq
* @Date 2019/6/20 15:13
* @Version 1.0
**/
public class Tree234 {
private TreeNode root = new TreeNode(); //根节点
/**
* 查找一个值
* @param key
* @return
*/
public int find(long key) {
TreeNode cur = root; //从根开始找
int childNumber;
while (true) {
if((childNumber = cur.findItem(key)) != -1) { //找到了
return childNumber;
}else if(cur.isLeaf()) { //当前节点是叶子节点
return -1; //没找到
}else {
cur = getNextChild(cur,key); //当前节点没找到,去它的下一个子节点
}
}
}
/**
* 寻找下一个子节点
* @param cur
* @param key
* @return
*/
private TreeNode getNextChild(TreeNode cur, long key) {
int i;
int numItems = cur.getNumitems(); //取数据项的个数
for (i = 0; i < numItems; i++) {
if(key < cur.getItem(i).data) {
return cur.getChild(i);
}
}
return cur.getChild(i);
}
/**
* 新增数据项
* @param value
*/
public void insert(long value) {
TreeNode cur = root;
DataItem temp = new DataItem(value); //数据项
//找到要插入数据项的节点位置
while (true) {
if(cur.isFull()) { //如果当前节点满了,需要先拆分再查找
split(cur); //拆分节点
cur = cur.getParent(); //拆分之后从它的父节点重新查找
cur = getNextChild(cur,value); //寻找下一个子节点
}else if(cur.isLeaf()) { //如果当前节点是叶子节点而且没有满
break;
}else {
cur = getNextChild(cur,value);
}
}
cur.insertItem(temp); //将当前数据项插进去
}
/**
* 拆分节点
* 最复杂的方法
* @param thisNode
*/
private void split(TreeNode thisNode) {
DataItem itemB,itemC;
TreeNode parent,child2,child3;
int itemIndex;
itemC = thisNode.removeIteam(); //因为删除的数据就是节点中最大的
itemB = thisNode.removeIteam(); //中间的数据项
child2 = thisNode.disConnectChild(2);
child3 = thisNode.disConnectChild(3);
TreeNode newRight = new TreeNode();
if(thisNode == root) { //如果拆分的节点是根节点
root = new TreeNode();
parent = root;
root.connectChild(0,thisNode);
}else {
parent = thisNode.getParent();
}
itemIndex = parent.insertItem(itemB);
int n = parent.getNumitems();
for (int i = n-1;i > itemIndex;i--) {
TreeNode temp = parent.disConnectChild(i);
parent.connectChild(i+1,temp);
}
parent.connectChild(itemIndex+1,newRight);
newRight.insertItem(itemC);
newRight.connectChild(0,child2);
newRight.connectChild(1,child3);
}
public void show() {
print(root,0,0);
}
/**
* 打印节点
* @param node 当前节点
* @param level 层数
* @param childNum 当前节点是第几个子节点
*/
private void print(TreeNode node,int level,int childNum) {
System.out.print("层数="+level+"\t当前节点是第"+childNum+"个子节点\t");
node.show();
int numItems = node.getNumitems(); //取子节点个数
for (int i = 0; i < numItems+1; i++) {
TreeNode next = node.getChild(i);
if(next != null) {
print(next,level+1,i); //向下层递归
}else { //没有下一个节点
return;
}
}
}
}
测试类:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @ClassName TestDemo
* @Description 2-3-4树
* @Author lzq
* @Date 2019/6/20 15:47
* @Version 1.0
**/
public class TestDemo {
public static void main(String[] args) throws IOException{
long value;
Tree234 theTree = new Tree234();
theTree.insert(50);
theTree.insert(40);
theTree.insert(60);
theTree.insert(30);
theTree.insert(70);
while (true) {
System.out.println("--------------------------");
System.out.println("输入打印(s)、增加(i)或者查找(f)");
char choice = getChar();
switch (choice) {
case 's':
theTree.show();
break;
case 'i':
System.out.println("输入要插入的值");
value = getInt();
theTree.insert(value);
break;
case 'f':
System.out.println("输入要查找的数据项");
value = getInt();
int found = theTree.find(value);
if(found != -1) {
System.out.println("查找到"+value);
}else {
System.out.println("没有找到数据"+value);
}
break;
default:
System.out.println("无效输入");
break;
}
}
}
public static String getString() throws IOException{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
return br.readLine();
}
public static char getChar() throws IOException{
return getString().charAt(0);
}
public static int getInt() throws IOException {
return Integer.parseInt(getString());
}
}
运行结果:
--------------------------
输入打印(s)、增加(i)或者查找(f)
s
层数=0 当前节点是第0个子节点 /50/
层数=1 当前节点是第0个子节点 /30/40/
层数=1 当前节点是第1个子节点 /60/70/
--------------------------
输入打印(s)、增加(i)或者查找(f)
i
输入要插入的值
35
--------------------------
输入打印(s)、增加(i)或者查找(f)
s
层数=0 当前节点是第0个子节点 /50/
层数=1 当前节点是第0个子节点 /30/35/40/
层数=1 当前节点是第1个子节点 /60/70/
--------------------------
输入打印(s)、增加(i)或者查找(f)
i
输入要插入的值
45
--------------------------
输入打印(s)、增加(i)或者查找(f)
s
层数=0 当前节点是第0个子节点 /35/50/
层数=1 当前节点是第0个子节点 /30/
层数=1 当前节点是第1个子节点 /40/45/
层数=1 当前节点是第2个子节点 /60/70/
--------------------------
输入打印(s)、增加(i)或者查找(f)
f
输入要查找的数据项
40
查找到40
--------------------------
输入打印(s)、增加(i)或者查找(f)
f
输入要查找的数据项
78
没有找到数据78
--------------------------
输入打印(s)、增加(i)或者查找(f)
2-3-4树中一个节点最多只能有4个孩子,那如果每个孩子数再多一些呢,这就是B树;
B树、B-树、B+树、B*树之间的关系