0
点赞
收藏
分享

微信扫一扫

树结构之基础

谷中百合517 2022-02-22 阅读 48

树结构之基础 应用 查找

树结构

数组存储方式的分析

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

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

在这里插入图片描述

链式存储方式的分析

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

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

在这里插入图片描述

树存储方式的分析

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

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

二叉树

基本概念

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

在这里插入图片描述

  • 如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1 , n 为层数,则我们称为满二叉树

在这里插入图片描述

  • 如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树

在这里插入图片描述

二叉树的遍历

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

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

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

思路分析:

在这里插入图片描述
代码实现:

实现下列二叉树:
在这里插入图片描述

package com.atguigu.tree;

/**
 * @ClassName BinaryTreeDemo
 * @Author Jeri
 * @Date 2022-02-22 9:52
 * @Description 二叉树
 */

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

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

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

    public HeroNode getLeft() {
        return left;
    }

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

    public HeroNode getRight() {
        return right;
    }

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

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

    /*
     * @Description 编写前序遍历方法
     * @Date 2022/2/22 17:16
     * @param
     * @return []
     */
    public void preOrder(){
        //输出当前节点
        System.out.println(this);
        //向左子树递归遍历
        if(this.left != null){
            this.left.preOrder();
        }

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

    /*
     * @Description 编写中序遍历方法
     * @Date 2022/2/22 17:17
     * @param
     * @return []
     */
    public void infixOrder(){
        //向左子树递归遍历
        if(this.left != null){
            this.left.infixOrder();
        }

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

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

    /*
     * @Description 编写后序遍历方法
     * @Date 2022/2/22 17:18
     * @param
     * @return []
     */
    public void postOrder(){
        //向左子树递归遍历
        if(this.left != null){
            this.left.postOrder();
        }

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

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

    }
}

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

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


    /*
     * @Description 前序遍历
     * @Date 2022/2/22 17:28
     * @param
     * @return []
     */
    public void preOrder(){
        if(this.root != null){
            this.root.preOrder();
        }else{
            System.out.println("当前二叉树为空 无法遍历");
        }
    }

    /*
     * @Description 中序遍历
     * @Date 2022/2/22 17:28
     * @param
     * @return []
     */
    public void infixOrder(){
        if(this.root != null){
            this.root.infixOrder();
        }else{
            System.out.println("当前二叉树为空 无法遍历");
        }
    }


    /*
     * @Description 后序遍历
     * @Date 2022/2/22 17:28
     * @param
     * @return []
     */
    public void postOrder(){
        if(this.root != null){
            this.root.postOrder();
        }else{
            System.out.println("当前二叉树为空 无法遍历");
        }
    }
}


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

        //手动添加二叉树节点  后面学习递归创建二叉树
        bt.setRoot(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);

        //测试遍历
        System.out.println("前序遍历结果:------");
        bt.preOrder();

        System.out.println();
        System.out.println("中序遍历结果:------");
        bt.infixOrder();

        System.out.println();
        System.out.println("后序遍历结果:------");
        bt.postOrder();
	}
}

前序遍历结果:------
HeroNode{no=1, name='宋江'}
HeroNode{no=2, name='吴用'}
HeroNode{no=3, name='卢俊义'}
HeroNode{no=5, name='关胜'}
HeroNode{no=4, name='林冲'}

中序遍历结果:------
HeroNode{no=2, name='吴用'}
HeroNode{no=1, name='宋江'}
HeroNode{no=5, name='关胜'}
HeroNode{no=3, name='卢俊义'}
HeroNode{no=4, name='林冲'}

后序遍历结果:------
HeroNode{no=2, name='吴用'}
HeroNode{no=5, name='关胜'}
HeroNode{no=4, name='林冲'}
HeroNode{no=3, name='卢俊义'}
HeroNode{no=1, name='宋江'}

二叉树的查找

思路分析:
在这里插入图片描述
代码实现:

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

package com.atguigu.tree;

