数据结构和算法学习笔记四_树基础(Java)

数据结构和算法学习笔记四_树基础

学习视频:尚硅谷韩老师Java讲解数据结构与算法

一、树

树示意图:

在这里插入图片描述

树的常用术语(结合示意图理解): 
1) 节点 
2) 根节点 
3) 父节点 
4) 子节点 
5) 叶子节点 (没有子节点的节点) 
6) 节点的权(节点值) 
7) 路径(从 root 节点找到该节点的路线) 
8) 层 
9) 子树 
10) 树的高度(最大层数) 
11) 森林 :多颗子树构成森林

二、二叉树

2.1、二叉树简述

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

  2. 二叉树的子节点分为左节点和右节点

  3. 示意图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1RW9yF8t-1609678072943)(D:\TyporaPic\image-.png)]

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

在这里插入图片描述

满二叉树

  1. 如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二

层的叶子节点在右边连续,我们称为完全二叉树
在这里插入图片描述

完全二叉树

2.2、二叉树前序,中序和后序遍历.

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

  2. 中序遍历: 先遍历左子树,再输出父节点,再遍历右子树

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

在这里插入图片描述

代码实现:

package com.lxf.Tree;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //创建需要的节点
        HeroNode root=new HeroNode(1,"宋江");
        HeroNode n2=new HeroNode(2,"吴用");
        HeroNode n3=new HeroNode(3,"卢俊义");
        HeroNode n4=new HeroNode(4,"林冲");
        //创建root为节点的树
        root.setLeft(n2);
        root.setRight(n3);
        n3.setRight(n4);

        //先需要创建一颗二叉树
        BinaryTree binaryTree=new BinaryTree(root);
        //测试前序:
        binaryTree.preOrder();
        System.out.println();

        //测试中序:
        binaryTree.midOrder();
        System.out.println();

        //测试后序遍历:
        binaryTree.nexOrder();
        System.out.println();
        
        //结果:
        //HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='林冲'} 
        
		//HeroNode{no=2, name='吴用'} HeroNode{no=1, name='宋江'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='林冲'} 
        
		//HeroNode{no=2, name='吴用'} HeroNode{no=4, name='林冲'} HeroNode{no=3, name='卢俊义'} HeroNode{no=1, name='宋江'} 
    }
}

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

    public BinaryTree() {
    }

    public BinaryTree(HeroNode root) {
        this.root = root;
    }
    //前序遍历
    public  void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //中序遍历
    public  void midOrder(){
        if(this.root!=null){
            this.root.midOrder();
        }else{
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //后序遍历
    public  void nexOrder(){
        if(this.root!=null){
            this.root.nexOrder();
        }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) {
        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 + '\'' +
                '}';
    }

    /**
     * 前序遍历的方法
     */
    public void preOrder(){
        System.out.print(this+" ");//先输出父节点
        //递归向左子树前序遍历
        if(this.left!=null){
            this.left.preOrder();
        }
        //递归向右子树前序遍历
        if(this.right!=null){
            this.right.preOrder();
        }
    }
    /**
     * 中序遍历的方法
     */
    public void midOrder(){
        //递归向左子树中序遍历
        if(this.left!=null){
            this.left.midOrder();
        }
        System.out.print(this+" ");//然后输出父节点
        //递归向右子树中序遍历
        if(this.right!=null){
            this.right.midOrder();
        }
    }
    /**
     * 后序遍历的方法
     */
    public void nexOrder(){
        //递归向左子树后序遍历
        if(this.left!=null){
            this.left.nexOrder();
        }
        //递归向右子树后序遍历
        if(this.right!=null){
            this.right.nexOrder();
        }
        System.out.print(this+" ");//再输出父节点
    }

}

2.3、二叉树前序,中序和后序查找

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

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

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

  4. 思路分析图解

在这里插入图片描述

代码实现:

package com.lxf.Tree;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //创建需要的节点
        HeroNode root=new HeroNode(1,"宋江");
        HeroNode n2=new HeroNode(2,"吴用");
        HeroNode n3=new HeroNode(3,"卢俊义");
        HeroNode n4=new HeroNode(4,"林冲");
        //创建root为节点的树
        root.setLeft(n2);
        root.setRight(n3);
        n3.setRight(n4);

        //先需要创建一颗二叉树
        BinaryTree binaryTree=new BinaryTree(root);
        //测试前序查找:
        System.out.println(binaryTree.preOrderSearch(n4));
        //测试中序查找:
        System.out.println(binaryTree.midOrderSearch(n2));
        //测试后序查找:
        System.out.println(binaryTree.nexOrderSearch(n2));



    }
}

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

    public BinaryTree() {
    }

    public BinaryTree(HeroNode root) {
        this.root = root;
    }
    //前序遍历
    public  HeroNode preOrderSearch(HeroNode searchNode){
        if(this.root!=null){
            if(searchNode.getName()!=null){
                return this.root.preOrderSearch(searchNode);
            }else{
                System.out.println("搜索的名称为空,无法查找");
            }
        }else{
            System.out.println("二叉树为空,无法查找");
        }
        return null;
    }
    //中序搜索
    public  HeroNode midOrderSearch(HeroNode searchNode){
        if(this.root!=null){
            if(searchNode.getName()!=null){
                return this.root.midOrderSearch(searchNode);
            }else{
                System.out.println("搜索的名称为空,无法查找");
            }
        }else{
            System.out.println("二叉树为空,无法查找");
        }
        return null;
    }
    //后序搜索
    public  HeroNode nexOrderSearch(HeroNode searchNode){
        if(this.root!=null){
            if(searchNode.getName()!=null){
                return this.root.nexOrderSearch(searchNode);
            }else{
                System.out.println("搜索的名称为空,无法查找");
            }
        }else{
            System.out.println("二叉树为空,无法查找");
        }
        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) {
        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 + '\'' +
                '}';
    }

    /**
     * 前序查找的方法
     */
    public HeroNode preOrderSearch(HeroNode searchNode){
        HeroNode heroNode=null;
        //先判断父节点
        if(this.name.equals(searchNode.name)){
            return this;
        }
        //递归向左子树前序查找
        if(this.left!=null){
            heroNode=this.left.preOrderSearch(searchNode);
        }
        if (heroNode != null) {
            return heroNode;
        }
        //递归向右子树前序查找
        if(this.right!=null){
            heroNode=this.right.preOrderSearch(searchNode);
        }
        if (heroNode != null) {
            return heroNode;
        }
        return heroNode;
    }
    /**
     * 中序查找的方法
     */
    public HeroNode midOrderSearch(HeroNode searchNode){
        HeroNode heroNode=null;
        //先递归向左子树中序遍历
        if(this.left!=null){
            heroNode=this.left.midOrderSearch(searchNode);
        }
        if (heroNode != null) {
            return heroNode;
        }
        //再判断当前节点
        if(this.name.equals(searchNode.name)){
            return this;
        }
        //后递归向右子树中序遍历
        if(this.right!=null){
            heroNode=this.right.midOrderSearch(searchNode);
        }
        if (heroNode != null) {
            return heroNode;
        }
        return heroNode;
    }
    /**
     * 后序查找的方法
     */
    public HeroNode nexOrderSearch(HeroNode searchNode){
        HeroNode heroNode=null;
        //先递归向左子树后序查找,找到就返回值
        if(this.left!=null){
            heroNode=this.left.nexOrderSearch(searchNode);
        }
        if (heroNode != null) {
            return heroNode;
        }
        //再递归向右子树后序查找,找到就返回值
        if(this.right!=null){
            heroNode=this.right.nexOrderSearch(searchNode);
        }
        if (heroNode != null) {
            return heroNode;
        }
        //再判断当前节点
        if(this.name.equals(searchNode.name)){
            return this;
        }
        return heroNode;
    }
}

