数据结构——B树

B树介绍

B树是平衡多叉树,是平衡二叉搜索树的一般化(图来自网络)
在这里插入图片描述

B树的用途

B树通过增加元素和节点的方式实现完美平衡搜索树,避免平衡二叉树多次旋转导致的时间性能损失,即用空间换时间

插入通式(看不懂先看下面的图)

m阶B树插入一个元素时,首先判断元素是否存在,如果不存在,即在叶子结点处结束,判断叶节点

  • 若该节点元素个数小于m-1,直接插入
  • 若该节点元素个数等于m-1,引起节点分裂,取中间元素(偶数则随机选取)插入到父节点中
  • 重复上面动作,直到所有节点符合B树的规则,若一直分裂到根节点,则生成新的根节点,高度增加1

删除通式

查找待删除元素是否存在,判断待删除元素是叶节点元素还是非叶节点元素

  • 删除非叶节点元素,则用后继元素替代待删除元素在节点中的位置,问题转为了删除后继元素,以此循环,直到变成删除叶节点元素
  • 删除叶节点元素,判断元素数量,若不小于⌈m/2⌉-1,则直接删除,否则判断兄弟结点元素数量
  • 若兄弟节点元素数量不小于⌈m/2⌉-1,则父结点元素下移,兄弟结点元素上移
  • 若兄弟节点元素数量等于⌈m/2⌉-1,则父节点和兄弟结点合并成一个结点,循环判断父节点的兄弟节点,若合并到根节点则高度减1

3阶B树(2-3树)

3阶B树每个节点最多有3个子节点,2个元素,一颗2-3树或为一颗空树,或有以下节点组成:

  • 2-节点:含一个元素和两个子树(左右子树),左子树所有元素的值均小于它父节点,右子树所有元素的值均大于它父节点

  • 3-节点:含两个元素和三个子树(左中右子树),左子树所有元素的值均小于它父节点,中子树所有元素的值都位于父节点两个元素之间,右子树所有元素的值均大于它父节点

插入

向2-节点插入,则将2-节点变为3-节点(即待插入节点不满直接插入)
在这里插入图片描述
向3节点插入(即待插入节点已满),则分为3种情况

  • 向一棵只含有3-结点的树插入,中间值成为根结点,左右值成为左右节点,树高+1
    在这里插入图片描述
  • 向父结点为2-结点的3-结点中插入,待插入节点成为4-结点,其中间值升到父节点,使父节点成为一个3-节点,然后将其左右值分别挂在这个父节点的恰当位置,树的高度不发生改变
    在这里插入图片描述
  • 向父节点为3-结点的3-结点中插入,待插入节点成为4-结点,其中间值升到父节点,然后父节点成为4-结点,其中间值升到父节点…这是一个递归的过程,直到遇到的结点不再是3-结点
    在这里插入图片描述

搜索

搜索和二叉搜索树一样,小于往左走,大于往右走,位于中间往中间走
在这里插入图片描述

删除

  • 关于2-3树的删除,很多都讲的不对,本文参考了这个链接 https://studygolang.com/articles/27787?fr=sidebar ,并对其进行了总结(如果看不懂我的,可以看看他的)

对于2-3树的删除,分为删除非叶节点元素删除叶节点元素

  • 删除非叶节点元素:元素可能位于2-节点或3节点(必有后继节点),从子树中找到其后继节点元素1(必为叶节点)替代,问题转化为删除叶节点元素1
待删除元素所在位置后继节点
2-节点元素1其右子树最小节点
3-节点元素1其中子树最小节点
3-节点元素2其右子树最小节点

在这里插入图片描述

  • 删除叶节点元素,若叶节点已满则直接删除,不影响平衡

