【数据结构(九)】二叉树基础(1)

文章目录

  • [1. 数组、链表、树的存储方式分析](#1. 数组、链表、树的存储方式分析)
    • [1.1. 数组的存储方式](#1.1. 数组的存储方式)
    • [1.2. 链式存储方式](#1.2. 链式存储方式)
    • [1.3. 树的存储方式](#1.3. 树的存储方式)
  • [2. 树的相关概念](#2. 树的相关概念)
    • [2.1. 树的常用术语](#2.1. 树的常用术语)
    • [2.2. 二叉树的概念](#2.2. 二叉树的概念)
  • [3. 二叉树遍历](#3. 二叉树遍历)
    • [3.1. 思路分析](#3.1. 思路分析)
    • [3.1.1. 代码实现](#3.1.1. 代码实现)
    • [3.2. 问题拓展](#3.2. 问题拓展)
  • [4. 二叉树查找指定节点](#4. 二叉树查找指定节点)
    • [4.1. 思路分析](#4.1. 思路分析)
    • [4.2. 代码实现](#4.2. 代码实现)
  • [5. 二叉树删除指定节点](#5. 二叉树删除指定节点)
    • [5.1. 思路分析](#5.1. 思路分析)
    • [5.2. 代码实现](#5.2. 代码实现)

1. 数组、链表、树的存储方式分析

为什么要需要树这种数据结构?

先分析一下数组、链表的存储方式

1.1. 数组的存储方式

优点:通过下标方式访问元素,速度快。对于有序数组,还可使用二分查找提高检索速度。

缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较低 [示意图]

操作示意图:

1.2. 链式存储方式

优点:在一定程度上对数组存储方式有优化(比如:插入一个数值节点,只需要将插入节点,链接到链表中即可,删除效率也很好)。

缺点:在进行检索时,效率仍然较低,比如(检索某个值,需要从头节点开始遍历) 【示意图】

操作示意图:

1.3. 树的存储方式

能提高数据存储,读取的效率, 比如利用 二叉排序树(Binary Sort Tree),既可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度。【示意图,后面详讲】

操作示意图:

案例: [7, 3, 10, 1, 5, 9, 12]

注意:树的排列顺序,左边节点比中间小,右边节点比中间大

2. 树的相关概念

2.1. 树的常用术语

树的常用术语(结合示意图理解):

  1. 节点
  2. 根节点
  3. 父节点
  4. 子节点
  5. 叶子节点 (没有子节点的节点)
  6. 节点的权(节点值)
  7. 路径(从 root 节点找到该节点的路线)
  8. 子树
  9. 树的高度(最大层数)
  10. 森林 :多颗子树构成森林

2.2. 二叉树的概念

  1. 树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树。
  2. 二叉树的子节点分为左节点和右节点

示意图:

  1. 如果该二叉树的所有叶子节点都在最后一层,并且 节点总数 = 2 n − 1 节点总数= 2^n -1 节点总数=2n−1 , n n n 为层数,则我们称为满二叉树。
  1. 如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续 ,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。

3. 二叉树遍历

使用前序中序后序对下面的二叉树进行遍历.

  1. 前序遍历: 先输出父节点,再遍历左子树和右子树
  2. 中序遍历: 先遍历左子树,再输出父节点,再遍历右子树
  3. 后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点

小结: 看输出父节点的顺序,就确定是前序,中序还是后序

3.1. 思路分析

应用实例的说明和思路:


3.1.1. 代码实现

java 复制代码
package tree;

import java.util.Objects;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        // 创建一棵二叉树
        BinaryTree binaryTree = new BinaryTree();
        // 创建需要的节点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");

        // 说明:我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        binaryTree.setRoot(root);

        // 测试前序遍历
        System.out.println("前序遍历");// 1,2,3,4
        binaryTree.preOrder();

        // 测试中序遍历
        System.out.println("中序遍历");// 2,1,3,4
        binaryTree.infixOrder();

        // 测试后序遍历
        System.out.println("后序遍历");// 2,4,3,1
        binaryTree.postOrder();

    }

}

// 定义一个BinaryTree二叉树
class BinaryTree {
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    // 前序遍历
    public void preOrder() {
        if (this.root != null) {
            this.root.preOder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 中序遍历
    public void infixOrder() {
        if (this.root != null) {
            this.root.infixOrde();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 后序遍历
    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
}

// 先创建HeroNode节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left;// 默认null
    private HeroNode right;// 默认null

    public HeroNode(int no, String name) {

        super();
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return this.no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return this.left;
    }

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

    public HeroNode getRight() {
        return this.right;
    }

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

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }

    // 编写前序遍历的方法
    public void preOder() {
        System.out.println(this);// 先输出父节点
        // 递归向左子树前序遍历
        if (this.left != null) {
            this.left.preOder();
        }
        // 递归向右子树前序遍历
        if (this.right != null) {
            this.right.preOder();
        }
    }

    // 编写中序遍历的方法
    public void infixOrde() {
        // 递归向左子树中序遍历
        if (this.left != null) {
            this.left.infixOrde();
        }

        // 输出父节点
        System.out.println(this);

        // 递归向右子树中序遍历
        if (this.right != null) {
            this.right.infixOrde();
        }
    }

    // 编写后序遍历的方法
    public void postOrder() {
        if (this.left != null) {
            this.left.postOrder();
        }

        if (this.right != null) {
            this.right.postOrder();
        }

        System.out.println(this);
    }

}

运行结果:


3.2. 问题拓展

前上图的 3号节点 "卢俊" , 增加一个左子节点 [5, 关胜]

使用前序,中序,后序遍历,请写出各自输出的顺序是什么?

代码实现:

java 复制代码
package tree;

import java.util.Objects;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        // 创建一棵二叉树
        BinaryTree binaryTree = new BinaryTree();
        // 创建需要的节点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");

        // 说明:我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        // 测试前序遍历
        System.out.println("前序遍历");// 1,2,3,4
        binaryTree.preOrder();

        // 测试中序遍历
        System.out.println("中序遍历");// 2,1,3,4
        binaryTree.infixOrder();

        // 测试后序遍历
        System.out.println("后序遍历");// 2,4,3,1
        binaryTree.postOrder();

    }

}

// 定义一个BinaryTree二叉树
class BinaryTree {
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    // 前序遍历
    public void preOrder() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 中序遍历
    public void infixOrder() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 后序遍历
    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
}

// 先创建HeroNode节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left;// 默认null
    private HeroNode right;// 默认null

    public HeroNode(int no, String name) {

        super();
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return this.no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return this.left;
    }

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

    public HeroNode getRight() {
        return this.right;
    }

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

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }

    // 编写前序遍历的方法
    public void preOrder() {
        System.out.println(this);// 先输出父节点
        // 递归向左子树前序遍历
        if (this.left != null) {
            this.left.preOrder();
        }
        // 递归向右子树前序遍历
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    // 编写中序遍历的方法
    public void infixOrder() {
        // 递归向左子树中序遍历
        if (this.left != null) {
            this.left.infixOrder();
        }

        // 输出父节点
        System.out.println(this);

        // 递归向右子树中序遍历
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    // 编写后序遍历的方法
    public void postOrder() {
        if (this.left != null) {
            this.left.postOrder();
        }

        if (this.right != null) {
            this.right.postOrder();
        }

        System.out.println(this);
    }

}

运行结果:

4. 二叉树查找指定节点

要求:

①请编写前序查找,中序查找和后序查找的方法。

②并分别使用三种查找方式,查找 heroNO = 5 的节点

③并分析各种查找方式,分别比较了多少次

4.1. 思路分析

思路分析图解:

使用前序,中序,后序的方式来查询指定的结点

前序查找思路:

1.先判断当前节点的no是否等于要查找的

2.如果是相等,则返回当前节点

3.如果不等,则判断当前节点的左子节点是否为空,如果不为空,则递归前序查找

4.如果左递归前序查找,找到节点,则返回,否继续判断,当前的节点的右子节点是否为空,如果不空,则继续向右递归前序查找.

中序查找思路:

1.判断当前节点的左子节点是否为空,如果不为空,则递归中序查找

2.如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前节点,否则继续进行右递归的中序查找

3.如果右递归中序查找,找到就返回,否则返回null

后序查找思路:

1判断当前节点的左子节点是否为空,如果不为空,则递归后序查找

2.如果找到,就返回,如果没有找到,就判断当前节点的右子节点是否为空,如果不为空,则右递归进行后序查找,如果找到,就返回

3.就和当前节点进行比较,比如,如果是则返回,否则返回null

4.2. 代码实现

java 复制代码
package tree;

import java.util.Objects;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        // 创建一棵二叉树
        BinaryTree binaryTree = new BinaryTree();
        // 创建需要的节点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");

        // 说明:我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        // 测试前序遍历
        System.out.println("前序遍历");// 1,2,3,4
        binaryTree.preOrder();

        // 测试中序遍历
        System.out.println("中序遍历");// 2,1,3,4
        binaryTree.infixOrder();

        // 测试后序遍历
        System.out.println("后序遍历");// 2,4,3,1
        binaryTree.postOrder();

        // 前序遍历:4次
        // System.out.println("前序遍历方式");
        // HeroNode resNode = binaryTree.preOrderSearch(5);
        // if (resNode != null) {
        // System.out.printf("找到了,信息为 no=%d name=%s", resNode.getNo(),
        // resNode.getName());
        // }else{
        // System.out.printf("没有找到 no=%d 的英雄", 5);
        // }

        // 中序遍历:3次
        // System.out.println("中序遍历方式");
        // HeroNode resNode = binaryTree.infixOrderSearch(5);
        // if (resNode != null) {
        // System.out.printf("找到了,信息为 no=%d name=%s", resNode.getNo(),
        // resNode.getName());
        // }else{
        // System.out.printf("没有找到 no=%d 的英雄", 5);
        // }

        // 后序遍历:2次
        System.out.println("后序遍历方式");
        HeroNode resNode = binaryTree.postOrderSearch(5);
        if (resNode != null) {
            System.out.printf("找到了,信息为 no=%d name=%s", resNode.getNo(), resNode.getName());
        } else {
            System.out.printf("没有找到 no=%d 的英雄", 5);
        }

    }

}

// 定义一个BinaryTree二叉树
class BinaryTree {
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    // 前序遍历
    public void preOrder() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 中序遍历
    public void infixOrder() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 后序遍历
    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            return root.preOrderSearch(no);
        } else {
            return null;
        }
    }

    // 中序遍历
    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            return root.infixOrderSearch(no);
        } else {
            return null;
        }
    }

    // 后序遍历
    public HeroNode postOrderSearch(int no) {
        if (root != null) {
            return this.root.postOrderSearch(no);
        } else {
            return null;
        }
    }
}

// 先创建HeroNode节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left;// 默认null
    private HeroNode right;// 默认null

    public HeroNode(int no, String name) {

        super();
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return this.no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return this.left;
    }

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

    public HeroNode getRight() {
        return this.right;
    }

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

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }

    // 编写前序遍历的方法
    public void preOrder() {
        System.out.println(this);// 先输出父节点
        // 递归向左子树前序遍历
        if (this.left != null) {
            this.left.preOrder();
        }
        // 递归向右子树前序遍历
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    // 编写中序遍历的方法
    public void infixOrder() {
        // 递归向左子树中序遍历
        if (this.left != null) {
            this.left.infixOrder();
        }

        // 输出父节点
        System.out.println(this);

        // 递归向右子树中序遍历
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    // 编写后序遍历的方法
    public void postOrder() {
        if (this.left != null) {
            this.left.postOrder();
        }

        if (this.right != null) {
            this.right.postOrder();
        }

        System.out.println(this);
    }

    // 前序遍历查找
    /**
     * 
     * @param no 查找的编号
     * @return 如果找到就返回该Node,如果没有找到就返回null
     */
    public HeroNode preOrderSearch(int no) {
        System.out.println("进入了前序查找一次~~");
        // 比较当前节点是不是
        if (this.no == no) {
            return this;
        }

        // 1. 判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
        // 2. 如果左递归前序查找,找到节点,则返回
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }

        if (resNode != null) {// 说明我们左子树找到
            return resNode;
        }

        // 1. 左递归前序查找,找到节点,则返回,否继续判断,
        // 2.当前的节点的右子节点是否为空,如果不空,则继续向右递归前序查找
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }

    // 中序遍历查找
    public HeroNode infixOrderSearch(int no) {

        // 判断当前节点的左子节点是否为空,如果不为空,则递归中序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }

        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入了中序查找一次~~");
        // 如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前节点
        if (this.no == no) {
            return this;
        }

        // 否则继续进行右递归的中序查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }

        return resNode;
    }

    // 后序遍历查找
    public HeroNode postOrderSearch(int no) {
        // 判断当前节点的左节点是否为空,如果不为空,则递归后序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.postOrderSearch(no);
        }
        if (resNode != null) {// 说明在左子树找到
            return resNode;
        }

        // 如果左子树没有找到,则向右子树递归进行后序遍历查找
        if (this.right != null) {
            resNode = this.right.postOrderSearch(no);
        }

        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入了后序查找一次~~");
        // 如果左右子树都没有找到,就比较当前节点是不是
        if (this.no == no) {
            return this;
        }
        return resNode;
    }

}

运行结果:

5. 二叉树删除指定节点

要求:

①如果删除的节点是叶子节点,则删除该节点

②如果删除的节点是非叶子节点,则删除该子树

实现,删除掉 5 号叶子节点 和 3 号子树.

5.1. 思路分析

思路 :

首先考虑:如果只有一个root结点,则等价将二叉树置空

// 然后进行下面步骤

1.因为我们的二叉树是单向的 ,所以我们是判断当前节点的子节点 是否需要删除结点,而不能去判断当前这个节点是不是需要删除节点。

2.如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null,并且就返回(结束递归删除)

3.如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)

4.如果第2和第3步没有删除节点,那么就需要向左子树进行递归删除

5.如果第4步也没有删除节点,则应当向右子树进行递归删除

5.2. 代码实现

java 复制代码
package tree;

import java.util.Objects;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        // 创建一棵二叉树
        BinaryTree binaryTree = new BinaryTree();
        // 创建需要的节点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");

        // 说明:我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        // 删除节点
        System.out.println("删除前,前序遍历");
        binaryTree.preOrder();// 1,2,3,5,4
         binaryTree.delNode(5);
//        binaryTree.delNode(3);

        System.out.println("删除后,前序遍历");
        binaryTree.preOrder();// 1,2,3,4

    }

}

// 定义一个BinaryTree二叉树
class BinaryTree {
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    // 删除节点
    public void delNode(int no) {
        if (root != null) {
            // 如果只有一个root节点,这里立即判断root是不是就是要删除的节点
            if (root.getNo() == no) {
                root = null;
            } else {
                // 递归删除
                root.delNode(no);
            }
        } else {
            System.out.println("空数,不能删除~");
        }
    }

    // 前序遍历
    public void preOrder() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 中序遍历
    public void infixOrder() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 后序遍历
    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    // 前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            return root.preOrderSearch(no);
        } else {
            return null;
        }
    }

    // 中序遍历
    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            return root.infixOrderSearch(no);
        } else {
            return null;
        }
    }

    // 后序遍历
    public HeroNode postOrderSearch(int no) {
        if (root != null) {
            return this.root.postOrderSearch(no);
        } else {
            return null;
        }
    }
}

