【数据结构与算法】详解二叉树以及模拟实现二叉树

news2024/11/17 0:47:38

文章目录

  • 前言:
  • 1.二叉树的定义
  • 2.二叉树的相关术语
  • 3.二叉树的性质
  • 4.特殊的二叉树
  • 5.二叉树的遍历
    • 前序遍历
    • 中序遍历
    • 后序遍历
    • 层序遍历
  • 6.获取树中节点的个数
    • 方法1:遍历思想
    • 方法2:子问题的思想
  • 7.获取叶子节点的个数
    • 方法1:遍历思想
    • 方法2:子问题的思想
  • 8.获取第K层节点的个数
  • 9.获取二叉树的高度
  • 10.检测值为value的元素是否存在
  • 11.判断一棵树是不是完全二叉树

前言:

二叉树在学习数据结构中是一种很重要的类型,也是学习数据结构中比较困难的一种结构,但是在平时用的也是非常多,因此二叉树尤为重要.
本篇文章中会涉及到大量的递归代码,如果一些地方不太理解,可以尝试画图梳理代码执行流程
关于文章中的二叉树源码→点击即可跳转 需要的可以去看一看

1.二叉树的定义

在这里插入图片描述

二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树

2.二叉树的相关术语

①节点:包含一个数据元素及若干指向子树分支的信息 。
②节点的度:一个节点拥有子树的数目称为节点的度 。
③叶子节点:也称为终端节点,没有子树的节点或者度为零的节点。
④分支节点:也称为非终端节点,度不为零的节点称为非终端节点 。
⑤树的度:树中所有节点的度的最大值。
⑥节点的层次:从根节点开始,假设根节点为第1层,根节点的子节点为第2层,依此类推,如果某一个节点位于第L层,则其子节点位于第L+1层。
⑦树的深度:也称为树的高度,树中所有节点的层次最大值称为树的深度。
⑧有序树:如果树中各棵子树的次序是有先后次序,则称该树为有序树。
⑨无序树:如果树中各棵子树的次序没有先后次序,则称该树为无序树。
⑩森林:由m(m≥0)棵互不相交的树构成一片森林。如果把一棵非空的树的根节点删除,则该树就变成了一片森林,森林中的树由原来根节点的各棵子树构成

3.二叉树的性质

性质1:二叉树的第i层上至多有2i-1(i≥1)个节点 。
性质2:深度为h的二叉树中至多含有2h-1个节点。
性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1。
性质4:具有n个节点的满二叉树深为log2n+1。
性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:
当i=1时,该节点为根,它无双亲节点 。
当i>1时,该节点的双亲节点的编号为i/2 。
若2i≤n,则有编号为2i的左节点,否则没有左节点 。
若2i+1≤n,则有编号为2i+1的右节点,否则没有右节点 。

4.特殊的二叉树

1、满二叉树:如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树。
2、完全二叉树:深度为k,有n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时,称为完全二叉树 。
完全二叉树的特点是叶子节点只可能出现在层序最大的两层上,并且某个节点的左分支下子孙的最大层序与右分支下子孙的最大层序相等或大1。

5.二叉树的遍历

二叉树的遍历主要有前序遍历,中序遍历,后序遍历以及层序遍历,前面三种遍历主要是采用递归的方式进行遍历的,如果看不懂,建议画图梳理一下代码执行流程

前序遍历

        public void preOrder(TreeNode root) {
            if(root == null){
                return;
            }
            System.out.print(root.val+" ");
            preOrder(root.left);
            preOrder(root.right);
        }

中序遍历

        public void inOrder(TreeNode root) {
            if(root == null){
                return;
            }
            inOrder(root.left);
            System.out.print(root.val+" ");
            inOrder(root.right);
        }

后序遍历

        void postOrder(TreeNode root) {
            if(root == null){
                return;
            }
            postOrder(root.left);
            postOrder(root.right);
            System.out.print(root.val+" ");
        }

层序遍历

    public void levelOrder(TreeNode root) {
        if (root == null){
            return;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode cur = queue.poll();
            System.out.print(cur.val + " ");
            if (cur.left != null){
                queue.offer(cur.left);
            }
            if (cur.right != null){
                queue.offer(cur.right);
            }

        }
    }

6.获取树中节点的个数

方法1:遍历思想

    public static int nodeSize = 0;// 记录二叉树的结点个数

    public void size(TreeNode root) {
        if(root == null){
            return;
        }
        nodeSize++;
        size(root.left);
        size(root.right);
    }

方法2:子问题的思想

    public int size2(TreeNode root) {
        if(root == null){
            return 0;
        }
        // 难点 -> 画图 在叶子结点时 会返回1 根是最后加上的
        return size2(root.left)+size2(root.right)+1;
    }

7.获取叶子节点的个数