在这里插入图片描述

  • 删除叶节点元素,若未满则需要考虑其父节点,若父节点为2节点,递归 / 循环判断兄弟节点,若兄弟节点满,则重新分配,若兄弟节点未满则合并,若合并到根节点则树高减一(完全二叉树)
    在这里插入图片描述
    (删除左右两边都是类似的,主要是合并或分配的方向和元素不同,注意合并会拐弯)
    在这里插入图片描述
  • 删除叶节点元素,若未满则需要考虑其父节点,若父节点为3节点,则判断删除的是哪个子树,下图删除左未满叶节点(调整可以有很多方式,只要平衡就行,如下不改变右节点)
    在这里插入图片描述
    下图删除中未满叶节点(调整可以有很多方式,只要平衡就行,删除中节点可能要改变左右节点)在这里插入图片描述
    下图删除右未满叶节点(调整可以有很多方式,只要平衡就行,如下不改变左节点)
    在这里插入图片描述
    至此2-3树的删除就完了

代码实现

  • 节点数据类Node的构造函数分别生成叶节点和非叶节点用于替换原来的节点
  • toString()、innerMidOrderTraversal()、innerLevelTraversal() 用于打印中序、层次遍历验证结果
  • getParent() 获取父类,通过比较元素大小判断(这里可能写的不太好)
  • searchNode() 用于找到待插入/删除的节点
  • search() 查找元素,找到待插入位置判断元素是否已经存在
  • replaceNode() 把增加删除看作替换节点,因为java是值传递,替换节点需要重新链接,故替换实际是替换内部属性,和Node构造函数结合避免了大量的属性设置代码,增加代码可读性
  • isFull()、isLeaf()、isTwoNode()、isThreeNode() 判断是否已满、叶节点、2-节点、3-节点

add()、spilt()、splitLeaf()、spiltTwoNode()、spiltThreeNode() 为添加元素代码,具体为

  • 第一次插入元素为根节点
  • searchNode()找到待插入的叶节点,判断是否已满,未满直接插入,已满则需要分裂
  • spilt()分裂先分裂叶节点,若父节点为3-节点则循环分裂,若父节点为2-节点则分裂为3-节点
  • 具体的分裂步骤需结合图和代码理解

del()、getMinNode()、delInnerNode()、delLeaf()、delFullLeaf()、delNotFullLeaf()、parentIsTwoNode()、parentIsThreeNode()为删除元素代码,具体为

  • del() 判断是删除叶节点元素还是非叶节点元素
  • delInnerNode() 将删除非叶节点元素转化为删除叶节点元素
  • delLeaf() 判断待删除叶节点是否已满,满直接删除,未满则需调整
  • delNotFullLeaf() 判断未满待删除叶节点其父节点是2-节点还是3-节点
  • 具体合并和调整步骤需结合图和代码理解
class Tree23 {
    private class Node {
        int value1;
        int value2;
        Node leftChild;
        Node midChild;
        Node rightChild;

        //生成1元素叶节点
        public Node(int value1) {
            this.value1 = value1;
        }

        //生成2元素叶节点
        public Node(int value1, int value2) {
            this.value1 = value1;
            this.value2 = value2;
        }

        //生成2-非叶节点
        public Node(int value1, Node leftChild, Node rightChild) {
            this.value1 = value1;
            this.leftChild = leftChild;
            this.rightChild = rightChild;
        }

        //生成3-非叶节点
        public Node(int value1, int value2, Node leftChild, Node midChild, Node rightChild) {
            this.value1 = value1;
            this.value2 = value2;
            this.leftChild = leftChild;
            this.midChild = midChild;
            this.rightChild = rightChild;
        }
    }

    private Node root;

    @NonNull
    @Override
    public String toString() {
        System.out.print("\n对节点层次遍历:");
        innerLevelTraversal();
        System.out.println();

        System.out.print("对元素中序遍历: ");
        innerMidOrderTraversal(root);
        System.out.println();
        System.out.println("----------------------------------");
        return "";
    }

    private void innerMidOrderTraversal(Node node) {
        if (node == null) {
            return;
        }
        innerMidOrderTraversal(node.leftChild);
        System.out.print(node.value1 + " ");
        innerMidOrderTraversal(node.midChild);
        if (node.value2 != 0) {
            System.out.print(node.value2 + " ");
        }
        innerMidOrderTraversal(node.rightChild);
    }

