【数据结构六】图文结合详解二叉树(五千字)

news2025/6/17 9:00:55

二叉树

        树是一种非线性的数据结构,它是由n个结点组成的具有层次关系的集合,把他叫做树是因为它的根朝上,叶子朝下,看起来像一颗倒挂的树。二叉树是一种最多只有两个节点的树型结构。这篇文章会用Java代码手撕二叉树的实现,从概念到实现,到oj题训练,你不仅能学会二叉树,还能加深对它的理解和运用。

1.树形结构的概念

在树形结构中,子树之间不能有交集,否则就不是树型结构,它具有以下的特点

  • 子树是不相交的;
  • 除了根节点外,每个节点有且仅有一个父节点;
  • 一颗N个结点的树有N-1条边。

 

 树中的相关概念

结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6

树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6

叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点

双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点

孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点

根结点:一棵树中,没有双亲结点的结点;如上图:A

结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推

树的高度或深度:树中结点的最大层次; 如上图:树的高度为4

非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等节点为分支结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

森林:由m(m>=0)棵互不相交的树组成的集合称为森林
 

树的应用

     树的思想和应用在我们周围应用非常广泛也非常重要,比如树的遍历运用递归的思想,电脑中的文件管理系统(目录和文件)运用树形结构存储。

2.二叉树的性质

二叉树是一个节点的有限集合,要么为空,要么是由一个根节点加上两颗被称为左子树和右子树的二叉树组成。如下图所示:

 两种特殊的二叉树:

1. 满二叉树: 一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。

2. 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。


 

 二叉树的性质:

  1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2^i-1 (i>0)个结点
  2. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是2^k - 1 (k>=0)
  3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1
  4.  具有n个结点的完全二叉树的深度k为 log2(n+1)上取整
  5.  对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
  • 若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
  • 若2i+1<n,左孩子序号:2i+1,否则无左孩子
  • 若2i+2<n,右孩子序号:2i+2,否则无右孩
     

3.如何构造一个二叉树

常见树的表示方式有很多种,如:双亲表示法,孩子表示法,孩子双亲表示法,孩子兄弟表示法等等。

二叉树用代码进行表示时有两种方法,一是通过数组形式的顺序存储,二是类似于链表的链式存储。

顺序存储:将元素存储到数组中,利用二叉树的性质5进行存储,设i为节点在数组中的下标,则:

  • 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
  • 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
  • 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子
     

完全二叉树 

                                                                  一般二叉树

tips: 对于非完全二叉树,则不适合使用顺序方式进行存储,,空间中必须存储空节点,导致空间利用率很低。

链式存储:二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式,具体如下:
 

// 孩子表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
} 

// 孩子双亲表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
Node parent; // 当前节点的根节点
}

4.二叉树的基本操作

二叉树遍历分为四种:

  • 前序遍历——访问根结点--->根的左子树--->根的右子树。
  • 中序遍历——根的左子树--->根节点--->根的右子树。
  • 后序遍历——根的左子树--->根的右子树--->根节点。
  • 层序遍历——第一层--->第二层--->第三层--->...最后一层

下面是用代码分别实现这四种遍历方式的过程。

package Mytree;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User:莎土比亚
 * Date:2024-03-08
 * Time:18:06
 */
public class Mytree {
    class Node{
        public Node(int val) {
            this.val=val;
        }
        int val;
        Node left;
        Node right;
    }
    private int usedsize;
    Node root;
    public Mytree(){
        root=null;
        usedsize=0;
    }
    public void createBinaryTree(){
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node6 = new Node(6);
        root = node1;
        node1.left = node2;
        node2.left = node3;
        node1.right = node4;
        node4.left = node5;
        node5.right = node6;
    }
    // 前序遍历
    public void preOrder(Node root) {
        if(root==null){
            return;
        }
        System.out.print(root.val+" ");
        preOrder(root.left);
        preOrder(root.right);
    }
    // 中序遍历
    public void inOrder(Node root) {
        if(root==null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }
    // 后序遍历
    public void postOrder(Node root){
     if(root==null){
         return;
     }
     postOrder(root.left);
     postOrder(root.right);
     System.out.print(root.val+" ");
    }
    //层序遍历
    public void LeOrder(Node root){
        Queue<Node> list=new LinkedList<>();
        list.add(root);
        while (!list.isEmpty()){
            Node cur=list.poll();
            if(cur!=null){
            System.out.print(cur.val+" ");
            list.add(cur.left);
            list.add(cur.right);
           }
        }
    }

    public static void main(String[] args) {
        Mytree tree=new Mytree();
        tree.createBinaryTree();
        System.out.println("前序遍历");
        tree.preOrder(tree.root);
        System.out.println("\n中序遍历");
        tree.inOrder(tree.root);
        System.out.println("\n后序遍历");
        tree.postOrder(tree.root);
        System.out.println("\n层序遍历");
        tree.LeOrder(tree.root);
    }
}

