Java二叉树超详解(常用方法介绍)(2)

news2024/11/19 14:27:54

二叉树中的常用方法

静态二叉树的手动创建

这里我们先给出二叉树结点的信息(这里是内部类):

static class TreeNode {
        public char val;
        public TreeNode left;//左孩子的引用
        public TreeNode right;//右孩子的引用

        public TreeNode(char val) {
            this.val = val;
        }
    }

手动创建的代码如下:

/**
     * 创建一棵二叉树 创建成功后 返回根节点
     * @return
     */
    public TreeNode createTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');

        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;

        return A;
    }

利用new出结点对象和左右指针的连接,我们就可以创建一棵这样的简单二叉树。(给你们画了个图嘿嘿)。

 获取树中结点的个数

这里依然是用到了树的递归的思想:我们可以使用递归左树右树的方法,利用一个常量来记录结点的个数。

 public static int nodeSize;

    /**
     * 获取树中节点的个数:遍历思路
     */
    void size(TreeNode root) {
        if(root == null) {
            return;
        }
        nodeSize++;
        size(root.left);
        size(root.right);
    }

但是更推荐用子问题的思想,即:一棵树的结点个数 = 左树的结点个数 + 右数的节点个数+ 根结点(即1),像这种利用递归方法的返回值计算我们就称为子问题思路。代码如下:

 /**
     * 获取节点的个数:子问题的思路
     *
     * @param root
     * @return
     */
    int size2(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return size2(root.left) + size2(root.right) + 1;
    }

获取叶子结点的个数

求叶子结点,在代码上,就是求含有多少左边和右边都是null的节点数(如果一个节点满足这样的条件就++),像上面一样,先给出遍历的方法:

/*
     获取叶子节点的个数:遍历思路
     */
    public static int leafSize = 0;

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

下面是子问题的思路:

 /*
     获取叶子节点的个数:子问题
     */
    int getLeafNodeCount2(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftCount = getLeafNodeCount2(root.left);
        int rightCount = getLeafNodeCount2(root.right);
//满足条件:即左右返回值+1, 不满足条件:还是给出原来的左右返回值
        return root.right == null && root.right == null ? leftCount + rightCount + 1 : leftCount + rightCount;
    }

获取一棵树第k层的节点数目

还是主要利用递归的思想:一棵树第k层的结点数 = 左树第k-1层的节点数 + 右树第k-1层的节点数。递归的截止条件就是:当k为1时,直接返回1即可,如果为空则返回0。那么所以,代码如下:

/*
    获取第K层节点的个数
     */
    int getKLevelNodeCount(TreeNode root, int k) {
        if(root == null) {
            return 0;
        }
        if(k == 1) {
            return 1;
        }
        //获取左树第k-1层的节点数
        int leftCount = getKLevelNodeCount(root.left, k - 1);
        //获取右树第k-1层的节点数
        int rightCount = getKLevelNodeCount(root.right, k - 1);
        //返回左右树之和
        return leftCount + rightCount;
    }

获取二叉树的深度

还是利用递归的思想(截止条件:为空返回零):一棵二叉树的深度 = 左树和右树之中最高的那一个的深度 + 1.

利用我们之前学的子问题思想和递归,可以轻松写出: 

int getHeight(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        //返回深度最大的那一个的深度 + 1
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

 检查树中是否有值为value的结点(即返回该节点)

还是利用之前的递归思路:value可能在当前结点,也可能在左右树当中,亦有可能不存在。

这里一定要注意一个点:如果已经提前找到了目标节点,就直接返回即可(比如在左树当中找到了,就不用在右树当中找了),这样做可以大大节约时间

 // 检测值为value的元素是否存在
    TreeNode find(TreeNode root, char val) {
        if(root == null) {
            return null;
        }
        if(root.val == val) {
            return root;
        }
        TreeNode ret1 = find(root.left, val);
        //不再去右边了
        if(ret1 != null) {
            return ret1;
        }
        TreeNode ret2 = find(root.right, val);
        if(ret2 != null) {
            return ret2;
        }
        return null;
    }

 层序遍历

层序遍历,在上一篇中我也有讲,就是对一棵树进行从上到下,从左到右的遍历操作。那么如何通过代码来实现这一个过程呢?这时我们就可以用到之前的一个数据结构,也就是队列,那么我们如何利用队列来进行层序遍历呢,我们来看下面:

这里注意一个点,当且仅当左/右儿子不为空时,才能批准放入队列,否则跳过。代码如下: 

 

 //层序遍历(利用队列)
    void levelOrder(TreeNode root) {
        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);
            }
        }
    }

