实现一个简单的234树

234树也是一种平衡树,但是相对于红黑树来说它的代码复杂度要低一些,它能做到树的平衡最主要的原因是它会进行合理的分裂。

1. 定义一个数据项类

public class DataItem {
    public int key;

    public DataItem(int key){
        this.key = key;
    }

    public void dispaly(){
        System.out.println("/"+this.key);
    }

}

2. 定义一个结点类

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("/");
    }
}

3. 定义234树

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

你可能感兴趣的:(实现一个简单的234树)