叶子结点的左子树和右子树都为null,因此只要看哪些结点符合条件的就可以了

方法1:遍历思想

    public static int leafSize = 0;

    public void getLeafNodeCount1(TreeNode root) {
        if(root == null){
            return;
        }
        if(root.left == null && root.right == null){
            leafSize++;
        }
        getLeafNodeCount1(root.left);
        getLeafNodeCount1(root.right);
    }

方法2:子问题的思想

    public int getLeafNodeCount2(TreeNode root) {
        if(root == null){
            return 0;
        }
        if(root.left == null && root.right == null){
            return 1;
        }
        return getLeafNodeCount2(root.left)+getLeafNodeCount2(root.right);
    }

8.获取第K层节点的个数

    public int getKLevelNodeCount(TreeNode root, int k) {
        if(root == null){
            return 0;
        }
        if(k <= 0){
            return 0;
        }
        if(k == 1){
            return 1;
        }
        k--;
        return getKLevelNodeCount(root.left,k) + getKLevelNodeCount(root.right,k);
    }

9.获取二叉树的高度

    public int getHeight(TreeNode root) {
        if(root == null){
            return 0;
        }
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        return leftHeight > rightHeight ? leftHeight+1 : rightHeight+1;
    }

10.检测值为value的元素是否存在

这里实现的时候返回值设置的是TreeNode,如果不喜欢可以换成boolean

    public TreeNode find(TreeNode root, char val) {
        if(root == null) {
            return null;
        }
        if(root.val == val){
            return root;
        }
        TreeNode treeNode1 = find(root.left,val);
        if(treeNode1 != null){
            return treeNode1;
        }
        TreeNode treeNode2 = find(root.right,val);
        if(treeNode2 != null){
            return treeNode2;
        }
        return null;
    }

11.判断一棵树是不是完全二叉树

判断是不是完全二叉树,这里使用的是队列做的.也涉及到分层遍历的思想,将每层的结点以及最后一层结点的孩子结点存下来,然后对第一次出现null时后的数据开始进行判断.如果是完全二叉树,那么最后一次遍历的数据都是null,如果不是完全二叉树,那么就是结点和null并存的情况,而且一定是在结点前有个null

    public boolean isCompleteTree(TreeNode root) {
        if (root == null) {
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
            TreeNode cur = queue.poll();
            if (cur != null){
                queue.offer(cur.left);
                queue.offer(cur.right);
            }else {
                break;
            }
        }
        while (!queue.isEmpty()) {
            TreeNode ret = queue.poll();
            if (ret != null){
                return false;
            }
        }
        return true;
    }

在这里插入图片描述

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

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

相关文章

链表(LinkedList)

链表(LinkedList) 链表是有序的列表&#xff0c;但是其在内存的存储不一定连续 由这张图我们可以看出 链表是以节点的方式来存储的&#xff0c;是链式存储 每个节点包含data域&#xff0c;next域&#xff1a;指向下一个节点 我们可以发现链表的各个节点不一定是连续存储的 …

再见了HDMI Alt

点击上方“LiveVideoStack”关注我们▲扫描图中二维码或点击阅读原文▲了解音视频技术大会更多信息编者按&#xff1a;其实在未能推出配套线缆和适配器的那一刻&#xff0c;HDMI Alt模式就已经没有未来了。HDMI已全面落后DisplayPort。本文来自Arstechnica。文/Scharon Hardin…

【SpringCloud】Nacos的安装与启动

【SpringCloud】Nacos的安装与启动 一、下载安装包 二、解压 三、端口配置 四、启动 五、访问 【SpringCloud】Nacos的安装与启动 一、下载安装包 在Nacos的GitHub页面&#xff0c;提供有下载链接&#xff0c;可以下载编译好的Nacos服务端或者源代码&#xff1a; GitHu…

分享149个PHP源码,总有一款适合您

PHP源码 分享149个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 149个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1RKnEbbhpfUndnMrxG8rSIQ?pwd0nqp 提取码&#x…

LINUX学习之文本编辑器VIM/VI(八)

简介 VI 是 Unix 操作系统和类 Unix 操作系统中最通用的文本编辑器 VIM 编辑器是从 VI 发展出来的一个性能更强大的文本编辑器。可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计 VIM 与 VI 编辑器完全兼容 模式转换 如下图所示&#xff0c;一般模式下输入i、…

