2-3树的java实现

2-3树是比较早期的一个平衡树,跟2-3-4树差不多,编程起来稍微麻烦点,也是被红黑树取代了。

B树其实跟2-3树很像,只是子节点和数据项可能比较多,叶节点满时将其2分成2个节点,操作类似2-3树。B树可以应用在外部存储(例如磁盘)上。


//2-3树,每个节点最多2个数据项,3个子节点
//跟2-3-4树的差别:插入时如果需要分裂节点,则要将数据插入参与分裂过程
//而2-3-4树是分裂完后再插入

class DataStore {
    int data;

    public DataStore(int data) {
        this.data = data;
    }

    public void displayData() {
        System.out.println("/" + data);
    }
}

class Node23 {
    private static final int N = 3;
    private Node23[] childArray = new Node23[N];
    private DataStore[] itemArray = new DataStore[N - 1];
    private Node23 parent;
    private int itemNums;

    public void connectChild(int index, Node23 child) {
        childArray[index] = child;
        if (child != null)
            child.parent = this;
    }

    public Node23 disconnectChild(int index) {
        Node23 temp = childArray[index];
        childArray[index] = null;
        return temp;
    }

    public Node23 getParent() {
        return parent;
    }

    public boolean isLeaf() {
        return childArray[0] == null;
    }

    public boolean isFull() {
        return itemNums == N - 1;
    }

    public DataStore getItem(int index) {
        return itemArray[index];
    }

    public Node23 getChild(int index) {
        return childArray[index];
    }

    public int getItemNums() {
        return itemNums;
    }

    public int findItem(int key) {
        for (int i = 0; i < itemNums; i++) {
            if (itemArray[i] == null)
                break;
            else if (key == itemArray[i].data)
                return i;
        }
        return -1;
    }

    public int insertItem(DataStore item) {
        itemNums++;
        int key = item.data;
        for (int i = N - 2; i >= 0; i--) {
            if (itemArray[i] == null)
                continue;
            else {
                int curKey = itemArray[i].data;
                if (key < curKey) {
                    itemArray[i + 1] = itemArray[i];
                } else {
                    itemArray[i + 1] = item;
                    return i + 1;
                }
            }
        }
        itemArray[0] = item;
        return 0;
    }

    public DataStore removeItem() {
        DataStore temp = itemArray[itemNums - 1];
        itemArray[itemNums - 1] = null;
        itemNums--;
        return temp;
    }

    public void displayNode() {
        for (int i = 0; i < itemNums; i++)
            itemArray[i].displayData();
        System.out.println("/");
    }

}

public class Tree23 {
    private Node23 root = new Node23();
    /*
     * public int find(int key) { Node23 curNode = root; int childNum; while
     * (true) { if ((childNum = curNode.findItem(key)) != -1) return childNum;
     * else if (curNode.isLeaf()) return -1; else curNode =
     * getNextChild(curNode, key); } }
     */

    private Node23 getNextChild(Node23 node, int key) {
        for (int i = 0; i < node.getItemNums(); i++) {
            if (key < node.getItem(i).data)
                return node.getChild(i);
        }
        return node.getChild(node.getItemNums());
    }

    public void insert(int data) {
        DataStore item = new DataStore(data);
        Node23 curNode = root;
        // 先找到插入的位置
        while (true) {
            if (curNode.isLeaf())
                break;
            else
                curNode = getNextChild(curNode, data);
        }
        // 如果到达叶节点且满了
        if (curNode.isFull()) {
            split(curNode, data);
        } else
            curNode.insertItem(item);

    }

    // 思路:在满的叶节点插入新的数据,得到1个父节点和2个子节点组成的一个分支
    // 然后通过递归使这个分支倒推上去
    public void split(Node23 node, int key) {
        Node23 parent = node.getParent();
        Node23 newNode = new Node23();// 叶节点拆开后新的子节点
        Node23 newNode2 = new Node23();// 保存首次分支父节点
        DataStore mid;
        if (key < node.getItem(0).data) {
            newNode.insertItem(node.removeItem());
            mid = node.removeItem();
            node.insertItem(new DataStore(key));
        } else if (key < node.getItem(1).data) {
            newNode.insertItem(node.removeItem());
            mid = new DataStore(key);
        } else {
            newNode.insertItem(new DataStore(key));
            mid = node.removeItem();
        }
        // 第一次根满了的时候,需要手动设置一次,感觉可以移动别处,因为只需要用一次
        if (node == root)
            root = newNode2;
        newNode2.insertItem(mid);
        newNode2.connectChild(0, node);
        newNode2.connectChild(1, newNode);
        connectNode(parent, newNode2);// 开始递归调整
    }