    private void innerLevelTraversal() {
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            System.out.print("[value1=" + node.value1 + ",value2=" + node.value2 + "] ");
            if (node.leftChild != null) {
                queue.offer(node.leftChild);
            }
            if (node.midChild != null) {
                queue.offer(node.midChild);
            }
            if (node.rightChild != null) {
                queue.offer(node.rightChild);
            }
        }
    }

    private Node getParent(Node node) { //通过比较元素判断往哪走,这里写的还待优化
        if (root == node) {
            return null;
        }
        if (node == null) {
            return null;
        }
        Node current = root;
        Node parent = null;
        while (current != null) {
            if (!isFull(node) && !isFull(current)) {
                if (node.value1 < current.value1) {
                    parent = current;
                    current = current.leftChild;
                } else if (node.value1 > current.value1) {
                    parent = current;
                    current = current.rightChild;
                } else {
                    return parent;
                }
            } else if (isFull(node) && !isFull(current)) {
                if (node.value2 < current.value1) {
                    parent = current;
                    current = current.leftChild;
                } else if (node.value1 > current.value1) {
                    parent = current;
                    current = current.rightChild;
                } else {
                    return parent;
                }
            } else if (!isFull(node) && isFull(current)) {
                if (node.value1 < current.value1) {
                    parent = current;
                    current = current.leftChild;
                } else if (node.value1 > current.value1 && node.value1 < current.value2) {
                    parent = current;
                    current = current.midChild;
                } else if (node.value1 > current.value2) {
                    parent = current;
                    current = current.rightChild;
                } else {
                    return parent;
                }
            } else if (isFull(node) && isFull(current)) {
                if (node.value2 < current.value1) {
                    parent = current;
                    current = current.leftChild;
                } else if (node.value1 > current.value1 && node.value2 < current.value2) {
                    parent = current;
                    current = current.midChild;
                } else if (node.value1 > current.value2) {
                    parent = current;
                    current = current.rightChild;
                } else {
                    return parent;
                }
            }
        }
        return parent;
    }

    private Node searchNode(int value) {
        Node insertNode = root;
        while (!isLeaf(insertNode)) {             //找到元素待插入或待删除的叶节点,未处理相等元素插入的情况
            if (value == insertNode.value1 || value == insertNode.value2) {
                return insertNode;
            } else if (value < insertNode.value1) {
                insertNode = insertNode.leftChild;
            } else if (value > insertNode.value1 && value < insertNode.value2) {
                insertNode = insertNode.midChild;
            } else if (value > insertNode.value2) {
                insertNode = insertNode.rightChild;
            }
        }
        return insertNode;
    }

    public boolean search(int value) {      //找到待插入的节点判断元素是否存在
        Node insertNode = searchNode(value);
        return insertNode.value1 == value || insertNode.value2 == value;
    }

    private void replaceNode(Node source, Node target) {    //统一替换元素各个属性,避免多处设置影响可读性
        if (source == null || target == null) {
            return;
        }
        source.value1 = target.value1;
        source.value2 = target.value2;
        source.leftChild = target.leftChild;
        source.midChild = target.midChild;
        source.rightChild = target.rightChild;
    }

    private boolean isFull(Node node) {     //判断元素是否已满
        if (node == null) {
            return false;
        }
        return node.value1 != 0 && node.value2 != 0;
    }

    private boolean isLeaf(Node node) {     //判断是否是叶节点
        if (node == null) {
            return false;
        }
        return node.leftChild == null && node.midChild == null && node.rightChild == null;
    }

    private boolean isTwoNode(Node node) {  //判断是否有左右节点
        if (node == null) {
            return false;
        }
        return node.leftChild != null && node.midChild == null && node.rightChild != null;
    }

    private boolean isThreeNode(Node node) {    //判断是否有左中右节点
        if (node == null) {
            return false;
        }
        return node.leftChild != null && node.midChild != null && node.rightChild != null;
    }

    public void add(int value) {
        System.out.print("插入:" + value + " ");
        if (root == null) {             //若第一次插入则成为根节点
            root = new Node(value);
            return;
        }
        Node insertNode = searchNode(value);    //找到待插入的节点,一定是叶节点
        if (isFull(insertNode)) {               //待插入节点已满,需要分裂
            spilt(value, insertNode);
        } else {                                //待插入的节点未满,可直接插入、检查是否需要交换元素位置
            if (value > insertNode.value1) {
                insertNode.value2 = value;
            } else {
                insertNode.value2 = insertNode.value1;
                insertNode.value1 = value;
            }
        }
    }

    private void spilt(int value, Node insertNode) {
        int up = splitLeaf(value, insertNode);          //分裂叶节点
        Node insertNodeParent = getParent(insertNode);
        if (insertNodeParent != null) {                //若存在父节点则分裂父节点
            while (isThreeNode(insertNodeParent)) {     //若父节点为3节点则递归分裂3节点,让3节点变成2节点,若递归到根节点树高+1
                up = spiltThreeNode(up, insertNodeParent);
                insertNodeParent = getParent(insertNodeParent);
            }
            if (isTwoNode(insertNodeParent)) {      //父节点为2节点(或递归到2节点)则让2节点变成3节点
                spiltTwoNode(up, insertNodeParent);
            }
        }
    }

    private int splitLeaf(int value, Node insertNode) {     //分裂叶节点,中间值成为根节点,左右值成为左右子树
        Node replaceNode = null;
        if (value < insertNode.value1) {
            replaceNode = new Node(insertNode.value1, new Node(value), new Node(insertNode.value2));
        } else if (value > insertNode.value1 && value < insertNode.value2) {
            replaceNode = new Node(value, new Node(insertNode.value1), new Node(insertNode.value2));
        } else if (value > insertNode.value2) {
            replaceNode = new Node(insertNode.value2, new Node(insertNode.value1), new Node(value));
        }
        replaceNode(insertNode, replaceNode);
        return insertNode.value1;
    }

    private void spiltTwoNode(int value, Node insertNode) {     //分裂2节点成3节点
        Node replaceNode = null;
        if (value < insertNode.value1) {        //若上升的值是左子树的根节点,则一定小于父节点,让其成为value1,将其左右子树变成父节点的左中子树
            replaceNode = new Node(value, insertNode.value1, insertNode.leftChild.leftChild, insertNode.leftChild.rightChild, insertNode.rightChild);
        } else if (value > insertNode.value1) { //若上升的值是右子树的根节点,则一定大于父节点,让其成为value2,将其左右子树变成父节点的中右子树
            replaceNode = new Node(insertNode.value1, value, insertNode.leftChild, insertNode.rightChild.leftChild, insertNode.rightChild.rightChild);
        }
        replaceNode(insertNode, replaceNode);
    }

    private int spiltThreeNode(int value, Node insertNode) {    //分裂3节点成2节点,树高+1
        Node replaceNode = null;
        Node left = null;
        Node right = null;
        if (value < insertNode.value1) {        //中间值成为新的父节点,值从左子树上升,说明左子树已经分裂好了,右节点拿到父节点的中右子树
            right = new Node(insertNode.value2, insertNode.midChild, insertNode.rightChild);
            replaceNode = new Node(insertNode.value1, insertNode.leftChild, right);
        } else if (value > insertNode.value1 && value < insertNode.value2) {    //中间值成为新的父节点,值从中子树上升,左节点拿到父节点左子树+中子树的左节点,右节点拿到父节点中子树的右节点+右子树
            left = new Node(insertNode.value1, insertNode.leftChild, insertNode.midChild.leftChild);
            right = new Node(insertNode.value2, insertNode.midChild.rightChild, insertNode.rightChild);
            replaceNode = new Node(value, left, right);
        } else if (value > insertNode.value2) {     //中间值成为新的父节点,值从右子树上升,说明右子树已经分裂好了,左节点拿到父节点的左中子树
            left = new Node(insertNode.value1, insertNode.leftChild, insertNode.midChild);
            replaceNode = new Node(insertNode.value2, left, insertNode.rightChild);
        }
        replaceNode(insertNode, replaceNode);
        return insertNode.value1;       //返回上升的节点,即根节点
    }

    public void del(int value) {
        System.out.print("删除:" + value + " ");
        Node removeNode = searchNode(value);
        if (isLeaf(removeNode)) {       //删除叶节点
            delLeaf(value, removeNode, getParent(removeNode));
        } else {
            delInnerNode(value, removeNode);    //删除非叶节点
        }
    }

    private Node getMinNode(Node node) {       //获取子树中最小节点
        while (node.leftChild != null) {
            node = node.leftChild;
        }
        return node;
    }

    private void delInnerNode(int value, Node removeNode) { //删除非叶节点元素,用后继节点元素value1替换
        Node sucNode = null;
        if (isTwoNode(removeNode)) {                      //若待删除元素在2节点,后继节点为右子树最小叶节点
            sucNode = getMinNode(removeNode.rightChild);
            removeNode.value1 = sucNode.value1;
        } else if (isThreeNode(removeNode)) {
            if (value == removeNode.value1) {         //若待删除元素在3节点的value1,后继节点为中子树最小叶节点
                sucNode = getMinNode(removeNode.midChild);
                removeNode.value1 = sucNode.value1;
            } else if (value == removeNode.value2) {      //若待删除元素在3节点的value2,后继节点为右子树最小叶节点
                sucNode = getMinNode(removeNode.rightChild);
                removeNode.value2 = sucNode.value1;
            }
        }
        delLeaf(sucNode.value1, sucNode, removeNode);   //问题转为删除叶节点value1
    }

    private void delLeaf(int value, Node removeNode, Node removeNodeParent) {
        if (isFull(removeNode)) {                       //删除已满叶节点,判断删除的是value1还是value2
            delFullLeaf(value, removeNode);
        } else {                                        //删除未满未满叶节点,删除后需要调整,调整过程和删除的value无关
            delNotFullLeaf(removeNode, removeNodeParent);
        }
    }

    private void delFullLeaf(int value, Node removeNode) {
        if (value == removeNode.value1) {           //若删除value1,则先用value2覆盖,再置空value2
            removeNode.value1 = removeNode.value2;
        }
        removeNode.value2 = 0;                      //删除value2直接置空
    }

    private void delNotFullLeaf(Node removeNode, Node removeNodeParent) {   //删除未满叶节点需要考虑其父节点
        if (isTwoNode(removeNodeParent)) {
            parentIsTwoNode(removeNode, removeNodeParent);
        } else if (isThreeNode(removeNodeParent)) {
            parentIsThreeNode(removeNode, removeNodeParent);
        }
    }

    private void parentIsTwoNode(Node removeNode, Node removeNodeParent) {  //当父节点为2节点,需要考虑完全二叉树的情况,这里比较复杂,最好结合图理解
        Node mergeNode = null;
        while (removeNodeParent != null) {                  //若父节点不为空,则循环判断兄弟节点
            if (removeNode == removeNodeParent.leftChild) {
                if (!isFull(removeNodeParent.rightChild)) {     //删除的是左节点且右节点未满,则合并右父节点
                    mergeNode = new Node(removeNodeParent.value1, removeNodeParent.rightChild.value1, mergeNode, removeNodeParent.rightChild.leftChild, removeNodeParent.rightChild.rightChild);
                } else {            //删除的是左节点且右节点已满,则让右节点从3节点分裂成2节点
                    Node left = new Node(removeNodeParent.value1, mergeNode, removeNodeParent.rightChild.leftChild);
                    Node right = new Node(removeNodeParent.rightChild.value2, removeNodeParent.rightChild.midChild, removeNodeParent.rightChild.rightChild);
                    Node replace = new Node(removeNodeParent.rightChild.value1, left, right);
                    replaceNode(removeNodeParent, replace);
                    break;
                }
            } else if (removeNode == removeNodeParent.rightChild) {
                if (!isFull(removeNodeParent.leftChild)) {  //删除的是右节点且左未满,则合并左父节点
                    mergeNode = new Node(removeNodeParent.leftChild.value1, removeNodeParent.value1, removeNodeParent.leftChild.leftChild, removeNodeParent.leftChild.rightChild, mergeNode);
                } else {    //删除的是右节点且左节点已满,则让左节点从3节点分裂成2节点
                    Node left = new Node(removeNodeParent.leftChild.value1, removeNodeParent.leftChild.leftChild, removeNodeParent.leftChild.midChild);
                    Node right = new Node(removeNodeParent.value1, removeNodeParent.leftChild.rightChild, mergeNode);
                    Node replace = new Node(removeNodeParent.leftChild.value2, left, right);
                    replaceNode(removeNodeParent, replace);
                    break;
                }
            }
            removeNode = removeNodeParent;                  //赋值,循环判断下一个是左还是右节点
            removeNodeParent = getParent(removeNodeParent);
        }
        if (removeNodeParent == null) {         //若合并到了根节点(完全二叉树),替换根节点,树高-1
            replaceNode(root, mergeNode);
        }
    }

    private void parentIsThreeNode(Node removeNode, Node removeNodeParent) {    //当父节点为3节点,可能变成3节点或2节点
        Node replaceNode;
        if (removeNode == removeNodeParent.leftChild) {
            if (isFull(removeNodeParent.midChild)) {                //若删除左节点且中节点已满,调整3节点
                Node left = new Node(removeNodeParent.value1);
                Node mid = new Node(removeNodeParent.midChild.value2);
                replaceNode = new Node(removeNodeParent.midChild.value1, removeNodeParent.value2, left, mid, removeNodeParent.rightChild);
                replaceNode(removeNodeParent, replaceNode);
            } else {                                        //若删除左节点且中节点未满,变为2节点
                Node left = new Node(removeNodeParent.value1, removeNodeParent.midChild.value1);
                replaceNode = new Node(removeNodeParent.value2, left, removeNodeParent.rightChild);
                replaceNode(removeNodeParent, replaceNode);
            }
        } else if (removeNode == removeNodeParent.midChild) {
            if (!isFull(removeNodeParent.leftChild)) {          //若删除中节点且左节点未满,变为2节点
                Node left = new Node(removeNodeParent.leftChild.value1, removeNodeParent.value1);
                replaceNode = new Node(removeNodeParent.value2, left, removeNodeParent.rightChild);
                replaceNode(removeNodeParent, replaceNode);
            } else if (!isFull(removeNodeParent.rightChild)) {  //若删除中节点且左节点满右节点未满,变为2节点
                Node right = new Node(removeNodeParent.value2, removeNodeParent.rightChild.value1);
                replaceNode = new Node(removeNodeParent.value1, removeNodeParent.leftChild, right);
                replaceNode(removeNodeParent, replaceNode);
            } else {        //若删除中节点且左右节点都满,调整3节点
                Node mid = new Node(removeNodeParent.value2);
                Node right = new Node(removeNodeParent.rightChild.value2);
                replaceNode = new Node(removeNodeParent.value1, removeNodeParent.rightChild.value1, removeNodeParent.leftChild, mid, right);
                replaceNode(removeNodeParent, replaceNode);
            }
        } else if (removeNode == removeNodeParent.rightChild) {
            if (isFull(removeNodeParent.midChild)) {            //若删除右节点且中节点满,调整3节点
                Node mid = new Node(removeNodeParent.midChild.value1);
                Node right = new Node(removeNodeParent.value2);
                replaceNode = new Node(removeNodeParent.value1, removeNodeParent.midChild.value2, removeNodeParent.leftChild, mid, right);
                replaceNode(removeNodeParent, replaceNode);
            } else {        //若删除右节点且中节点未满,变为2节点
                Node right = new Node(removeNodeParent.midChild.value1, removeNodeParent.value2);
                replaceNode = new Node(removeNodeParent.value1, removeNodeParent.leftChild, right);
                replaceNode(removeNodeParent, replaceNode);
            }
        }
    }
}