选出相似的文本按照相似度排序difflib.get_close_matches

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 选出相似的文本 按照相似度排序 difflib.get_close_matches 选择题 对于以下python代码表述错误的是? from difflib import get_close_matches myText"python" myList[&…

23种设计模式(十六)——备忘录模式【状态变化】

备忘录模式 文章目录 备忘录模式意图什么时候使用备忘录真实世界类比备忘录模式的实现备忘录模式的优缺点亦称:调解人、控制器、Intermediary、Controller、Mediator 意图 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将这个对…

穿越万年的轮回[期望dp]

首先我们设置dpi,0/1,0/1dp_{i,0/1,0/1}dpi,0/1,0/1​表示经过iii次操作之后开头为red/edrred/edrred/edr&#xff0c;结尾为red/edrred/edrred/edr的串的期望redredred字符串个数。 然后我们考虑转移&#xff1a; 首先我们要来思考一下期望的本质&#xff0c;这样一个状态&…

「Python|VS Code」如何在VS Code中使用Jupyter Notebook运行Python代码

本文主要介绍如何在VS Code安装Jupyter Notebook的扩展并创建notebook文件&#xff0c;并在notebook中运行python代码。同时&#xff0c;介绍使用Jupyter notebook运行python代码的好处。 文章目录安装Jupyter notebook扩展并运行代码Jupyter notebook的好处安装Jupyter notebo…

第十章 STM32F103+ESP8266接入机智云 实现小型IOT智能家居项目

前言 最近有不少小伙伴私信留言&#xff0c;想要我推出一章能够通过APP进行远程控制并获取传感器信息的实验教程。说实话在嵌入式毕设里边&#xff0c;这算是中等偏上水平的了。刚好我也有兴趣写写。全篇4700多字&#xff0c;我写的很详细&#xff0c;按着文章一步一步操作即可…

RabbitMQ的基础学习(上)

前言&#xff1a; RabbitMQ是一个基于AMQP规范实现的消息队列。它具有性能好、高可用、跨平台性、社区活跃等优点&#xff0c;比较适合中小型公司使用。掌握RabbitMQ相关知识&#xff0c;对工作和学习都有帮助。下面我讲详细介绍一下Rabbit的相关知识。 正文&#xff1a; 一、…

【机器学习】缺失值的处理方法总结

目录&#xff1a;缺失值的处理一、总录二、引言三、数据缺失的原因四、数据缺失的类型五、数据缺失的处理方法5.1 删除记录5.2 数据填充5.2.1 替换缺失值5.2.2 拟合缺失值5.2.3 虚拟变量5.3 不处理六、实证演练七、小结一、总录 二、引言 业界广泛流传这样一句话&#xff1a;数…

java构造器2023021

构造器&#xff1a; 构造器是一个特殊的方法&#xff0c;用于创建实例时执行初始化。构造器是创建对象的重要途径&#xff08;即使使用工厂模式、反射等方式创建对象&#xff0c;其实质依然是依赖于构造器&#xff09;&#xff0c;因此Java类必须包含一个或一个以上的构造器。 …

23种设计模式(十五)——适配器模式【接口隔离】

文章目录 意图什么时候使用适配器真实世界类比适配器模式的实现适配器模式的优缺点亦称:封装器模式、Wrapper、Adapter 意图 将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器。 什么时候使用适配器 1、系统需要使用现有的类,而这…

软件设计到底是什么?

软件设计是什么&#xff1a; 就是讨论要用什么技术实现功能&#xff1f;就是要考虑选择哪些框架和中间件&#xff1f;设计就是设计模式&#xff1f;设计就是Controller、Service加Model&#xff1f;…… 一百个程序员&#xff0c;就有一百种理解。若按照这些方式去了解“软件…

Java设计模式中状态模式介绍/状态模式怎么使用

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 6.6 状态模式 6.6.1定义 对有状态的对象&#xff0c;把复杂的"判断逻辑"提取到不同的状态对象中&#xff0c;允许状态对象在其内部状态发生改变时改变…

【C++】哈希表 | 闭散列 | 开散列 | unordered_map 和 unordered_set 的模拟实现

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;unordere…

Ubuntu20.04/22.04 安装 Arduino IDE 2.x

这周收到两片基于LGT8F328P LQFP32的Arduino Mini EVB, 机器上没有 Arduino 环境需要新安装, 正好感受一下新出的 Arduino IDE 2.x, 记录一下 Ubuntu 20.04/22.04 下安装 Arduino IDE 2.x 的过程. 下载解压 下载 访问 Arduino 的官网下载 https://www.arduino.cc/en/softwar…

2021-04-12

今天在练习自定义标题栏&#xff08;Android初级开发&#xff08;四&#xff09;——补充3&#xff09;的过程中遇到了隐藏系统自带标题栏的问题&#xff0c;现将几种去掉系统自带标题栏的方式做一总结。大体上可以分为两种方式&#xff0c;一种是修改xml文件&#xff08;这种方…

第六层:继承

文章目录前情回顾继承继承的作用继承的基本语法继承方式公共继承保护继承私有继承继承中的对象模型继承中的构造和析构顺序继承中同名成员访问非静态成员静态成员多继承语法注意多继承中的对象模型多继承父类成员名相同菱形继承概念菱形继承出现的问题虚继承步入第七层本章知识…