037.二叉树的结点查找和结点删除


博主的 Github 地址


1. 二叉树查找指定结点

例题要求

  1. 编写前序查找, 中序查找和后序查找的方法
  2. 分别使用三种查找方式, 查找指定的目标结点
  3. 分析各种查找方式分别比较了多少次才找到目标

1.1. 二叉树查找的分类和思路

1.1.1. 前序查找
  • 先判断当前结点的 no 是否与目标相等, 若相等返回当前结点
    • 如果不相等
      • 先判断当前结点的左子结点是否为空, 非空则递归前序查找
      • 再判断当前结点的右子结点是否为空, 非空则递归前序查找
  • 递归过程中如果匹配正确则输出, 都不匹配则最终返回 null
1.1.2. 中序查找
  • 先判断当前结点的左子结点是否为空, 非空则递归中序查找
  • 然后判断当前结点的 no 是否与目标相等,
    相等就返回, 不相等就进行下一步判断
  • 再判断当前结点的右子结点是否为空, 非空则递归中序查找
  • 递归过程中如果匹配正确则输出, 都不匹配则最终返回 null
1.1.3. 后序查找
  • 先判断当前结点的左子结点是否为空, 非空则递归后续查找
  • 再判断当前结点的右子结点是否为空, 非空则递归后续查找
  • 再判断当前结点的 no 是否与目标相等, 相等就返回
  • 递归过程中如果匹配正确则输出, 都不匹配则最终返回 null

1.2. 二叉树三种查找的代码实现

  • 接着使用上一篇的类来进行增添相关方法
1.2.1. 结点类新增三个查找方法
  • 每个方法前定义了计数器用来统计比较次数, 若不需要计数器直接删除相关内容即可
    //计数器
    public static int preCount = 0;
    //前序查找方法
    public TreeNode preSearch(int target_no){
        //创建一个临时变量用以接收目标结点
        TreeNode target = null;
        //在比较前计数器加一
        preCount++;
        System.out.printf("the preCount is [%d]\n", preCount);
        //先判断当前结点的 no 是否和目标 no 相等
        if (this.person_no == target_no){
            target = this;
        }
        //不相等, 则进入下面的判断
        else{
            //如果当前结点的左子结点不为空, 则递归前序查找
            if(this.left_node != null){
                target = this.left_node.preSearch(target_no);
            }
            //如果当前结点的右子结点不为空, 则递归前序查找
            //target 非空说明找到目标, 直接跳过即可
            if (target == null && this.right_node != null){
                target = this.right_node.preSearch(target_no);
            }
        }
        //如果当前结点的左右结点都不符合或者没有左右子结点, 则返回默认值 null
        return target;
    }

    //计数器
    public static int infixCount = 0;
    //中序查找方法
    public TreeNode infixSearch(int target_no){
        //创建一个临时变量用以接收目标结点
        TreeNode target = null;

        //如果当前结点的左子结点不为空, 则递归中序查找
        if (this.left_node != null){
            target = this.left_node.infixSearch(target_no);
        }

        //在比较前计数器加一
        infixCount++;
        System.out.printf("the infixCount is [%d]\n", infixCount);
        //判断当前结点的 no 是否和目标 no 相等
        //target 非空说明找到目标, 直接跳过即可
        if(target == null && this.person_no == target_no){
            target = this;
        }

        //如果当前结点的右子结点不为空, 则递归中序查找
        //target 非空说明找到目标, 直接跳过即可
        if (target == null && this.right_node != null){
            target = this.right_node.infixSearch(target_no);
        }
        return target;
    }

    //计数器
    public static int postCount = 0;
    //后序查找方法
    public TreeNode postSearch(int target_no){
        //创建一个临时变量用以接收目标结点
        TreeNode target = null;

        //如果当前结点的左子结点不为空, 则递归中序查找
        if (this.left_node != null){
            target = this.left_node.postSearch(target_no);
        }

        //如果当前结点的右子结点不为空, 则递归中序查找
        //target 非空说明找到目标, 直接跳过即可
        if (target == null && this.right_node != null){
            target = this.right_node.postSearch(target_no);
        }

        //在比较前计数器加一
        postCount++;
        System.out.printf("the postCount is [%d]\n", postCount);
        //判断当前结点的 no 是否和目标 no 相等
        //target 非空说明找到目标, 直接跳过即可
        if(target == null && this.person_no == target_no){
            target = this;
        }

        return target;
    }
1.2.2. 二叉树类新增三个查找调用方法
  • 新增三个查找调用方法, 实际只是以根结点调用查找方法
    //二叉树前序查找
    public void preSearch(int target_no){
        if(this.root_node != null){
            TreeNode target = this.root_node.preSearch(target_no);
            if (target != null){
                System.out.printf("The target[no=%d] found which is %s\n", target_no, target.toString());
            }
            else {
                System.out.printf("Can't found the target[no=%d]\n", target_no);
            }
        }
    }

    //二叉树中序查找
    public void infixSearch(int target_no){
        if(this.root_node != null){
            TreeNode target = this.root_node.infixSearch(target_no);
            if (target != null){
                System.out.printf("The target[no=%d] found which is %s\n", target_no, target.toString());
            }
            else {
                System.out.printf("Can't found the target[no=%d]\n", target_no);
            }
        }
    }

    //二叉树后序查找
    public void postSearch(int target_no){
        if(this.root_node != null){
            TreeNode target = this.root_node.postSearch(target_no);
            if (target != null){
                System.out.printf("The target[no=%d] found which is %s\n", target_no, target.toString());
            }
            else {
                System.out.printf("Can't found the target[no=%d]\n", target_no);
            }
        }
    }
