数据结构与算法练习(三)二叉树

news2024/12/23 8:45:05

文章目录

  • 1、树
  • 2、二叉树
  • 3、满二叉树
  • 4、完全二叉树
  • 5、二叉树的遍历(前序、中序、后序)
    • 二叉树删除节点或树
  • 6、顺序存储二叉树
    • 顺序存储二叉树遍历(前序、中序、后序)
  • 7、线索化二叉树
    • 中序线索二叉树
    • 前序线索二叉树
    • 后序线索二叉树


1、树

树是一种非线性的数据结构,是由n(n >=0)个结点组成的有限集合
如果n=0,树为空树。
如果n>0,除根节点外,其余结点被分成m(m>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。

在这里插入图片描述
相关概念:

  • 根节点:没有父节点的节点。
  • 叶节点:没有子节点的节点。
  • 兄弟节点:具有相同父节点的节点;
  • 结点的度:结点拥有的子树个数。例如A节点的度3。 B,C为2
  • 树的度:树内各结点最大的度 。例如A节点的度最大,所以树的度为3
  • 节点的层次 :从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;
  • 树的高度或深度 :树中节点的最大层次; 如上图:树的高度为 4
  • 森林 :由 m( m>0 )棵互不相交的树的集合称为森林。

2、二叉树

二叉树(Binary tree)是每个节点最多有两个子节点的树。(删掉上面的D节点)

在这里插入图片描述

3、满二叉树

 一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
 如果一个二叉树的层数为n,且结点总数是2^n -1 ,则它就是满二叉树

在这里插入图片描述

4、完全二叉树

完全二叉树是一种特殊的二叉树,它除了最后一层外,其他每一层都被完全填满,且最后一层的节点都靠左排列。
满二叉树一定是完全二叉树

在这里插入图片描述

5、二叉树的遍历(前序、中序、后序)

按照不同的规则(看输出父节点的顺序)分为:

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

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

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

    例如上面的完全二叉树:
        前序遍历:ABEFCG
        中序遍历:EBFAGC
        后序遍历:EFBGCA
    

代码实现:

package Tree;
public class BinaryTreeDemo {
    public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        Node a = new Node("A");
        Node b = new Node("B");
        Node c = new Node("C");
        Node e = new Node("E");
        Node f = new Node("F");
        Node g = new Node("G");
        //手动创建树
        binaryTree.setRoot(a);
        a.left=b;
        a.right=c;
        b.left=e;
        b.right=f;
        c.left=g;
        System.out.println("前序遍历:");
        binaryTree.preOrderM();
        System.out.println("中序遍历:");
        binaryTree.infixOrderM();
        System.out.println("后序遍历:");
        binaryTree.postOrderM();
    }
}
//创建树的属性和方法
class BinaryTree{
    //定义根节点
    Node root;

    public void setRoot(Node root) {
        this.root = root;
    }
    //前序遍历
    public void preOrderM() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void infixOrderM() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void postOrderM() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空!无法遍历!");
        }
    }
}

//创建节点对象的属性和方法
class  Node{
    String name;
    Node left;
    Node right;

    public Node(String name) {
        this.name = name;
    }
    //重写toString方法
    @Override
    public String toString() {
        return "Node{" +
                "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);
    }
}
前序遍历中序遍历后序遍历
在这里插入图片描述在这里插入图片描述在这里插入图片描述

二叉树删除节点或树

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

代码实现:删除树B和叶节点E

package Tree;
public class BinaryTreeDemo {
    public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        Node a = new Node("A");
        Node b = new Node("B");
        Node c = new Node("C");
        Node e = new Node("E");
        Node f = new Node("F");
        Node g = new Node("G");
        //手动创建树
        binaryTree.setRoot(a);
        a.left=b;
        a.right=c;
        b.left=e;
        b.right=f;
        c.left=g;
        System.out.println("前序遍历:");
        binaryTree.preOrderM();
        //删除某个节点
        System.out.println("删除节点E");
        binaryTree.deleteNode("E");
        System.out.println("查看删除后的节点");
        binaryTree.preOrderM();
        System.out.println("删除树B");
        binaryTree.deleteNode("B");
        System.out.println("查看删除后的节点");
        binaryTree.preOrderM();
    }
}
//创建树的属性和方法
class BinaryTree{
    //定义根节点
    Node root;