 二叉树的常见方法:

// 获取树中节点的个数
int size(Node root);

// 获取叶子节点的个数
int getLeafNodeCount(Node root);

// 获取第K层节点的个数
int getKLevelNodeCount(Node root,int k);

// 获取二叉树的高度
int getHeight(Node root);

// 检测值为value的元素是否存在
Node find(Node root, int val);

//层序遍历
void levelOrder(Node root);

// 判断一棵树是不是完全二叉树
boolean isCompleteTree(Node root);

创建一个MyTree类实现一个自己的二叉树并包含上述方法: 

    // 获取树中节点的个数
    int size(Node root) {
        int num=0;
        if(root==null){
            return 0;
        }
        num+=size(root.left);
        num+=size(root.right);
        num++;
        return num;
    }

    // 获取叶子节点的个数
    int getLeafNodeCount(Node root) {
        if(root==null){
            return 0;
        }
        if(root.left==null&&root.right==null) {
            return 1;
        }
        return getLeafNodeCount(root.left)+getLeafNodeCount(root.right);
    }

   //  获取第K层节点的个数
    int getKLevelNodeCount(Node root,int k) {
     if(k==1){
         return 1;
     }
     return getKLevelNodeCount(root.left,k-1)+getKLevelNodeCount(root.right,k-1);
    }

   // 获取二叉树的高度
   int getHeight(Node root) {
        if(root==null){
            return 0;
        }
        return Math.max(getHeight(root.left),getHeight(root.right))+1;
   }

    // 检测值为value的元素是否存在
    Node find(Node root, int val){
        if (root==null){
            return null;
        }
        if(root.val==val){
            return root;
        }
        Node left=find(root.left,val);
        Node right=find(root.right,val);
        if(left!=null){
            return left;
        }
        return right;
    }