/**
 * @ClassName BinaryTreeDemo
 * @Author Jeri
 * @Date 2022-02-22 9:52
 * @Description 二叉树
 */

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

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

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

    public HeroNode getLeft() {
        return left;
    }

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

    public HeroNode getRight() {
        return right;
    }

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

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

    /*
     * @Description 编写前序遍历方法
     * @Date 2022/2/22 17:16
     * @param
     * @return []
     */
    public void preOrder(){
        //输出当前节点
        System.out.println(this);
        //向左子树递归遍历
        if(this.left != null){
            this.left.preOrder();
        }

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

    /*
     * @Description 编写中序遍历方法
     * @Date 2022/2/22 17:17
     * @param
     * @return []
     */
    public void infixOrder(){
        //向左子树递归遍历
        if(this.left != null){
            this.left.infixOrder();
        }

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

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

    /*
     * @Description 编写后序遍历方法
     * @Date 2022/2/22 17:18
     * @param
     * @return []
     */
    public void postOrder(){
        //向左子树递归遍历
        if(this.left != null){
            this.left.postOrder();
        }

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

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

    }

    /*
     * @Description 前序遍历查找
     * @Date 2022/2/22 17:18
     * @param no 带查找节点的编号
     * @return [no]
     */
    public HeroNode preOrderSearch(int no){
        System.out.println("进行前序遍历查找");
        //比较当前节点是否为待查找节点
        if(this.no == no){
            return this;
        }

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

        //判断左递归前序查找结果
        if(resNode != null){
            return resNode;
        }

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

        //无需判断 直接返回
        return resNode;
    }



    /*
     * @Description 中序遍历查找
     * @Date 2022/2/22 17:18
     * @param no 带查找节点的编号
     * @return [no]
     */
    public HeroNode infixOrderSearch(int no){
        //resNode 临时变量
        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;
    }

    /*
     * @Description 后序遍历查找
     * @Date 2022/2/22 17:18
     * @param no 带查找节点的编号
     * @return [no]
     */
    public HeroNode postOrderSearch(int no){
        //resNode 临时变量
        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;
    }

}

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

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


    /*
     * @Description 前序遍历
     * @Date 2022/2/22 17:28
     * @param
     * @return []
     */
    public void preOrder(){
        if(this.root != null){
            this.root.preOrder();
        }else{
            System.out.println("当前二叉树为空 无法遍历");
        }
    }

    /*
     * @Description 中序遍历
     * @Date 2022/2/22 17:28
     * @param
     * @return []
     */
    public void infixOrder(){
        if(this.root != null){
            this.root.infixOrder();
        }else{
            System.out.println("当前二叉树为空 无法遍历");
        }
    }


    /*
     * @Description 后序遍历
     * @Date 2022/2/22 17:28
     * @param
     * @return []
     */
    public void postOrder(){
        if(this.root != null){
            this.root.postOrder();
        }else{
            System.out.println("当前二叉树为空 无法遍历");
        }
    }

    /*
     * @Description 前序遍历查找
     * @Date 2022/2/22 17:30
     * @param no 待查找结点的编号
     * @return [no]
     */
    public HeroNode preOrderSearch(int no){
        if(this.root != null){
            return this.root.preOrderSearch(no);
        }else{
            return null;
        }
    }

    /*
     * @Description 中序遍历查找
     * @Date 2022/2/22 17:30
     * @param no 待查找结点的编号
     * @return [no]
     */
    public HeroNode infixOrderSearch(int no){
        if(this.root != null){
            return this.root.infixOrderSearch(no);
        }else{
            return null;
        }
    }

    /*
     * @Description 后序遍历查找
     * @Date 2022/2/22 17:30
     * @param no 待查找结点的编号
     * @return [no]
     */
    public HeroNode postOrderSearch(int no){
        if(this.root != null){
            return this.root.postOrderSearch(no);
        }else{
            return null;
        }
    }

}


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

        //手动添加二叉树节点  后面学习递归创建二叉树
        bt.setRoot(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);

        //测试查找
        HeroNode newNode = new HeroNode(5,"关胜");

        //临时变量
        HeroNode temp;
        System.out.println();
        System.out.println("前序遍历查找:------");
        temp = bt.preOrderSearch(newNode.getNo());
        if(temp == null){
            System.out.printf("没有找到编号为%d的人物\n",newNode.getNo());
        }else{
            System.out.printf("找到了,人物信息为" + temp + "\n");
        }

        System.out.println();
        System.out.println("中序遍历查找:------");
        temp = bt.infixOrderSearch(newNode.getNo());
        if(temp == null){
            System.out.printf("没有找到编号为%d的人物\n",newNode.getNo());
        }else{
            System.out.printf("找到了,人物信息为" + temp + "\n");
        }

        System.out.println();
        System.out.println("后序遍历查找:------");
        temp = bt.postOrderSearch(newNode.getNo());
        if(temp == null){
            System.out.printf("没有找到编号为%d的人物\n",newNode.getNo());
        }else{
            System.out.printf("找到了,人物信息为" + temp + "\n");
        }

    }
}