1.2.3. 测试类新增
  • 本次测试测试结点编号为 3 的结点
        //测试前序查找
        System.out.println("========test preSearch=======");
        testTree.preSearch(3);

        //测试中序查找
        System.out.println("========test infixSearch=======");
        testTree.infixSearch(3);

        //测试后续查找
        System.out.println("========test postSearch=======");
        testTree.postSearch(3);

1.3. 二叉树查找测试

  • 用的是如下二叉树
    示例二叉树
1.3.1. 测试结点 3
  • 前序查找的结果

    • 一共对比了 5 次
      preSearch
  • 中序查找的结果

    • 一共对比了 6 次
      infixSearch
  • 后序查找的结果

    • 一共对比了 7 次
      postSearch
1.3.2. 测试结点 4
  • 结果是比较次数都一样
    res

2. 二叉树结点的删除

例题要求

  1. 如果删除的结点是叶子结点, 则删除该结点
  2. 如果删除的结点是非叶子结点, 则删除该子树
    (在二叉树没有规则前, 删除非叶子结点对其子结点的操作
    并没太大意义, 因此直接删除子树即可)

2.1. 二叉树删除结点的操作思路

  1. 先判断根结点是否为空树, 如果只有一个根结点, 则二叉树是空的.

  2. 因为二叉树是单向的, 所以有如下结论:

    • 子结点是不能回溯回去父结点的,
      因此就要判断当前结点的子结点是否是待删除结点,
      而不能去判断当前结点是否为待删除结点.

    • 结点删除是父结点断开对子结点的引用,
      删除操作必须是要在父结点完成.

  3. 如果当前结点的左子结点不为空且是要删除的结点,
    直接将其左子结点置空为 null 即可, 并返回结束递归.
    否则进行下一步判断.

  4. 如果当前结点的右子结点不为空且是要删除的结点,
    直接将其右子结点置空为 null 即可, 并返回结束递归.
    否则进行下一步判断.

  5. 如果步骤 2 和步骤 3 都没有删除结点, 则向左子树进行递归删除

  6. 如果步骤 4 没有删除结点, 则向右子树进行递归删除

  7. 如果步骤 5 没有删除结点, 则结点并不存在


2.2. 二叉树结点删除代码实现

  • 接着使用上一篇的类来进行增添相关方法
2.2.1. 结点类新增
  • 根结点的非空判断交由二叉树类调用时实现
  • 结点类主要对根结点后的子结点进行判断
    //删除结点方法
    public boolean delNode(int target_no){
        //如果当前结点的左子结点不为空且是要删除的结点,
        //直接将其左子结点置空为 null 即可, 并返回结束递归.
        if(this.left_node != null && this.left_node.person_no == target_no){
            this.left_node = null;
            return true;
        }

        //如果当前结点的右子结点不为空且是要删除的结点,
        //直接将其右子结点置空为 null 即可, 并返回结束递归.
        if(this.right_node != null && this.right_node.person_no == target_no){
            this.right_node = null;
            return true;
        }

        boolean res = false;
        //上述步骤都没有删除结点, 则向左子树进行递归删除
        if(this.left_node != null){
            res = this.left_node.delNode(target_no);
            if(res){
                return res;
            }
        }

        //向左递归没有删除结点, 则向右子树进行递归删除
        if(this.right_node != null){
            res = this.right_node.delNode(target_no);
            if(res){
                return res;
            }
        }

        //如果都没有删除结点, 则结点并不存在, 删除失败
        return false;
    }
2.2.2. 二叉树类新增
  • 二叉树类调用结点类删除结点时要先对二叉树根结点进行非空判断
    //二叉树删除结点
    public void delNode(int target_no){
        //先判断根结点是否为空树, 如果只有一个根结点, 则二叉树是空的.
        if(root_node != null){
            //再判断根结点是否为目标删除结点
            if(root_node.getPerson_no() == target_no){
                //如果是, 则置空当前二叉树即可
                root_node = null;
            }
            else{
                boolean res = root_node.delNode(target_no);
                if(res){
                    System.out.printf("deleted the node[no=%d]!\n", target_no);
                }
                else {
                    System.out.printf("failed to delete the node[no=%d]!\n", target_no);
                }
            }
        }
        else {
            System.out.println("the root is null!");
        }
    }
2.2.3. 测试类新增
  • 删除结点前后遍历一次
        //测试前序遍历
        System.out.println("========test preOrder=======");
        testTree.preOrder();
        //测试删除结点
        System.out.println("========test delNode=======");
        testTree.delNode(8);//2
        //测试前序遍历
        System.out.println("========test preOrder=======");
        testTree.preOrder();

2.3. 二叉树删除结点测试结果

  • 用的是如下二叉树
    示例二叉树
2.3.1. 删除结点编号为 2 的非叶子结点
  • 显而易见包含结点 B 在内的子树 B - D - E 三个结点都被删除
    node2
2.3.2. 删除结点编号为 4 的叶子结点
  • 显而易见, 只有结点 D 被删除
    node4
2.3.3. 删除结点编号为 8 的不存在结点
  • 节点不存在, 则提示删除失败
    node8
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值