    public void setRoot(Node root) {
        this.root = root;
    }
    //前序遍历
    public void preOrderM() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void infixOrderM() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //中序遍历
    public void postOrderM() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空!无法遍历!");
        }
    }
    //删除节点,先判断根节点是不是所需要的节点
    public  void deleteNode(String name){
       if (this.root!=null){
           if (this.root.name==name){
               this.root=null;
           }else {
               this.root.delNode(name);
           }
       }else {
           System.out.println("二叉树为空!无法删除节点");
       }
    }
}
//创建节点对象的属性和方法
class  Node{
    String name;
    Node left;
    Node right;

    public Node(String name) {
        this.name = name;
    }
    //重写toString方法
    @Override
    public String toString() {
        return "Node{" +
                "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);
    }
    //删除node方法
    public void delNode(String name) {
        if (this.left!=null&&this.left.name==name){
            this.left=null;
            return;
        }
        if (this.right!=null&&this.right.name==name){
            this.right=null;
            return;
        }
        //上述是root底下的两个节点。若这两个都不是我们要的那个节点,需再递归
        if (this.left!=null){
            this.left.delNode(name);
        }
        if (this.right!=null){
            this.right.delNode(name);
        }
    }
}

6、顺序存储二叉树

 顺序存储二叉树是二叉树的一种存储方式。
 将二叉树存储在一个数组中,通过存储元素的下标反映元素之间的父子关系。

在这里插入图片描述
特点:

  • 顺序存储二叉树通常只考虑完全二叉树
  • 遍历数组arr时,仍然可以(前序、中序、后序遍历)
  • 下标为n的元素的左子节点下标为2*n+1
  • 下标为n的元素的右子节点下标为2*n+2
  • 下标为n的元素的父节点为(n-1)/2

顺序存储二叉树遍历(前序、中序、后序)

public class ArrayBinaryTreeDemo {
    public static void main(String[] args) {
        String [] arr={"A","B","C","E","F","G"};
        ArrayBinaryTree tree = new ArrayBinaryTree(arr);
        tree.preOrder();
        System.out.println();
        tree.infixOrder();
        System.out.println();
        tree.postOrder();
    }
}
//实现顺序存储二叉树
class ArrayBinaryTree{
    String[] arr;

    public ArrayBinaryTree(String[] arr) {
        this.arr = arr;
    }
    public  void  preOrder(){
        this.preOrder(0);
    }
    public  void infixOrder(){
        this.infixOrder(0);
    }
    public  void  postOrder(){
        this.postOrder(0);
    }
    //实现前序遍历
    public  void  preOrder(int index){
        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);
        }
    }
    //实现中序遍历
    public  void  infixOrder(int index){
        if (arr==null || arr.length==0){
            System.out.println("数组为空");
            return;
        }
        if ((index * 2 + 1) < arr.length) {
            infixOrder(index * 2 + 1);
        }
        System.out.print(arr[index]+" ");
        //再递归右子树
        if ((index * 2 + 2) < arr.length) {
            infixOrder(index * 2 + 2);
        }
    }
    //后序遍历
    public  void  postOrder(int index){
        if (arr==null || arr.length==0){
            System.out.println("数组为空");
            return;
        }
        if ((index * 2 + 1) < arr.length) {
            postOrder(index * 2 + 1);
        }
        //再递归右子树
        if ((index * 2 + 2) < arr.length) {
            postOrder(index * 2 + 2);
        }
        System.out.print(arr[index]+" ");
    }
}

运行结果:
在这里插入图片描述

7、线索化二叉树

对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。
根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。