前序遍历查找:------
进行前序遍历查找
进行前序遍历查找
进行前序遍历查找
进行前序遍历查找
找到了,人物信息为HeroNode{no=5, name='关胜'}

中序遍历查找:------
进行中序遍历查找
进行中序遍历查找
进行中序遍历查找
找到了,人物信息为HeroNode{no=5, name='关胜'}

后序遍历查找:------
进行后序遍历查找
进行后序遍历查找
找到了,人物信息为HeroNode{no=5, name='关胜'}

二叉树的删除(规则1)

思路分析:

要求:

  • 如果删除的节点是叶子节点,则删除该节点
  • 如果删除的节点是非叶子节点,则删除该子树
  • 测试,删除掉 5 号叶子节点 和 3 号子树

在这里插入图片描述
代码实现:

HeroNode 类增加方法

/*
     * @Description 删除节点
     *              1.如果删除的节点是叶子节点,则删除该节点
     *              2.如果删除的节点是非叶子节点,则删除该子树
     * @Date 2022/2/22 19:55
     * @param no 待删除节点的编号
     */
    public void deleteByNo(int no){
        //左子树是待查找节点
        if(this.left != null && this.left.no == no){
            this.left = null;
            return;
        }
        //右子树是待查找节点
        if(this.right != null &&this.right.no == no){
            this.right = null;
            return;
        }

        //向左子树递归删除
        if(this.left != null){
            this.left.deleteByNo(no);
        }

        //向右子树递归删除
        if(this.right != null){
            this.right.deleteByNo(no);
        }

    }

在 BinaryTree 类增加方法

public void deleteByNo(int no){
        if(this.root == null){
            System.out.println("空树 无法删除");
        }else{
            //判断根节点是否为待查找结点
            if(this.root.getNo() == no){
                root = null;
                return;
            }

            //否则 递归删除
            this.root.deleteByNo(no);
        }
    }

在 BinaryTreeDemo 类增加测试代码:

		//测试删除
        System.out.println("删除前,前序遍历结果:--------");
        bt.preOrder();
        System.out.println("删除5号节点后,前序遍历结果为:------");
        bt.deleteByNo(5);
        bt.preOrder();
删除前,前序遍历结果:--------
HeroNode{no=1, name='宋江'}
HeroNode{no=2, name='吴用'}
HeroNode{no=3, name='卢俊义'}
HeroNode{no=5, name='关胜'}
HeroNode{no=4, name='林冲'}
删除5号节点后,前序遍历结果为:------
HeroNode{no=1, name='宋江'}
HeroNode{no=2, name='吴用'}
HeroNode{no=3, name='卢俊义'}
HeroNode{no=4, name='林冲'}
        //测试删除
        System.out.println("删除前,前序遍历结果:--------");
        bt.preOrder();
//        System.out.println("删除5号节点后,前序遍历结果为:------");
        bt.deleteByNo(5);
        System.out.println("删除3号节点后,前序遍历结果为:------");
        bt.deleteByNo(3);
        bt.preOrder();
删除前,前序遍历结果:--------
HeroNode{no=1, name='宋江'}
HeroNode{no=2, name='吴用'}
HeroNode{no=3, name='卢俊义'}
HeroNode{no=5, name='关胜'}
HeroNode{no=4, name='林冲'}
删除3号节点后,前序遍历结果为:------
HeroNode{no=1, name='宋江'}
HeroNode{no=2, name='吴用'}