测试

插入测试

向2节点插入变为3节点

向 [50, 0] 插入30变为 [30, 50]

Tree23 tree = new Tree23();
tree.add(50);
System.out.println(tree);
tree.add(30);
System.out.println(tree);

打印如下

插入:50 
对节点层次遍历:[value1=50,value2=0] 
对元素中序遍历: 50 
----------------------------------
插入:30 
对节点层次遍历:[value1=30,value2=50] 
对元素中序遍历: 30 50 
----------------------------------

向只含3节点的树插入

向 [30, 50] 插入80 分裂为2节点,[50, 0] 为根,[30, 0] 和 [80, 0] 为左右子树

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
System.out.println(tree);
tree.add(80);
System.out.println(tree);

打印如下

插入:50 插入:30 
对节点层次遍历:[value1=30,value2=50] 
对元素中序遍历: 30 50 
----------------------------------
插入:80 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] 
对元素中序遍历: 30 50 80 
----------------------------------

向父结点为2-结点的3-结点中插入

向父节点为 [50, 0] 的右子节点 [80, 90] 插入 70,父节点分裂为3节点

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.add(70);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90] 
对元素中序遍历: 30 50 80 90 
----------------------------------
插入:70 
对节点层次遍历:[value1=50,value2=80] [value1=30,value2=0] [value1=70,value2=0] [value1=90,value2=0] 
对元素中序遍历: 30 50 70 80 90 
----------------------------------