    // 判断一棵树是不是完全二叉树
    boolean isCompleteTree(Node root) {
        if(root==null){
            return true;
        }
        if((root.left==null&&root.right!=null)||(root.left!=null&&root.right==null)){
            return false;
        }
        return isCompleteTree(root.left)&&isCompleteTree(root.right);
    }


 

总结:解决二叉树相关问题时要善用子问题思路,将原问题简化为从左子树和右子树中求原始结果,在套用方法递归即可得到问题答案。

5.二叉树相关oj题训练

1.翻转二叉树

2.检查两颗二叉树是否相同

3.判断一颗二叉树是否是平衡二叉树

4.根据一棵树的前序遍历和中序遍历构造二叉树

5.根据二叉树创建字符串

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

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

相关文章

水果小程序有哪些功能 怎么制作

​水果店的小程序&#xff0c;通常都非常受欢迎&#xff0c;而且下单率非常不错。它可以帮助水果商家在线销售水果并提供更好的购物体验。在这篇文章中&#xff0c;我们将介绍水果小程序常见的功能以及制作方法。 1. **商品展示与购买**&#xff1a;水果小程序可以展示各种水…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的体育赛事目标检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发和研究体育赛事目标检测系统对于增强体育分析和观赏体验至关重要。本篇博客详细讲述了如何运用深度学习技术构建一个体育赛事目标检测系统&#xff0c;并提供了完整的实现代码。系统基于先进的YOLOv8算法&#xff0c;对比了YOLOv7、YOLOv6、YOLOv5的性能&a…

linux网络通信(TCP)

TCP通信 1.socket----->第一个socket 失败-1&#xff0c;错误码 参数类型很多&#xff0c;man查看 2.connect 由于s_addr需要一个32位的数&#xff0c;使用下面函数将点分十进制字符串ip地址以网络字节序转换成32字节数值 同理端口号也有一个转换函数 我们的端口号位两个字…

脚踩顺序表

目录 引言 一&#xff1a;顺序表的结构定义 二&#xff1a;顺序表的操作 1.顺序表的初始化 2.顺序表的销毁 3.顺序表数据的打印 4.顺序表的尾插 5.顺序表的头插 6.顺序表的尾删 7.顺序表的头删 8.顺序表的查找 9.顺序表的删除pos位置的值 10.顺序表的在…

微调模型——续(Machine Learning 研习之十三)

集成方法 微调系统的另一种方法是尝试组合性能最佳的模型。 群体&#xff08;或“整体”&#xff09;通常会比最好的单个模型表现得更好&#xff0c;就像随机森林比它们所依赖的单个决策树表现更好一样&#xff0c;特别是当各个模型犯下不同类型的错误时。 例如&#xff0c;您…

瑞_JVM虚拟机_类的生命周期

文章目录 1 JVM虚拟机概述2 类的生命周期2.1 加载阶段2.1.1 加载过程2.1.2 查看内存中的对象&#xff08;hsdb工具&#xff09; 2.2 连接阶段2.2.1 验证2.2.2 准备&#xff08;final特殊&#xff09;2.2.3 解析 2.3 初始化阶段\<client> ★★★2.4 使用阶段2.5 卸载阶段 …

深入理解神经网络

图片怎么被识别的过程 (每层神经网络是数组,会对进来的数据进行加权求和[(weight*数据 然后累加) bias])(激活函数是为了训练weight和bias偏移值,在每个神经网络)(分类器会统计概率分类) 2. 引用链接 https://mp.weixin.qq.com/s?__bizMzIyNjMxOTY0NA&mid2247500124&…

蓝桥集训之鱼塘钓鱼

蓝桥集训之鱼塘钓鱼 核心思想&#xff1a;多路归并 人不会在鱼塘间往返浪费时间将每个鱼塘的取值列出 想要最多鱼 就是每次在最顶上取最大注意&#xff1a;找最大的顺序和实际钓鱼的顺序不同 先在一个坑钓完再去另一个 #include <iostream>#include <cstring>#…

猫头虎分享已解决Bug || 系统监控故障:MonitoringServiceDown, MetricsCollectionError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

把握机遇:2024年游戏行业春招提前批全攻略

当前&#xff0c;国内游戏行业正处于高速发展期&#xff0c;各大游戏公司对应届毕业生的人才需求十分旺盛。这一趋势不仅为即将步入职场的学生们提供了广阔的就业前景&#xff0c;也为游戏产业的创新和多元化发展注入了新鲜血液。 在这样的大环境下&#xff0c;2024年春季提前批…

2024038期传足14场胜负前瞻

2024038期售止时间为3月10日&#xff08;周日&#xff09;20点30分&#xff0c;敬请留意&#xff1a; 本期深盘多&#xff0c;1.5以下赔率3场&#xff0c;1.5-2.0赔率2场&#xff0c;其他场次是平半盘、平盘。本期14场整体难度中等偏上。以下为基础盘前瞻&#xff0c;大家可根据…

数字化转型导师坚鹏:大模型的应用实践(金融)

大模型的应用实践 ——开启人类AI新纪元 打造数字化转型新利器 课程背景&#xff1a; 很多企业和员工存在以下问题&#xff1a; 不清楚大模型对我们有什么影响&#xff1f; 不知道大模型的发展现状及作用&#xff1f; 不知道大模型的针对性应用案例&#xff1f; 课程…

GPT-4-turbo还是大家心中第一!Claude 3竞技场人类投票成绩出炉:仅居第三

Claude 3的竞技场排名终于揭晓了&#xff1a; 在仅仅3天的时间里&#xff0c;20000张投票使得排名的流量达到了前所未有的高度。 最后&#xff0c;Claude 3的"大杯"模型Opus以1233的分数赢得了胜利&#xff0c;成为了第一个能和GPT-4-Turbo匹敌的选手。 "中杯…

VUE Element例子学习

参考:【前端】VueElement UI案例&#xff1a;通用后台管理系统-项目总结_vue elementui 管理系统-CSDN博客 之前参考的el-admin-web太复杂了&#xff0c;不是纯净的demo. 所以找了一圈资料&#xff0c;找到了这个博客&#xff0c;很合适&#xff0c;有例子的代码&#xff0c;…

安全先行,合规的内外网文件摆渡要重点关注什么?

内外网隔离在政府、军工部门、科研单位等已成为很常见的网络安全建设措施&#xff0c;内外网隔离是一种网络安全措施&#xff0c;用于保护内部网络免受外部网络的攻击和威胁。 内外网隔离的目的在于限制内外网之间的通信和数据交换&#xff0c;但网络隔离后&#xff0c;仍有数据…

深入解析汽车MCU的软件架构

一、背景知识 电动汽车&#xff08;EV&#xff09;正在成为首选的交通方式&#xff0c;为传统内燃机汽车提供了一种可持续发展的环保型替代方案。在电动汽车复杂的生态系统中&#xff0c;众多电子控制单元&#xff08;ECU&#xff09;在确保其高效运行方面发挥着至关重要的作用…

ChatGPT 串接到 Discord - 团队协作好助理

ChatGPT 串接到 Discord - 团队协作好助理 ChatGPT 是由 OpenAI 开发的一个强大的语言模型&#xff0c;本篇文章教你如何串接 Discord Bot &#xff0c;协助团队在工作上更加高效并促进沟通与协作。使 ChatGPT 发挥出最大的功效&#xff0c;进一步提升工作效率和团队协作能力。…

Joe主题网站

一款博客网站源码 发现源码为大家内置了主题 清爽又强大真正的永久可用的一条源码&#xff0c;该版本为整合版本&#xff0c;内置了Joe主题&#xff0c;搭建后直接启用即可~ 安装环境要求&#xff1a; PHP 7.2 以上 MySQL, PostgreSQL, SQLite 任意一种数据库支持&#xff0c;…

拿捏算法的复杂度

目录 前言 一&#xff1a;算法的时间复杂度 1.定义 2.简单的算法可以数循环的次数&#xff0c;其余需要经过计算得出表达式 3.记法&#xff1a;大O的渐近表示法 表示规则&#xff1a;对得出的时间复杂度的函数表达式&#xff0c;只关注最高阶&#xff0c;其余项和最高阶…

Linux--gdb(调试工具)

1. 背景 程序的发布方式有两种&#xff0c;debug模式和release模式 Linux gcc/g出来的二进制程序&#xff0c;默认是release模式 要使用gdb调试&#xff0c;必须在源代码生成二进制程序的时候, 加上 -g 选项 2. 命令 gdb binFile 退出&#xff1a; ctrl d 或 quit 调试命令&am…