二叉树的删除(规则2)

要求:

  • 如果该非叶子节点 A 只有一个子节点B,则子节点 B 替代节点A
  • 如果该非叶子节点 A 有左子节点B和右子节点C,则让左子节点B代替节点A
  • 如果叶子节点 A 则 直接删除

代码实现:

HeroNode 类增加方法

public void deleteByRule2(int no){
        //如果左子节点不为空且为删除节点
        if(this.left != null && this.left.no == no){
            //定义临时变量 为待删除节点
            HeroNode temp = this.left;

            //判断 该节点为叶子节点
            if(temp.left == null && temp.right == null){
                this.left = null;
                return;
            }

            //判断 该节点 同时存在左右子节点
            if(temp.left != null && temp.right != null){
                this.left = temp.left;
                this.left.right = temp.right;
                return;
            }

            //判断只存在左节点
            if(temp.left != null && temp.right == null){
                this.left = temp.left;
                return;
            }

            //判断只存在右节点
            if(temp.left == null && temp.right != null){
                this.left = temp.right;
                return;
            }

        }

        //如果右子节点不为空且为删除节点
        if(this.right != null && this.right.no == no){

            //定义临时变量 为待删除节点
            HeroNode temp = this.right;

            //判断 该节点为叶子节点
            if(temp.left == null && temp.right == null){
                this.right = null;
                return;
            }

            //判断 该节点 同时存在左右子节点
            if(temp.left != null && temp.right != null){
                this.right = temp.left;
                this.right.right = temp.right;
                return;
            }

            //判断只存在左节点
            if(temp.left != null && temp.right == null){
                this.right = temp.left;
                return;
            }

            //判断只存在右节点
            if(temp.left == null && temp.right != null){
                this.right = temp.right;
                return;
            }
        }

        //左子树不为空 进行递归删除
        if(this.left != null){
            this.left.deleteByRule2(no);
        }

        //向右子树递归删除
        if(this.right != null){
            this.right.deleteByRule2(no);
        }
    }

在 BinaryTree 类增加方法

 public void deleteByRule2(int no){
        if(this.root == null){
            System.out.println("空树 无法删除");
        }else{
            //判断根节点是否为待查找结点
            if(this.root.getNo() == no){

                //判断 该节点为叶子节点
                if(root.getLeft() == null && root.getRight() == null){
                    root = null;
                    return;
                }

                //判断 该节点 同时存在左右子节点
                // 删除有些问题
//                if(root.getLeft() != null && root.getRight() != null){
//                    //辅助变量
//                    HeroNode temp;
//                    temp = root.getRight();
//                    setRoot(root.getLeft());
//                    root.setRight(temp);
//                    return;
//                }

                //判断只存在左节点
                if(root.getLeft() != null && root.getRight() == null){
                    setRoot(root.getLeft());
                    return;
                }

                //判断只存在右节点
                if(root.getLeft() == null && root.getRight() != null){
                    setRoot(root.getRight());
                    return;
                }

            }

            //否则 递归删除
            this.root.deleteByRule2(no);
        }
    }

在 BinaryTreeDemo 类增加测试代码:

        //测试删除
        System.out.println("按照规则2删除前,前序遍历结果:--------");
        bt.preOrder();
//        System.out.println("删除5号节点后,前序遍历结果为:------");
        bt.deleteByNo(5);
        System.out.println("按照规则2删除3号节点后,前序遍历结果为:------");
        bt.deleteByRule2(3);
        bt.preOrder();
按照规则2删除前,前序遍历结果:--------
HeroNode{no=1, name='宋江'}
HeroNode{no=2, name='吴用'}
HeroNode{no=3, name='卢俊义'}
HeroNode{no=5, name='关胜'}
HeroNode{no=4, name='林冲'}
按照规则2删除3号节点后,前序遍历结果为:------
HeroNode{no=1, name='宋江'}
HeroNode{no=2, name='吴用'}
HeroNode{no=5, name='关胜'}
HeroNode{no=4, name='林冲'}

参考文献

尚硅谷Java数据结构与java算法

举报

相关推荐

0 条评论