注意:线索链表解决了无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题,解决了二叉链表找左、右孩子困难的问题。

遍历线索化二叉树:因为线索化后,各个结点指向有变化,因此原来的遍历方式不能使用,这时需要使用新的方式遍历线索化二叉树,各个节点可以通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。

中序线索二叉树

例如:中序线索二叉树

在这里插入图片描述

public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        MyNode a = new MyNode("A");
        MyNode b = new MyNode("B");
        MyNode c = new MyNode("C");
        MyNode e = new MyNode("E");
        MyNode f = new MyNode("F");
        MyNode g = new MyNode("G");
        //手动创建树
        threadedBinaryTree.setRoot(a);
        a.left=b;
        a.right=c;
        b.left=e;
        b.right=f;
        c.left=g;
        System.out.println("未线索化前e节点的前驱节点和后驱");
        System.out.println("F号结点的前驱结点为:"+e.left);//3
        System.out.println("F号结点的后继结点为:"+e.right);//1
        System.out.println("中序线索化后e节点的前驱节点和后驱");
        threadedBinaryTree.infixThreadedNodes();
        System.out.println("F号结点的前驱结点为:"+e.left);//3
        System.out.println("F号结点的后继结点为:"+e.right);//1
    }
}
//定义能实现线索化的二叉树
class ThreadedBinaryTree {
    MyNode root;
    MyNode pre=null;//指向当前节点的前驱节点  递归过程中pre总是保留前一个节点
    //为了实现线索化,需要创建指向当前节点的前驱结点的指针
    public void setRoot(MyNode root) {
        this.root = root;
    }
    public void infixThreadedNodes() {
        this.infixThreadedNodes(root);
    }
    //编写对二叉树进行中序线索化的方法
    public void infixThreadedNodes(MyNode node) {
        if (node == null) {//节点为空 不能线索化
            return;
        }
            //线索化左子树
            infixThreadedNodes(node.left);
            if (node.left==null){
                node.left=pre;
                node.leftType=1;
            }
            //处理后继节点
            if (pre!=null && pre.right==null){
                pre.right=node;
                pre.rightType=1;
            }
            //每处理一个节点,让当前节点是下一个节点的前驱节点
            pre=node;
            //线索化右子树
            infixThreadedNodes(node.right);
    }
}
class  MyNode{
    String name;
    MyNode left;
    MyNode right;
    //说明
    //1.如果leftType==0 表示指向的是左子树,为1 表示指向前驱节点
    //2.如果rightType==0 表示指向的是右子树,为1 表示指向后继节点
    int leftType;
    int rightType;
    public MyNode(String name) {
        this.name = name;
    }
    //重写toString方法
    @Override
    public String toString() {
        return "Node{" +
                "name='" + name + '\'' +
                '}';
    }
}

运行结果:
在这里插入图片描述

前序线索二叉树

在这里插入图片描述

 //编写对二叉树进行前序线索化的方法
    public void preThreadedNodes(MyNode node) {
        if (node == null) {
            return;
        }
        //线索化当前节点
        //处理前驱节点     左指针为空 则将左指针指向前驱节点
        if (node.left == null) {
            node.left=pre;
            node.leftType=1;
        }
        //处理后继节点     前一个节点的后继节点指向当前节点
        if (pre != null && pre.right == null) {
            pre.right=node;
            pre.rightType=1;
         }
        //更新pre
        pre = node;
        //线索化左子树
        if (node.leftType == 0) {
            preThreadedNodes(node.left);
        }
        //线索化右子树
        if (node.rightType == 0) {
            preThreadedNodes(node.right);
        }
    }

在这里插入图片描述

后序线索二叉树

