二叉搜索树的插入,删除,遍历

299 篇文章 1 订阅
227 篇文章 1 订阅

节点类:

package Demo31;

public class TreeNode {
    private TreeNode left;
    private Integer data;
    private TreeNode right;

    @Override
    public String toString() {
        return "TreeNode{" +
                "left=" + left +
                ", data=" + data +
                ", right=" + right +
                '}';
    }

    public TreeNode(Integer data) {
        this.data = data;
    }

    public TreeNode getLeft() {
        return left;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public Integer getData() {
        return data;
    }

    public void setData(Integer data) {
        this.data = data;
    }

    public TreeNode getRight() {
        return right;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }
}

构建二叉搜索树:

package Demo31;

public class BinTree {
    TreeNode root;

    public TreeNode getRoot() {
    // 获取根节点
        return root;
    }

    public void Insert(TreeNode temp, int val) {
    //插入节点,二叉搜索树的左子树都小于根节点,右子树都大于等于根节点
    //本代码没有考虑出现重复节点的情况
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            return;
        }
        //如果发现,要插入的节点小于当前节点
        if (node.getData() < temp.getData()) {
        //如果当前节点的左子树不存在,直接设置左子树为要插入的节点
            if (temp.getLeft() == null) {
                temp.setLeft(node);
            } else {// 否则,让当前节点往左子树走
                Insert(temp.getLeft(), val);
            }
        } else {//反之,一样
            if (temp.getRight() == null) {
                temp.setRight(node);
            } else {
                Insert(temp.getRight(), val);
            }
        }
    }

    public TreeNode search(int val) {
    // 在二叉树中搜索值为 val 的节点并返回
        if (root == null) {
            return null;
        }
        TreeNode t = root;
        while (true) {
            if (val < t.getData()) {//如果 val 小于当前节点 t 的值,让 t 往左走
                if (t.getLeft() == null) {
                    return null;
                } else {
                    t = t.getLeft();
                }
            } else if (val > t.getData()) {
                if (t.getRight() == null) {
                    return null;
                } else {
                    t = t.getRight();
                }
            } else {
                return t;
            }
        }
    }

    public TreeNode searchParentNode(TreeNode node, int val) {
    // 搜素值为 val 的节点的父亲节点
    //此方法在删除节点中用
        if (root == null) {
            return null;
        }
        // 如果发现当前节点的左子树或者右子树的值等于 val,说明当前节点就是父亲节点
        if ((node.getLeft() != null && node.getLeft().getData() == val) || (node.getRight() != null && node.getRight().getData() == val)) {
            return node;
        }
        // 如果当前节点的值大于要找的 val,让当前节点往左走
        if (val < node.getData() && node.getLeft != null) {
            return searchParentNode(node.getLeft(), val);
        } else if (val > node.getData() && node.getRight != null){
            return searchParentNode(node.getRight(), val);
        } else return null;//不满足上述情况,就说明该节点没有父亲节点,说明该节点是个根节点

    }
    public int deleteRightTreeNode(TreeNode node) {
    // 找到右子树的最小值,在删除有两个子节点的节点时使用,画图可分析
        while (node.getLeft() != null) {//由于传过来的就是右子树
        //所以只需要不断往左找就行,找到最小的
            node = node.getLeft();
        }
        delete(node.getData());// 别忘了将该节点删除
        return node.getData();
    }

    public void delete(int val) {
    // 删除节点,考虑的情况比较多,不过不难,仔细就 okk
        if (root == null) return;// 根节点为空直接返回
        TreeNode node = search(val);//找到要删除的节点
        TreeNode parNode = searchParentNode(getRoot(), val);//找到要删除的节点的父亲节点(有可能是空,下面会判断)
        if (node == null) return;//没找到要删除的节点,直接返回
        if (node.getLeft() == null && node.getRight() == null) {
        // 说明此节点是一个叶子结点,我们只需要找到该节点是父节点的左子树还是右子树
        //如果该节点也是根节点,直接让根节点等于 null
        if (parNode == null) {
                root = null;
                return;
            }
            if (parNode.getLeft() != null && parNode.getLeft().getData() == val) {//是左子树
                parNode.setLeft(null);
            } else {
                parNode.setRight(null);
            }
        } else if (node.getLeft() != null && node.getRight() != null) {
        //要删除的节点有左右子树,只需要要让该节点的值等于右子树的最小值,并且删除右子树的最小值
        	// 定义最小值,找到
            int minRightTreeNode = deleteRightTreeNode(node.getRight());
            // 让该节点的值等于最小值
            node.setData(minRightTreeNode);
        } else { // 最复杂的情况,该节点只有一个子树,并且不知道是哪个
            if (parNode != null) {// 如果该节点不是跟节点
                if (node.getLeft() != null) {// 判断该节点有左子树还是右子树
                    if (parNode.getLeft() != null && parNode.getLeft().getData() == val) {// 判断该节点是父亲节点的左子树还是右子树
                    //这里一定要仔细看代码,画个图,比较绕。
                        parNode.setLeft(node.getLeft());
                    } else {
                        parNode.setRight(node.getLeft());
                    }
                } else {
                    if (parNode.getLeft() != null && parNode.getLeft().getData() == val) {
                        parNode.setLeft(node.getRight());
                    } else {
                        parNode.setRight(node.getRight());
                    }
                }
            } else {// 该节点是根节点,找到该节点有左子树还是右子树,让根节点等于即可
                if (node.getLeft() != null) {
                    root = node.getLeft();
                } else {
                    root = node.getRight();
                }
            }
        }
    }

    public void preSearch(TreeNode tree) {
    //前序遍历,不多说了
        if (tree != null) {
            System.out.println(tree.getData());
            preSearch(tree.getLeft());
            preSearch(tree.getRight());
        }
    }
}

这里我画个图解释一下删除的节点只有一个子树的情况:
在这里插入图片描述
删除节点 3,3 是有左子树的,并且 3 是父亲节点的左子树,所以让父亲节点的左子树指向 3 的左子树
在这里插入图片描述
此时:3 是有右子树的,3 是父亲节点的左孩子,只需让父亲节点的左孩子指向 3 的右孩子
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_努力努力再努力_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值