判断一棵树是否为完全二叉树

我们来回顾一下完全二叉树的性质:即完全二叉树是一棵从上到下,从左到右依次排列元素的树(即直到最后一个结点之后,其余前面的结点都不能为空)。所以我们就浮现出了这样的思路,即还是利用队列来遍历树每次,都将左右儿子放入队列(不论是不是空),当遇到了第一个为空的元素后,立刻停止放入,检查队列中剩余的元素是否有非空的,如果全是null,则是完全二叉树,如果有一个不是,就不是完全二叉树,画图如下:

代码如下:

 // 判断一棵树是不是完全二叉树
    boolean isCompleteTree(TreeNode root) {
        if(root == null) {
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            TreeNode cur = queue.poll();

            while(cur == null && !queue.isEmpty()) {
                TreeNode t = queue.poll();
                if(t != null) {
                    return false;
                }
            }
            queue.offer(cur.left);
            queue.offer(cur.right);
        }
        return true;
    }

 

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

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

相关文章

嵌入式系统开发【深入浅出】 UART 与 USART

目录 UART: 通用串行异步收发器 串行通信的时序 8N1&#xff1a;8位数据位 N没有校验位 1停止位1位 中断控制 编程重点 引言&#xff1a; 串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式&#xff0c;并且大部分电子设备都支持该通讯方式&#xff0c;也…

TensorFlow入门(二十一、softmax算法与损失函数)

在实际使用softmax计算loss时,有一些关键地方与具体用法需要注意: 交叉熵是十分常用的,且在TensorFlow中被封装成了多个版本。多版本中,有的公式里直接带了交叉熵,有的需要自己单独手写公式求出。如果区分不清楚,在构建模型时,一旦出现问题将很难分析是模型的问题还是交叉熵的使…

【 数据结构:堆(Heap)】大根堆、小根堆、堆的向上调整算法、向下调整算法 及 堆的功能实现!

前言 本系列文章【数据结构】默认会使用 C/C 进行设计实现&#xff01;其他语言的实现方式请参照分析设计思路自行实现&#xff01; 注[1]&#xff1a;文章属于学习总结&#xff0c;相对于课本教材而言&#xff0c;不具有相应顺序性&#xff01;&#xff08;可在合集中自行查看…

C++: 继承

学习目标 1.继承的概念及定义 2.基类和派生类对象赋值转换(切片) 3.继承中的作用域(隐藏/重定义) 4.派生类的默认成员函数 5.继承与友元 6.继承与静态成员 7.菱形继承与菱形虚拟继承 8.总结 1.继承的概念及定义 1.1概念 继承: 它允许你创建一个新的类&#xff08;称为子类或派…

小程序uView2.X框架upload组件上传方法总结+避坑

呈现效果: 1.1单图片上传 1.2多图片上传 前言:相信很多人写小程序会用到uView框架,总体感觉还算OK吧,只能这么说,肯定也会遇到图片视频上传,如果用到这个upload组件相信你,肯定遇到各种各样的问题,这是我个人总结的单图片和多图片上传方法. uView2.X框架:uView 2.0 - 全面兼容…

JavaSE学习值之--String类

&#x1f495;"不要同情自己&#xff0c;同情自己是卑劣懦夫的勾当&#xff01;"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;JavaSE学习值之--String类 目录 前言&#xff1a; 一.String类 1.String类的属性 2.字符串的构造 注意&#xf…

基于YOLOv8模型的塑料瓶目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的塑料瓶目标检测系统可用于日常生活中检测与定位塑料瓶目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数…

翻译docker官方文档(残缺版)

Build with docker(使用 Docker 技术构建应用程序或系统镜像) Overview (概述) 介绍&#xff08;instruction&#xff09; 层次结构&#xff08;Layers&#xff09; The order of Dockerfile instructions matters. A Docker build consists of a series of ordered build ins…

“高级Vue状态管理 - Vuex的魅力与应用“

目录 引言1. Vuex的简介1.1 什么是Vuex&#xff1f;1.2 Vuex的核心概念 2. Vuex的值获取与改变(综合案例)3. Vuex的异步请求总结 引言 在现代Web开发中&#xff0c;前端应用变得越来越复杂。随着应用规模的扩大和数据流的复杂性增加&#xff0c;有效地管理应用的状态成为了一项…

Android---Synchronized 和 ReentrantLock