向父节点为3-结点的3-结点中插入

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(70);
tree.add(20);
tree.add(40);
tree.add(10);
tree.add(25);
tree.add(15);
System.out.println(tree);
tree.add(18);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:70 插入:20 插入:40 插入:10 插入:25 插入:15 
对节点层次遍历:[value1=50,value2=0] [value1=20,value2=30] [value1=80,value2=0] [value1=10,value2=15] [value1=25,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] 
对元素中序遍历: 10 15 20 25 30 40 50 70 80 90 
----------------------------------
插入:18 
对节点层次遍历:[value1=20,value2=50] [value1=15,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=10,value2=0] [value1=18,value2=0] [value1=25,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] 
对元素中序遍历: 10 15 18 20 25 30 40 50 70 80 90 
----------------------------------

搜索测试

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
boolean search = tree.Search(80);
if(search){
    System.out.println("found");
}else {
    System.out.println("not found");
}

打印如下

插入:50 插入:30 插入:80 插入:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90] 
对元素中序遍历: 30 50 80 90 
----------------------------------
found

删除测试

删除已满叶节点

删除已满叶节点元素1

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.del(80);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90] 
对元素中序遍历: 30 50 80 90 
----------------------------------
删除:80 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=90,value2=0] 
对元素中序遍历: 30 50 90 
----------------------------------