在这里插入图片描述

 //编写对二叉树进行后序线索化的方法
    public void postThreadedNodes(MyNode node) {
        if (node == null) {
            return;
        }
        //线索化左子树
        if (node.leftType == 0) {
            postThreadedNodes(node.left);
        }
        //线索化右子树
        if (node.rightType == 0) {
            postThreadedNodes(node.right);
        }
        //线索化当前节点
        if (node.left == null) {
            node.left=pre;
            node.leftType=1;
        }
        if (pre != null && pre.right == null) {
            pre.right=node;
            pre.rightType=1;
        }
        //更新pre
        pre = node;
    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/617372.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Matlab 之 Curve Fitting APP 使用笔记

文章目录 Part.I IntroductionPart.II 使用笔记Chap.I 拟合函数Chap.II 注意事项 Part.I Introduction 曲线或曲面拟合获取拟合参数。本篇博文主要记录一下 Matlab 拟合 APP Curve Fitting 的使用方法。 Part.II 使用笔记 这个APP用来做拟合的&#xff0c;包括二维数据的线拟…

常见的样本统计量及其数字特征

常见的样本统计量及其数字特征 下图来自《统计学图鉴》 样本统计量有什么作用&#xff1f; 因为总体特征包含有总体均值、总体方差等特征&#xff0c;我们在用样本推断总体时&#xff0c;其实就是用样本特征去估计总体特征&#xff0c;例如&#xff1a;样本均值这个统计量的期…

案例33:基于Springboot名城小区物业管理系统开题报告设计

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Spark RDD统计每日新增用户

文章目录 一&#xff0c;提出任务二&#xff0c;实现思路三&#xff0c;准备工作1、在本地创建用户文件2、将用户文件上传到HDFS指定位置 四&#xff0c;完成任务1、在Spark Shell里完成任务&#xff08;1&#xff09;读取文件&#xff0c;得到RDD&#xff08;2&#xff09;倒排…

为什么要对实体类进行序列化并且要生成序列化ID?

一、为什么要对实体类进行序列化且要生成序列化ID 在Java开发中&#xff0c;实体类将会被用来与其他对象进行交互。Java语言是面向对象的&#xff0c;所以实体类包含了很多信息和方法。序列化是Java中一种将对象转换为字节流的机制&#xff0c;使得对象可以在网络上传输和存储。…

相机成像模型(一)

相机模组 如上图所示相机模组由多个元件组成,其中比较重要的元件包括镜头、感光芯片、驱动芯片。镜头的作用是聚集光线,确保良好的成像环境;感光芯片将光信号转换为电信号;驱动芯片则负责信号处理(去噪、白平衡等)与格式转换。 相机的成像过程为物体通过镜头聚集…

jvm cpu 高定位

快速的发现线程cpu高, 最终发现是gc线程, 最终去分析jvm top -o %CPU top -Hp108920 jmap -dump:formatb,fileheap.bin 108920 jvm 命令和工具_个人渣记录仅为自己搜索用的博客-CSDN博客 $ jstat -gcold 108920 MC MU CCSC CCSU OC OU YGC FGC FGCT GCT 218368.0 212670.3 253…

Java POI技术

引入依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.0.1</version> </dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-oo…

Netty的bytebuf详解

ByteBuf ByteBuf是对nio中ByteBuffer的增强。主要的增强点就是ByteBuf它可以动态调整容量大小&#xff0c;当要存储的数据超过了当前容量的上限就会进行扩容&#xff0c;扩容的上限是多少&#xff1f;扩容机制是什么&#xff1f;请跟着本文往下看。对了&#xff0c;还有一个增强…

区间预测 | MATLAB实现基于QRCNN-GRU卷积门控循环单元多变量时间序列区间预测

区间预测 | MATLAB实现基于QRCNN-GRU卷积门控循环单元多变量时间序列区间预测 目录 区间预测 | MATLAB实现基于QRCNN-GRU卷积门控循环单元多变量时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于QRCNN-GRU卷积神经网络结合门控循…

[BUUOJ] [RE] [ACTF新生赛2020] rome1

IDA 好久没写博客了&#xff0c;最近在刷re&#xff0c;这道题是我觉得十分有意义的一道题。故AC后总结分享给大家。不足之处请指正。 分析 直接导入IDA shift F12 双击后按 ctrl x跳转到被调用的函数中&#xff0c;按F5反编译&#xff0c;源代码如下 int func() {int r…

数据结构-线性表-链表

目录 线性表的链式存储结构&#xff1a;一、单向链表的ADT定义二、链表的优缺点 线性表的链式存储结构&#xff1a; 为了表示数据元素ai和其后继元素ai1之间的逻辑关系&#xff0c;对ai来说需存储其本身信息和后继元素的信息&#xff08;存储位置&#xff09;。这两部分组成ai…

Android系统(AOSP)--编译指令篇

目录 一、编译Android系统 二、普通编译指令 三、快速编译指令 四、新建lunch项和编译类型说明 五、Android编译系统的整体架构 六、编译后的输出目录和生成文件 七、Android常用编译命令总结 一、编译Android系统 1.Android系统全编译(Android5.1以后mtk都是这种方式…

裸辞3个月没工作,害怕面试,害怕HR问我的问题,怎么办?

其实裸辞最大的伤害就是很容易导致自己的不自信。 现在可能就是你的低谷期&#xff0c;你需要做的是什么呢&#xff0c;丰富自己。 你要相信&#xff0c;你只是太久没有面试过&#xff0c;生疏了而已。 今天小月带你回到面试场&#xff0c;找回面试最纯正的感觉&#xff01; 面…

火龙果MM32F3273G8P开发板MindSDK开发教程1 - 点亮LED

火龙果MM32F3273G8P-MindSDK开发教程1-点亮LED 1、登录官网下载对应的MindSDK固件 https://mindsdk.mindmotion.com.cn/&#xff0c;然后注册下载mm32F3270的固件即可。 下载完的文件为 plus-f3270_mdk.zip 解压后的文件路径如图&#xff1a; 2、新建LED工程 将下载的plu…

基于张量补全的交通数据复原文献汇总(最新)

由于传感器故障和通信故障等因素导致的交通数据缺失严重制约了ITS的发展与应用。如何准确、高效地恢复缺失数据已成为ITS的一个关键问题。近年来&#xff0c;LRTC&#xff08;低秩张量补全&#xff09;的方法已被广泛应用于交通数据补全。本文将介绍几篇最新的关于交通数据补全…

python3 爬虫相关学习7:初步摸索使用 BeautifulSoup

1 一个爬图片pic的代码的例子 下面这段是爬一些图片pic的代码学写了一段bs的代码&#xff0c;但是马上报错 #E:\work\FangCloudV2\personal_space\2learn\python3\py0001.txtimport requests from bs4 import BeautifulSoupurl"https://movie.douban.com/celebrity/10115…

搭建个人博客

个人网站用处有很多&#xff0c;可以写博客来记录学习过程中的各种事&#xff0c;不管是新知识还是踩坑记录&#xff0c;写完就丢在网站上&#xff0c;方便日后复习&#xff0c;也可以共享给他人&#xff0c;让其他人避免踩雷。 当然也不仅限于技术性的文章&#xff0c;生活中有…

隐马尔可夫模型在数学建模中的应用及MATLAB实现

2023年9月数学建模国赛期间提供ABCDE题思路加Matlab代码,专栏链接(赛前一个月恢复源码199,欢迎大家订阅):http://t.csdn.cn/Um9Zd 目录 2023年9月数学建模国赛期间提供ABCDE题思路加Matlab代码,专栏链接(赛前一个月恢复源码199,欢迎大家订阅):http://t.csdn.cn/Um9Zd 隐马尔可…

(二)CSharp-数据类型

一、数据类型 1、C#程序是一组类型声明 C 程序是一组函数和数据类型C 程序是一组函数和类C# 程序是一组类型声明 2、类型是一种模板 类型由下面的元素定义&#xff1a; 名称用于保存数据成员的数据结构一些行为及约束条件 3、实例化类型 从某个类型模板创建实际的对象&am…