Synchronized 基本使用 1. 修饰实例方法 public class SynchronizedMethods{private int sum 0;public synchronized void calculate(){sum sum 1;} } 这种情况下的锁对象是当前实例对象&#xff0c;因此只有同一个实例对象调用此方法才会产生互斥效果&#xff1b;不同的…

APP测试常见功能测试点汇总

1、安装和卸载 安装和卸载是任何一款APP中都属于最基本功能。一旦出错&#xff0c;就属于优先级为紧要的BUG。因此APP的安装和卸载应作为一个测试点多加重视。 1 应用是否可以正常安装&#xff08;命令行安装&#xff1b;豌豆荚&#xff0f;手机助手等第三方软件安装&#xff…

B树、B+树详解

B树 前言   首先&#xff0c;为什么要总结B树、B树的知识呢&#xff1f;最近在学习数据库索引调优相关知识&#xff0c;数据库系统普遍采用B-/Tree作为索引结构&#xff08;例如mysql的InnoDB引擎使用的B树&#xff09;&#xff0c;理解不透彻B树&#xff0c;则无法理解数据…

【通信系列 1 -- GSM 和 LTE】

文章目录 1. LTE(Long Term Evolution)1.1 FDD&TDD简介1.1.1 3G与4G差异1.1.2 频点与band关系1.1.3 band 与运营商的关系 1.2 TDD&FDD区别1.2.1 FDD帧结构1.2.2 TDD帧结构1.2.3 TDD&FDD优势对比1.2.4 TDD缺点 1.3 VoLTE1.3.1 VoLTE 优点11.3.2 VoLTE 优点21.3.3 Vo…

redis-6.2.7 集群安装3主3从

因为资源有限准备了3 台 服务器&#xff0c;先查看防火墙的端口是否开放&#xff0c;如果没有开放先开放端口我使用的 6379 和 6380 这两个端口 所以将这两个端口放开。去redis 官网下载redis 安装包。下载地址 &#xff1a; redis 安装包下载 3. 安装redis 上传上去之后 3 台…

【Java 进阶篇】JavaScript 数据类型详解

JavaScript是一种弱类型脚本语言&#xff0c;具有动态类型。这意味着JavaScript中的变量可以容纳不同类型的数据&#xff0c;并且它们的类型可以在运行时更改。在本文中&#xff0c;我们将深入探讨JavaScript中的数据类型&#xff0c;包括原始数据类型和引用数据类型&#xff0…

TCP/IP(十)TCP的连接管理(七)CLOSE_WAIT和TCP保活机制

一 CLOSE_WAIT探究 CLOSE_WAIT 状态出现在被动关闭方,当收到对端FIN以后回复ACK,但是自身没有发送FIN包之前 ① 服务器出现大量 CLOSE_WAIT 状态的原因有哪些? 1、通常来讲,CLOSE_WAIT状态的持续时间应该很短,正如SYN_RCVD状态2、但是在一些特殊情况下,就会出现大量连接长…

【动态库】Ubuntu 添加动态库的搜索路径

在运行程序时&#xff0c;经常遇到下面这种动态库加载失败的情况&#xff0c;这时往往是系统在动态库的搜索路径下没有找到对应的库文件导致的。 目录 一、使用 LD_LIBRARY_PATH 二、修改 /etc/ld.so.conf 一、使用 LD_LIBRARY_PATH 环境变量 LD_LIBRARY_PATH是动态库的搜索…

【例题】逆波兰表达式求值(图解+代码)

【例题】逆波兰表达式求值(图解代码) 这里写目录标题 【例题】逆波兰表达式求值(图解代码)逆波兰表达式解释优点转换计算代码 题目描述 : 逆波兰表示法是一种将运算符&#xff08;operator&#xff09;写在操作数&#xff08;operand&#xff09;后面的描述程序&#xff08;算式…

1600*A. Linova and Kingdom(DFS优先队列贪心)

Problem - 1336A - Codeforces Linova and Kingdom - 洛谷 解析&#xff1a; 开始认为分情况讨论 k 小于等于叶子结点和大于叶子结点的情况&#xff0c;然后选择深度最深的叶子结点和子孙数量最小的结点&#xff0c;但是发现如果把某一个非叶子结点选取&#xff0c;那么其子孙…

VTP协议

VTP的概念 个人简介 VTP--------------VLAN Trunking protocol VLAN干道协议&#xff08;思科私有协议&#xff09; 同步VLAN编号 VTP&#xff08;Virtual Trunking Protocol&#xff09;是思科&#xff08;Cisco&#xff09;网络设备中的一种协议&#xff0c;用于在交换机之…