二叉排序树(BinarySortTree)的添加,删除功能实现(java代码实现)

二叉排序树(BinarySortTree)的添加,删除功能实现(java代码)

介绍

二叉排序树:BST(Binary Sort Tree):要求该树的任何一个非叶子结点的左子结点的值小于当前结点的值,右子结点的值大于当前结点的值
注意:若是存在相同值的情况,则将该结点放在当前结点的左子结点或者右子结点都可以
删除结点的思路分析

分为三种情况:
第一种情况:删除的是叶子结点
第二种情况:删除的是只有一颗子树的结点
第三种情况:删除的是有两颗子树的结点
下面代码中会做详细的步骤分析

package com.bingym.temp01.binarysorttree;

public class BinarySortTreeDemo {
    /*
    * 二叉排序树:BST(Binary Sort Tree):要求该树的任何一个非叶子结点的左子结点的值小于当前结点的值,右子结点的值大于当前结点的值
    * 注意:若是存在相同值的情况,则将该结点放在当前结点的左子结点或者右子结点都可以
    * 下面进行二叉排序树的创建,结点添加和中序遍历代码的实现
    * */
    public static void main(String[] args) {
        int[] arr = {7,3,10,12,5,1,9,4};
        BinarySortTree binarySortTree = new BinarySortTree();
        //for循环添加结点
        for (int i = 0; i < arr.length; i++) {
            binarySortTree.add(new Node(arr[i]));
        }
        //中序遍历
        System.out.println("该二叉排序树的中序遍历为:");
        binarySortTree.infixOrder();

        //测试删除叶子结点
        //binarySortTree.delNode(9);
        //System.out.println("二叉树删除叶子结点9后的中序遍历为:");
        //binarySortTree.infixOrder();

        //测试删除只有一颗子树的结点
        //binarySortTree.delNode(5);
        //System.out.println("二叉树删除只有一颗子树的结点5后的中序遍历为:");
        //binarySortTree.infixOrder();

        //测试删除有两颗子树的结点
        binarySortTree.delNode(7);
        System.out.println("二叉树删除有两颗子树的结点7以后的中序遍历为:");
        binarySortTree.infixOrder();

    }

}

//定义二叉排序树
class BinarySortTree {
    //定义头结点
    private Node root;

    //构造方法:空参构造
    
    //结点的添加方法
    public void add(Node node) {
        if (this.root == null) {
            //根节点为空,则直接将该结点设置为根节点
            this.root =node;
        }else {
            this.root.add(node);//否则,利用root结点与添加结点的value值进行比较,完成结点的添加工作
        }
    }

    //中序遍历的方法
    public void infixOrder() {
        if (this.root != null) {
            //说明当前树不为空
            this.root.infixOrder();
        }else {
            System.out.println("二叉排序树为空...");
        }
    }

    //查找当前删除结点的方法
    public Node search(int value) {
        if (this.root == null) {
            //说明该树没有结点
            return null;
        } else {
            return this.root.search(value);
        }
    }

    //查找当前删除结点的父结点的方法
    public Node searchParent(int value) {
        if (this.root == null) {
            return null;
        }else {
            return this.root.searchParent(value);
        }
    }

    //根据传入的结点(以传入的结点为根节点),查找左子树最小结点的值,返回其值,并删除其最小结点
    //由于找到的最小结点必然为叶子结点,则可以调用delNode方法进行删除
    public int delMinNode(Node node) {
        Node tempNode = node;
        //通过while循环查找到左子树的最小结点
        while (tempNode.left != null) {
            tempNode = tempNode.left;
        }
        //退出while循环,tempNode即为左子树的最小结点
        //删除该最小结点
        delNode(tempNode.value);
        //返回最小结点的值
        return tempNode.value;
    }

    //根据传入的结点(以传入的结点为根节点),查找右子树最大结点的值,返回其值,并删除其最大结点
    //由于找到的最大结点必然为叶子结点,则可以调用delNode方法进行删除
    public int delMaxNode(Node node) {
        Node tempNode = node;
        //通过while循环查找到右子树的最小结点
        while (tempNode.right != null) {
            tempNode = tempNode.right;
        }
        //退出while循环,tempNode即为右子树的最大结点
        //删除该最大结点
        delNode(tempNode.value);
        //返回最小结点的值
        return tempNode.value;
    }