2.4、二叉树删除节点

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

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

  3. 测试,删除掉 5 号叶子节点 和 3 号子树.

  4. 完成删除思路分析

在这里插入图片描述

代码实现:

package com.lxf.Tree;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //创建需要的节点
        HeroNode root=new HeroNode(1,"宋江");
        HeroNode n2=new HeroNode(2,"吴用");
        HeroNode n3=new HeroNode(3,"卢俊义");
        HeroNode n4=new HeroNode(4,"林冲");
        //创建root为节点的树
        root.setLeft(n2);
        root.setRight(n3);
        n3.setRight(n4);

        //先需要创建一颗二叉树
        BinaryTree binaryTree=new BinaryTree(root);
        //删除前前序遍历:
        binaryTree.preOrder();
        System.out.println();
        //HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='林冲'} 

        //删除no为3的节点
        binaryTree.deleteNode(3);

        //删除后前序遍历:
        binaryTree.preOrder();
        //HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} 
    }
}

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

    public BinaryTree() {
    }

    public BinaryTree(HeroNode root) {
        this.root = root;
    }
    //前序遍历
    public  void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //中序遍历
    public  void midOrder(){
        if(this.root!=null){
            this.root.midOrder();
        }else{
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //后序遍历
    public  void nexOrder(){
        if(this.root!=null){
            this.root.nexOrder();
        }else{
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //删除节点
    public  void deleteNode(int no){
        if(this.root!=null){
            if(root.getNo()==no){
                this.root=null;
            }else{
                this.root.deleteNode(no);
            }
        }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) {
        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 + '\'' +
                '}';
    }

    /**
     * 前序遍历的方法
     */
    public void preOrder(){
        System.out.print(this+" ");//先输出父节点
        //递归向左子树前序遍历
        if(this.left!=null){
            this.left.preOrder();
        }
        //递归向右子树前序遍历
        if(this.right!=null){
            this.right.preOrder();
        }
    }
    /**
     * 中序遍历的方法
     */
    public void midOrder(){
        //递归向左子树中序遍历
        if(this.left!=null){
            this.left.midOrder();
        }
        System.out.print(this+" ");//然后输出父节点
        //递归向右子树中序遍历
        if(this.right!=null){
            this.right.midOrder();
        }
    }
    /**
     * 后序遍历的方法
     */
    public void nexOrder(){
        //递归向左子树后序遍历
        if(this.left!=null){
            this.left.nexOrder();
        }
        //递归向右子树后序遍历
        if(this.right!=null){
            this.right.nexOrder();
        }
        System.out.print(this+" ");//再输出父节点
    }

    /**
     * 删除节点
     * @param no
     */
    public void deleteNode(int no) {
        if(this.left!=null&&this.left.getNo()==no){
            this.left=null;
            return;
        }
        if(this.right!=null&&this.right.getNo()==no){
            this.right=null;
            return;
        }
        if(this.left!=null){
            this.left.deleteNode(no);
        }
        if(this.right!=null){
            this.right.deleteNode(no);
        }
    }
}

三、顺序存储二叉树

基本说明

从数据存储来看,数组存储方式和树的存储方式可以相互转换,即数组可以转换成树树也可以转换成数组
在这里插入图片描述

要求:

  1. 右图的二叉树的结点,要求以数组的方式来存放 arr : [1, 2, 3, 4, 5, 6, 6]

  2. 要求在遍历数组 arr 时,仍然可以以前序遍历,中序遍历和后序遍历的方式完成结点的遍历

顺序存储二叉树的特点:

  1. 顺序二叉树通常只考虑完全二叉树

  2. 第 n 个元素的左子节点为 2 * n + 1

  3. 第 n 个元素的右子节点为 2 * n + 2

  4. 第 n 个元素的父节点为 (n-1) / 2

  5. n : 表示二叉树中的第几个元素(按 0

代码实现:

package com.lxf.Tree;

public class ArrBinaryTreeDemo {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5,6,7};
        ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
        //前序
        arrBinaryTree.preOrder(0);//1 2 4 5 3 6 7
        System.out.println();
        //中序
        arrBinaryTree.midOrder(0);//4 2 5 1 6 3 7
        System.out.println();
        //后序
        arrBinaryTree.nexOrder(0);//4 5 2 6 7 3 1
    }
}

/**
 * 编写一个ArrayBinaryTree,实现顺序存储二叉树遍历
 */
class ArrBinaryTree{
    private int[] arr;//存储数据结点的数组

    public ArrBinaryTree(int[] arr){
        this.arr=arr;
    }

    /**
     * 编写一个方法,完成顺序存储二叉树的前序遍历
     * @param index 数组的下标
     */
    public void preOrder(int index){
        //如果数组为空,arr.length==0
        if(arr==null||arr.length==0) {
            System.out.println("数组为空,不能遍历!");
            return;
        };
        //输出数组当前值
        System.out.print(arr[index]+" ");
        //向左前序遍历
        if(index*2+1<arr.length){
            preOrder(index*2+1);
        }
        //向右前序遍历
        if(index*2+2<arr.length){
            preOrder(index*2+2);
        }
    }
    /**
     * 编写一个方法,完成顺序存储二叉树的中序遍历
     * @param index 数组的下标
     */
    public void midOrder(int index){
        //如果数组为空,arr.length==0
        if(arr==null||arr.length==0) {
            System.out.println("数组为空,不能遍历!");
            return;
        };
        //向左前序遍历
        if(index*2+1<arr.length){
            midOrder(index*2+1);
        }
        //输出数组当前值
        System.out.print(arr[index]+" ");
        //向右前序遍历
        if(index*2+2<arr.length){
            midOrder(index*2+2);
        }
    }
    /**
     * 编写一个方法,完成顺序存储二叉树的后序遍历
     * @param index 数组的下标
     */
    public void nexOrder(int index){
        //如果数组为空,arr.length==0
        if(arr==null||arr.length==0) {
            System.out.println("数组为空,不能遍历!");
            return;
        };
        //向左前序遍历
        if(index*2+1<arr.length){
            nexOrder(index*2+1);
        }
        //向右前序遍历
        if(index*2+2<arr.length){
            nexOrder(index*2+2);
        }
        //输出数组当前值
        System.out.print(arr[index]+" ");
    }
}

四、线索化二叉树(以后用到再学)

https://blog.csdn.net/s_999999/article/details/86157532

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值