// 先创建HeroNode节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left;// 默认null
    private HeroNode right;// 默认null

    public HeroNode(int no, String name) {

        super();
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return this.no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return this.left;
    }

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

    public HeroNode getRight() {
        return this.right;
    }

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

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }

    // 递归删除节点
    // 1. 如果删除的节点是叶子节点,则删除该节点
    // 2. 如果删除的节点是非叶子节点,则删除该子树
    public void delNode(int no) {
        // 思路
        /*
         * 1.因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否需要删除结点,而不能去判断当前这个节点是不是需要删除节点。
         * 2.如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null,并且就返回(结束递归删除)
         * 3.如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
         * 4.如果第2和第3步没有删除节点,那么就需要向左子树进行递归删除
         * 5.如果第4步也没有删除节点,则应当向右子树进行递归删除
         */
        // 2.如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null,并且就返回(结束递归删除)
        if (this.left != null && this.left.no == no) {
            this.left = null;
            return;
        }
        // 3.如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
        if (this.right != null && this.right.no == no) {
            this.right = null;
            return;
        }
        // 4.如果第2和第3步没有删除节点,那么就需要向左子树进行递归删除
        if (this.left != null) {
            this.left.delNode(no);
        }

        // 5.如果第4步也没有删除节点,则应当向右子树进行递归删除
        if (this.right != null) {
            this.right.delNode(no);
        }

    }

    // 编写前序遍历的方法
    public void preOrder() {
        System.out.println(this);// 先输出父节点
        // 递归向左子树前序遍历
        if (this.left != null) {
            this.left.preOrder();
        }
        // 递归向右子树前序遍历
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    // 编写中序遍历的方法
    public void infixOrder() {
        // 递归向左子树中序遍历
        if (this.left != null) {
            this.left.infixOrder();
        }

        // 输出父节点
        System.out.println(this);

        // 递归向右子树中序遍历
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    // 编写后序遍历的方法
    public void postOrder() {
        if (this.left != null) {
            this.left.postOrder();
        }

        if (this.right != null) {
            this.right.postOrder();
        }

        System.out.println(this);
    }

    // 前序遍历查找
    /**
     * 
     * @param no 查找的编号
     * @return 如果找到就返回该Node,如果没有找到就返回null
     */
    public HeroNode preOrderSearch(int no) {
        System.out.println("进入了前序查找一次~~");
        // 比较当前节点是不是
        if (this.no == no) {
            return this;
        }

        // 1. 判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
        // 2. 如果左递归前序查找,找到节点,则返回
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }

        if (resNode != null) {// 说明我们左子树找到
            return resNode;
        }

        // 1. 左递归前序查找,找到节点,则返回,否继续判断,
        // 2.当前的节点的右子节点是否为空,如果不空,则继续向右递归前序查找
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }

    // 中序遍历查找
    public HeroNode infixOrderSearch(int no) {

        // 判断当前节点的左子节点是否为空,如果不为空,则递归中序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }

        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入了中序查找一次~~");
        // 如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前节点
        if (this.no == no) {
            return this;
        }

        // 否则继续进行右递归的中序查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }

        return resNode;
    }

    // 后序遍历查找
    public HeroNode postOrderSearch(int no) {
        // 判断当前节点的左节点是否为空,如果不为空,则递归后序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.postOrderSearch(no);
        }
        if (resNode != null) {// 说明在左子树找到
            return resNode;
        }

        // 如果左子树没有找到,则向右子树递归进行后序遍历查找
        if (this.right != null) {
            resNode = this.right.postOrderSearch(no);
        }

        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入了后序查找一次~~");
        // 如果左右子树都没有找到,就比较当前节点是不是
        if (this.no == no) {
            return this;
        }
        return resNode;
    }

}

运行结果:


思考题:

如果要删除的节点是非叶子节点,现在不希望将该非叶子节点为根节点的子树删除,需要指定规则, 假如规定如下:

①如果该非叶子节点 A 只有一个子节点 B,则子节点 B 替代节点 A

②如果该非叶子节点 A 有左子节点 B 和右子节点 C,则让左子节点 B 替代节点 A。

请思考,如何完成该删除功能,在后面二叉排序树中,讲解具体的删除方法

相关推荐
柒小毓2 分钟前
将excel导入SQL数据库
数据库
bug菌¹7 分钟前
滚雪球学Oracle[2.5讲]:数据库初始化配置
数据库·oracle·数据库初始化·初始化配置
一休哥助手14 分钟前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
救救孩子把15 分钟前
深入理解 Java 对象的内存布局
java
落落落sss17 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
苏宸啊20 分钟前
顺序表及其代码实现
数据结构·算法
翔云12345622 分钟前
MVCC(多版本并发控制)
数据库·mysql
万物皆字节23 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
lin zaixi()24 分钟前
贪心思想之——最大子段和问题
数据结构·算法
夜雨翦春韭30 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法