    private void connectNode(Node23 parent, Node23 node) {
        int key = node.getItem(0).data;
        if (node == root) {// 当前节点为根,这样不需要判断更高的parent了
            return;
        }
        if (parent.isFull()) {
            Node23 gparent = parent.getParent();
            Node23 newNode = new Node23();
            Node23 temp1, temp2;
            DataStore item;
            // 当叶节点满了,并且其父节点也满了时,有3种情况
            // 分别对应此叶节点为父节点的第1,2,3个子节点
            if (key < parent.getItem(0).data) {
                temp1 = parent.disconnectChild(1);
                temp2 = parent.disconnectChild(2);
                newNode.connectChild(0, temp1);
                newNode.connectChild(1, temp2);
                newNode.insertItem(parent.removeItem());
                item = parent.removeItem();
                parent.insertItem(item);
                parent.connectChild(0, node);
                parent.connectChild(1, newNode);
            } else if (key < parent.getItem(1).data) {
                temp1 = parent.disconnectChild(0);
                temp2 = parent.disconnectChild(2);
                Node23 n2 = new Node23();
                newNode.insertItem(parent.removeItem());// N
                newNode.connectChild(0, node.disconnectChild(1));
                newNode.connectChild(1, temp2);
                n2.insertItem(parent.removeItem());// M
                n2.connectChild(0, temp1);
                n2.connectChild(1, node.disconnectChild(0));
                parent.insertItem(node.removeItem());
                parent.connectChild(0, n2);
                parent.connectChild(1, newNode);
            } else {
                item = parent.removeItem();
                newNode.insertItem(parent.removeItem());// M
                newNode.connectChild(0, parent.disconnectChild(0));
                newNode.connectChild(1, parent.disconnectChild(1));
                parent.disconnectChild(2);
                parent.insertItem(item);
                parent.connectChild(0, newNode);
                parent.connectChild(1, node);
            }
            // 直接修改了parent,省了个变量。。
            // 新的分支引领节点是parent,gparent为开始时的父节点
            connectNode(gparent, parent);
        }
        // 如果满叶节点的父节点不满,2种情况,无需递归
        else {
            if (key < parent.getItem(0).data) {
                Node23 temp = parent.disconnectChild(1);
                parent.connectChild(0, node.disconnectChild(0));
                parent.connectChild(1, node.disconnectChild(1));
                parent.connectChild(2, temp);
            } else {
                parent.connectChild(1, node.disconnectChild(0));
                parent.connectChild(2, node.disconnectChild(1));
            }
            parent.insertItem(node.getItem(0));
        }
    }

    public void displayTree() {
        fineDisplay(root, 0, 0);
    }

    // 展示23树的结构
    private void fineDisplay(Node23 node, int level, int childNum) {
        System.out.println("level " + level + " child " + childNum);
        node.displayNode();
        for (int i = 0; i < node.getItemNums() + 1; i++) {
            Node23 child = node.getChild(i);
            if (child != null)
                fineDisplay(child, level + 1, i);
            else
                return;
        }

    }

    // 一个一个数地插入进去,把这些数据修改顺序结果好像不变。
    // 没有更细致的检验程序的正确性,看到错误的朋友请指出。
    public static void main(String[] args) {
        Tree23 tree = new Tree23();
        tree.insert(80);
        tree.insert(50);
        tree.insert(40);
        tree.insert(30);
        tree.insert(20);
        tree.insert(70);
        tree.insert(60);
        tree.insert(10);
        tree.displayTree();
    }
}


猜你喜欢

转载自blog.csdn.net/qq_26567507/article/details/80158317
今日推荐