    //进行删除指定value值的结点的方法
    public void delNode(int value) {
        if (root == null) { //说明查找的树为空
            return;
        }else { //否则进行删除操作
            //第一步:找到要删除的结点targetNode
            Node targetNode = search(value);
            //若没有(没找到要删除的结点),则直接返回
            if (targetNode == null) {
                return;
            }
            //若该树只有一个结点root,并且root结点即为要删除的结点,则直接将root置空即可
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }
            //第二步:找到要删除结点的父结点
            Node parent = searchParent(value);
            //第三步:第一种情况:删除结点为叶子结点
            if(targetNode.left == null && targetNode.right == null) {//即targetNode的左子结点和右子结点均为null(叶子结点)
                //判断targetNode为parent的左子结点还是右子结点
                if (parent.left != null && parent.left.value == value) {
                    //说明targetNode为parent的左子结点
                    parent.left = null;
                }else if (parent.right != null && parent.right.value == value) {
                    //说明targetNode为parent的右子结点
                    parent.right = null;
                }
            }else if (targetNode.left != null && targetNode.right != null) {//第三步:第三种情况:删除结点为有两颗子树的结点
                //删除含有两颗子树的结点的结点
                //从targetNode的右子树中找到最小结点的值minValue:其中内部已经删除掉minValue对应的结点
                int minValue = delMinNode(targetNode.right);
                //将target的value修改为minValue
                targetNode.value = minValue;
                //第二种方式
                /*int maxValue = delMaxNode(targetNode.left);
                targetNode.value = maxValue;*/
            }else {//第三步:第二种情况:删除结点为只有一颗子树的结点
                //判断删除结点存在左子结点还是右子结点
                if (targetNode.left != null) {  //说明targetNode存在左子结点
                    if (parent.left.value == value) {//targetNode是parent的左子结点
                        parent.left = targetNode.left;
                    }else {//targetNode是parent的右子结点
                        parent.right = targetNode.left;
                    }
                }else { //说明targetNode存在右子结点
                    if (parent.left.value == value) {//targetNode是parent的左子结点
                        parent.left = targetNode.right;
                    }else {//targetNode是parent的右子结点
                        parent.right = targetNode.right;
                    }
                }
            }

        }
    }

}


//定义结点类

class Node {
    public int value;//结点的值
    public Node left;
    public Node right;

    public Node(int value) {
        this.value = value;
    }


    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    //中序遍历的方法
    public void infixOrder() {
        //左子树递归遍历
        if (this.left != null) {
            this.left.infixOrder();
        }
        //输出当前结点
        System.out.println(this);
        //右子树递归遍历
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    //结点的添加方法
    public void add(Node node) {
        if (node == null) {
            //首先判断添加的结点是否为空,若为空,直接返回
            return;
        }
        if (node.value < this.value) {//如果传入结点的value值小于当前根节点的value值
            if (this.left == null) {//若当前根节点的左子树为空
                this.left = node;
            }else {
                this.left.add(node);//递归地向左子树添加
            }
        }else {//如果传入结点的value值大于(或者等于)当前根节点的value值
            if (this.right == null) {
                this.right = node;
            }else {
                this.right.add(node);//递归地向右子树添加
            }
        }
    }

    //根据value值查找当前要删除的结点
    //value:表示查找的结点的value值,return:若找到,则返回,若没找到,则返回null
    public Node search(int value) {
        //首先判断当前结点是否为要删除的结点
        if (this.value == value) {
            //说明当前结点即为要删除的结点
            return this;
        }

        if (this.value > value) {//查找的value值小于当前结点的value值,则向左递归查找
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        }else {//查找的value值大于或者等于当前结点的value值(尽可能保证二叉排序树的结点值都不相同),则向右递归查找
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }

    //根据value值查找当前要删除结点的父节点
    //value:表示查找的结点的value值,return:若找到,则返回,若没找到,则返回null
    public Node searchParent(int value) {
        //如果当前结点就是所要删除结点的父节点,则直接返回其结点
        if ((this.left != null &&this.left.value == value) || (this.right != null && this.right.value ==value)) {
            //直接返回当前结点,即为删除结点的父结点
            return this;
        }else {
            if (value < this.value && this.left != null) {
                //删除结点值小于当前结点值,并且当前结点左子树不为空,则递归左子树进行查询
                return this.left.searchParent(value);
            }else if (value >= this.value && this.right != null) {
                //删除结点值大于或者等于当前结点值,并且当前结点右子树不为空,则递归右子树进行查询
                return this.right.searchParent(value);
            }else {
                //如果都不满足,则说明找不到该结点的父节点(例如是根节点),则直接返回null
                return null;
            }
        }

    }
}

注意:上面的代码存在bug:我们在删除结点的过程,加入我们删除到最后剩下10和1,并且10此时为root结点:
在这里插入图片描述
此时我们再删除10这个只含有一个子树的结点时:由于此时10已经成为root,结点,即10的父节点为null,所以我们再delNode(int value)的只含有一颗子树的代码中,需要对targetNode的父节点进行判断,防止出现零指针异常:

//判断删除结点存在左子结点还是右子结点
                if (targetNode.left != null) {  //说明targetNode存在左子结点
                    //注意:需要判断此时parent是否为空的情况:有可能通过我们的删除操作最后此时删除的结点变成了root结点
                    //则此时root的parent结点为空
                    //这里判断root是否为空:若为空,直接将root指向此时targetNode存在的左子结点
                    if (parent != null) {
                        if (parent.left.value == value) {//targetNode是parent的左子结点
                            parent.left = targetNode.left;
                        }else {//targetNode是parent的右子结点
                            parent.right = targetNode.left;
                        }
                    }else{
                        //如果为空,则直接将root指向此时存在的左子结点即可
                        root = targetNode.left;
                    }
                }else { //说明targetNode存在右子结点
                	//这里同上:对parent进行判断
                    if (parent != null) {
                        if (parent.left.value == value) {	//targetNode是parent的左子结点
                            parent.left = targetNode.right;
                        }else {//targetNode是parent的右子结点
                            parent.right = targetNode.right;
                        }
                    }else {
                        //同上:将此时的root结点指向此时存在的右子结点即可
                        root = targetNode.right;
                    }

                }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值