234树也是一种平衡树,但是相对于红黑树来说它的代码复杂度要低一些,它能做到树的平衡最主要的原因是它会进行合理的分裂。
public class DataItem {
public int key;
public DataItem(int key){
this.key = key;
}
public void dispaly(){
System.out.println("/"+this.key);
}
}
import javax.xml.crypto.Data;
public class NodeTree234 {
private static final int ORDER = 4;
private int itemNums;
private NodeTree234 parent;
private DataItem[] itemArrary = new DataItem[ORDER - 1]; //最多三个数据项
private NodeTree234[] childArray = new NodeTree234[ORDER]; //最多4个节点
//添加一个子节点,编码为0-4;
public void addChild(int childNum, NodeTree234 child) {
this.childArray[childNum] = child;
if (child != null) {
child.parent = this;
}
}
public NodeTree234 removeChild(int childNum) {
NodeTree234 nodeTree234 = this.childArray[childNum];
childArray[childNum] = null;
return nodeTree234;
}
public NodeTree234 getChild(int childNum) {
return this.childArray[childNum];
}
public NodeTree234 getParent() {
return parent;
}
public boolean isLeaf() {
return this.childArray[0] == null;
}
//数据项数量
public int getItemNum() {
return this.itemNums;
}
public DataItem getDataItem(int index) {
return itemArrary[index];
}
public boolean isFull() {
return this.itemNums == (ORDER - 1);
}
public int findItem(int key) {
for (int i = 0; i < ORDER - 1; i++) {
if (this.itemArrary[i] == null) {
break;
} else if (this.itemArrary[i].key == key) {
return i;
}
}
return -1;
}
//插入数据到正确的位置并返回索引
public int insertItem(DataItem newItem) {
//从后往前找到合适的位置
if (itemNums == (ORDER - 1)) {
return -1; //已经满了
}
itemNums++;
int newKey = newItem.key;
for (int i = ORDER - 2; i >= 0; i--) {
if (itemArrary[i] == null) {
continue;
} else {
int currentKey = itemArrary[i].key;
if (newKey < currentKey) { //当前项往后移
itemArrary[i + 1] = itemArrary[i];
} else { //找到合适位置了
itemArrary[i + 1] = newItem;
return i + 1;
}
}
}
this.itemArrary[0] = newItem;
return 0;
}
//移除最大一个项,也就是数据项的最后一个
public DataItem removeLargestItem() {
DataItem dataItem = itemArrary[this.itemNums - 1];
itemArrary[this.itemNums - 1] = null;
this.itemNums--;
return dataItem;
}
public void display() {
for (int i = 0; i < itemNums; i++) {
itemArrary[i].dispaly();
}
System.out.println("/");
}
}
import java.util.Random;
//二三四树 没有红黑树那些代码复杂,但同样保持了树的平衡,只是效率上低一点点,不是很多场合下可以考虑这种结构
public class Tree234 {
private NodeTree234 root = new NodeTree234(); //定义一个根节点
//找出KEY值项的第一个索引
public int find(int key){
NodeTree234 nodeTree234 = root;
int chindIndex;
while (true){
if((chindIndex = nodeTree234.findItem(key))!=-1){
return chindIndex;
}
else if(nodeTree234.isLeaf()){
return -1;
}
else{
nodeTree234 = getNextChild(nodeTree234,key);
}
}
}
public void insert(int key){
NodeTree234 curNode = root; //从根开始
DataItem tempItem = new DataItem(key);
while (true){
//数据项已满的就得先分裂
if(curNode.isFull()){
split(curNode);
//分裂完后继续从当前的父节点重新查询节点
curNode = curNode.getParent();
curNode = getNextChild(curNode,key);
}
else if(curNode.isLeaf()){ //叶子节点就是并且没有满 就是要找的节点
break;
}
else{
curNode = getNextChild(curNode,key);
}
}
//当前未满的叶子节点
curNode.insertItem(tempItem);
}
//分裂很重要,它是保持234树平衡的重要的动作,只要是数据项满的节点都应该分裂
public void split(NodeTree234 node){
if(node.isFull()==false){ //未满的情况下不可以
return;
}
//假定从左到右依次是ABC三个数据项,分裂主要操作就是将B上移(如果当前是根,那么上移到一个新节点。
// C右移到一个新节点,然后BC相关相连的子节点转移到右新节点里,具体可以看看代码
DataItem itemB,itemC;
NodeTree234 parent,child2,child3;
int itemIndex;
itemC = node.removeLargestItem();
itemB = node.removeLargestItem();
child2 = node.removeChild(2);
child3 = node.removeChild(3);
NodeTree234 newRight = new NodeTree234();
if(node == root){
this.root = new NodeTree234();
parent = this.root;
this.root.addChild(0,node);
}
else{
parent = node.getParent();
}
itemIndex = parent.insertItem(itemB); //上移
int n = parent.getItemNum(); //父节点数据量
//针对插入到中间的情况,那么右边的子节点编号要加1
for(int i=n-1;i>itemIndex;i--){ //修改子节点编号
parent.addChild(i+1,parent.removeChild(i));
}
parent.addChild(itemIndex+1,newRight);
newRight.insertItem(itemC); //将最右边的节点右移到新节点
//原来最右边的新节点相连的子节点同样右移
newRight.addChild(0,child2);
newRight.addChild(1,child3);
}
public NodeTree234 getNextChild(NodeTree234 node, int key) {
int i;
//依次找出合适的子节点,跳过比当前小的
for(i=0;i