删除已满叶节点元素2

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.del(90);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90] 
对元素中序遍历: 30 50 80 90 
----------------------------------
删除:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] 
对元素中序遍历: 30 50 80 
----------------------------------

删除未满叶节点且其父节点为2-节点

若兄弟节点已满,则重新分配

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
System.out.println(tree);
tree.del(30);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=90] 
对元素中序遍历: 30 50 80 90 
----------------------------------
删除:30 
对节点层次遍历:[value1=80,value2=0] [value1=50,value2=0] [value1=90,value2=0] 
对元素中序遍历: 50 80 90 
----------------------------------

若兄弟节点未满(完全二叉树),则循环合并兄弟和父节点

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
System.out.println(tree);
tree.del(40);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 
----------------------------------
删除:40 
对节点层次遍历:[value1=50,value2=80] [value1=20,value2=30] [value1=70,value2=0] [value1=90,value2=0] 
对元素中序遍历: 20 30 50 70 80 90 
----------------------------------

若循环过程中遇到满兄弟节点,则重新分配

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(40);
System.out.println(tree);
----------------------------------

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 95 100 
----------------------------------
删除:40 
对节点层次遍历:[value1=80,value2=0] [value1=50,value2=0] [value1=95,value2=0] [value1=20,value2=30] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 50 70 80 90 95 100 
----------------------------------

删除未满叶节点且其父节点为3-节点

删除左未满叶节点

若中节点未满

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(70);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 95 100 
----------------------------------
删除:70 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=95,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=80,value2=90] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 80 90 95 100 
----------------------------------

若中节点已满

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(94);
System.out.println(tree);
tree.del(70);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:94 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=94] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 94 95 100 
----------------------------------
删除:70 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=90,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=80,value2=0] [value1=94,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 80 90 94 95 100 
----------------------------------
删除中未满叶节点

若左节点未满

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(90);
System.out.println(tree);
----------------------------------

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 95 100 
----------------------------------
删除:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=95,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=80] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 95 100 
----------------------------------

若左节点已满,右节点未满

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(65);
System.out.println(tree);
tree.del(90);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:65 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 65 70 80 90 95 100 
----------------------------------
删除:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=95,value2=100] 
对元素中序遍历: 20 30 40 50 65 70 80 95 100 
----------------------------------

若左右节点已满

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(65);
tree.add(105);
System.out.println(tree);
tree.del(90);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:65 插入:105 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=90,value2=0] [value1=100,value2=105] 
对元素中序遍历: 20 30 40 50 65 70 80 90 95 100 105 
----------------------------------
删除:90 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=100] [value1=20,value2=0] [value1=40,value2=0] [value1=65,value2=70] [value1=95,value2=0] [value1=105,value2=0] 
对元素中序遍历: 20 30 40 50 65 70 80 95 100 105 
----------------------------------
删除右未满叶节点

若中节点未满

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(100);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 95 100 
----------------------------------
删除:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=95] 
对元素中序遍历: 20 30 40 50 70 80 90 95 
----------------------------------

若中节点已满

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
tree.add(94);
System.out.println(tree);
tree.del(100);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 插入:94 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=94] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 94 95 100 
----------------------------------
删除:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=94] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=95,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 94 95 
----------------------------------

删除非叶2-节点

转换为删除其右子树最小叶节点

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(30);
System.out.println(tree);

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 95 100 
----------------------------------
删除:30 
对节点层次遍历:[value1=80,value2=0] [value1=50,value2=0] [value1=95,value2=0] [value1=20,value2=40] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 40 50 70 80 90 95 100 
----------------------------------

删除非叶3-节点

删除元素1,转换为删除其中子树最小叶节点

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(80);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 95 100 
----------------------------------
删除:80 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=95,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=90] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 90 95 100 
----------------------------------

删除元素2,转换为删除其右子树最小叶节点

Tree23 tree = new Tree23();
tree.add(50);
tree.add(30);
tree.add(80);
tree.add(90);
tree.add(20);
tree.add(70);
tree.add(40);
tree.add(95);
tree.add(100);
System.out.println(tree);
tree.del(95);
System.out.println(tree);

打印如下

插入:50 插入:30 插入:80 插入:90 插入:20 插入:70 插入:40 插入:95 插入:100 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=95] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=0] [value1=100,value2=0] 
对元素中序遍历: 20 30 40 50 70 80 90 95 100 
----------------------------------
删除:95 
对节点层次遍历:[value1=50,value2=0] [value1=30,value2=0] [value1=80,value2=0] [value1=20,value2=0] [value1=40,value2=0] [value1=70,value2=0] [value1=90,value2=100] 
对元素中序遍历: 20 30 40 50 70 80 90 100 
----------------------------------
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值