二叉树入门算法题详解

二叉树入门题目详解

首先知道二叉树是什么:

代码随想录 (programmercarl.com)
了解后知道其实二叉树就是特殊的链表,只是每个根节点节点都与两个子节点相连而其实图也是特殊的链表,是很多节点互相连接;这样说只是便于理解和定义,严格来说他们都是不同的数据结构了,在使用中还是要牢记各种数据结构的性质的。

二叉树的存储方式

链式的:

java

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

对于二叉树一定会第一时间想到递归,他们俩是相辅相成,互相存在的

就比如说简单的二叉树遍历就用到了递归

前序遍历:中左右

中序遍历:左中右

后序遍历:左右中

二叉树的递归遍历

// 前序遍历·递归·LC144_二叉树的前序遍历
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {
        if (root == null) {
            return;
        }
        result.add(root.val);
        preorder(root.left, result);
        preorder(root.right, result);
    }
}
// 中序遍历·递归·LC94_二叉树的中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        inorder(root.left, list);
        list.add(root.val);             // 注意这一句
        inorder(root.right, list);
    }
}
// 后序遍历·递归·LC145_二叉树的后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorder(root, res);
        return res;
    }

    void postorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        postorder(root.left, list);
        postorder(root.right, list);
        list.add(root.val);             // 注意这一句
    }
}

二叉树的层序遍历

其实可以说前边的递归遍历是深度遍历,那么层序就是字面意思,一层一层遍历,如何能一层一层遍历呢,我们想到了队列,以及父节点和子节点的关系,即先有父节点,后有子节点,子节点连着父节点,所以

当使用队列时,可以先把父节点放进去,有父节点就可以找出对应的子节点,把父节点的子节点也放进去,再把父节点输出出去,队列中剩下的就是子节点,再找到对应的子子节点,这时就可以把子节点输出出去,聪明点的已经发现了,我们现在输出的顺序就像是层序遍历,是的,层序就是这么一个过程。

这中间需要有一个用来描述此时这层节点个数的变量来中转,因为每层的节点个数都不同,

分为递归和借助队列两种方式

借助队列:更容易理解上边的过程:

class Solution {
    public List<List<Integer>> resList = new ArrayList<List<Integer>>();

    public List<List<Integer>> levelOrder(TreeNode root) {
  
        checkFun02(root);

        return resList;
    }  
//BFS--迭代方式--借助队列
    public void checkFun02(TreeNode node) {
        if (node == null) return;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(node);

        while (!que.isEmpty()) {
            List<Integer> itemList = new ArrayList<Integer>();
            int len = que.size();

            while (len > 0) {
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);

                if (tmpNode.left != null)  que.offer(tmpNode.left);
                if (tmpNode.right != null) que.offer(tmpNode.right);
                len--;
            }

            resList.add(itemList);
        }

    }

翻转二叉树:

其实就是使用递归先把二叉树的每个左右节点都交换一遍,再遍历一遍,得到的新的就是翻转的

java

class Solution {
   /**
     * 前后序遍历都可以
     * 中序不行,因为先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子),再右孩子交换孩子(此时其实是对原来的左孩子做交换)
     */
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        //以后序遍历为例
        invertTree(root.left);   //左
        invertTree(root.right);  //右
        swapChildren(root);      //中
        //虽然先遍历左右子树,但第三步到根节点时会改变其左右子树,所以仍然正确,先写中,左右,其实效果是一样的;
        return root;             
    }

    private void swapChildren(TreeNode root) {
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
    }
}

对称二叉树

image-20240216202053231

如图

我们可以想象一下后序遍历时,

左侧:5 6 3 4 2 右侧: 4 6 5 3 2 中:1

再结合图片看待,对于左子树其实是按照左右中遍历,

右子树其实可以看做左子树按照右左中的顺序遍历;

所以对于对称二叉树,可以使用后序遍历左子树,并将右子树再按照右左中的顺序遍历,若两者结果一样,那么可以判断是对称二叉树;(找规律,细微之处见斟酌)(想法很不错,现实很骨感,只有后序是不能唯一确认二叉树的);

而对于代码怎么写,是比上边的推理要麻烦一些的,因为,即使两个子树不同,只有先序遍历一种写法,是没办法唯一确认一颗二叉树的!!!

所以我们需要另想办法:再观察,其实递归遍历左子树和右子树,是将每次递归中的左右子树的左右子树对称点的值是否相同,若相同其结果就一样,所以代码条件就变成了,递归遍历二叉树并且每次递归都是带上左右子树,每次都遍历到对称的两边,左节点的子节点应始终和右节点的子节点对称,即:若左为空,右也为空,不为空时,左值应=右值;(过瘾)

java:
(每次先看了c++的代码,再看java写这种链表和二叉树的题目代码是真的复杂。。。)

class Solution {
    public boolean isSymmetric(TreeNode root) {

        return compare(root.left, root.right);
    }

    private boolean compare(TreeNode left, TreeNode right) {

        if (left == null && right != null) {
            return false;
        }
        if (left != null && right == null) {
            return false;
        }

        if (left == null && right == null) {
            return true;
        }
        if (left.val != right.val) {
            return false;
        }
        // 比较外侧
        boolean compareOutside = compare(left.left, right.right);
        // 比较内侧
        boolean compareInside = compare(left.right, right.left);
        return compareOutside && compareInside;
    }
}

参考c++的:

class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right) {
        // 首先排除空节点的情况
        if (left == NULL && right != NULL) return false;
        else if (left != NULL && right == NULL) return false;
        else if (left == NULL && right == NULL) return true;
        // 排除了空节点,再排除数值不相同的情况
        else if (left->val != right->val) return false;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
        bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
        bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)
        return isSame;
    }
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }
};

二叉树的最大深度

image-20240216214740832

怎么求呢?直接使用递归求出根节点对应的最大高度就是该二叉树的最大深度啦

java

class solution {
    /**
     * 递归法
     */
    public int maxDepth(TreeNode root) {
        //空节点高度为0,置为return 0,则叶子结点高度一定都为1,越往上的父节点每次都加1;
        if (root == null) {
            return 0;
        }
        int leftDepth = maxDepth(root.left);    //左
        int rightDepth = maxDepth(root.right);  //右
        return Math.max(leftDepth, rightDepth) + 1; //中:高度为左右中的最大值+1;
    }
}

二叉树的最小深度

方法和求最大深度有点像,又不太一样;

当只把最大改最小时会犯以下的错误。需要对节点的字节点都进行判断,当两子节点都为空时才是叶子节点;

易错点:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

class Solution {
    /**
     * 递归法
     */
    public int minDepth(TreeNode root) {
        //空节点高度为0,置为return 0,则叶子结点高度一定都为1,越往上的父节点每次都加1;
        if (root == null) {//意思是左右节点都为空
            return 0;
        }
        int leftDepth = minDepth(root.left);    //左
        int rightDepth = minDepth(root.right);  //右
        if (root.left==null){// 只有左节点为空,返回的最小值只能为右节点+1
            return rightDepth+1;
        }
        if(root.right ==null){//只有右节点为空,返回的最小值只能为左节点+1
            return leftDepth+1;
        }
        return Math.min(leftDepth, rightDepth) + 1; //中:高度为左右中的最小值+1;
    //
    }
}

完全二叉树的节点个数

image-20240217093540254

image-20240217093604229

仔细思考,这道题其实和求二叉树的最大深度很类似,只不过那道题是取左右深度的最大值,这个就可以写成求左右深度+1(就是这个子树的节点个数了)

class solution {
    /**
     * 递归法
     */
    public int Number(TreeNode root) {
        //空节点高度为0,置为return 0,则叶子结点高度一定都为1,越往上的父节点每次都加1;
        if (root == null) {
            return 0;
        }
        int leftNumber = Number(root.left);    //左
        int rightNumber = Number(root.right);  //右
        return leftNumber+rightNumber + 1; //中:高度为左右中的最大值+1;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安卓TextView 拖动命名

需求&#xff1a;该布局文件使用线性布局来排列三个文本视图和一个按钮&#xff0c;分别用于显示两个动物名称以及占位文本视图。在占位文本视图中&#xff0c;我们为其设置了背景和居中显示样式&#xff0c;并用其作为接收拖放操作的目标 效果图&#xff1b; 实现代码 第一布…

大数据02-数据仓库

零、文章目录 大数据02-数据仓库 1、数据仓库介绍 &#xff08;1&#xff09;基本概念 数据仓库&#xff0c;英文名称为Data Warehouse&#xff0c;可简写为DW或DWH。数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;为企业提供决策支持&#xff08;Decision Sup…

牛客网SQL进阶123:高难度试卷的得分的截断平均值

官网链接&#xff1a; SQL类别高难度试卷得分的截断平均值_牛客题霸_牛客网牛客的运营同学想要查看大家在SQL类别中高难度试卷的得分情况。 请你帮她从exam_。题目来自【牛客题霸】https://www.nowcoder.com/practice/a690f76a718242fd80757115d305be45?tpId240&tqId2180…

[Android]Frida-hook环境配置

准备阶段 反编译工具:Jadx能够理解Java语言能编写小型的JavaScript代码连接工具:adb设备:Root的安卓机器&#xff0c;或者模拟器 Frida&#xff08;https://frida.re/&#xff09; 就像是你计算机或移动设备的妙妙工具。它帮助你查看其他程序或应用内部发生的事情&#xff0…

OpenAI发布文生视频大模型Sora

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 一觉醒来发现自己快失业了&#xff0c;Open AI又放大招了。没有任何消息&#xff0c;没有任何预热&#xff0c;直接王炸。 OpenAI突然发布文生视频大模型Sora&#xff0c;生成一段长达1分钟的高清流畅视频。它能模…

机器学习8-决策树

决策树&#xff08;Decision Tree&#xff09;是一种强大且灵活的机器学习算法&#xff0c;可用于分类和回归问题。它通过从数据中学习一系列规则来建立模型&#xff0c;这些规则对输入数据进行递归的分割&#xff0c;直到达到某个终止条件。 决策树的构建过程&#xff1a; 1.…

selenium定位元素报错:‘WebDriver‘ object has no attribute ‘find_element_by_id‘

Selenium更新到 4.x版本后&#xff0c;以前的一些常用的代码的语法发生了改变 from selenium import webdriver browser webdriver.Chrome() browser.get(https://www.baidu.com) input browser.find_element_by_id(By.ID,kw) input.send_keys(Python)目标&#xff1a;希望通…

Python静态方法和类方法的区别和应用

实际上&#xff0c;Python 完全支持定义类方法&#xff0c;甚至支持定义静态方法。Python 的类方法和静态方法很相似&#xff0c;它们都推荐使用类来调用&#xff08;其实也可使用对象来调用&#xff09;。 类方法和静态方法的区别在于&#xff0c;Python会自动绑定类方法的第…

第三节:基于 InternLM 和 LangChain 搭建你的知识库(课程笔记)

视频链接&#xff1a;https://www.bilibili.com/video/BV1sT4y1p71V/?vd_source3bbd0d74033e31cbca9ee35e111ed3d1 文档地址&#xff1a; https://github.com/InternLM/tutorial/tree/main/langchain 课程笔记&#xff1a; 1.仅仅包含训练时间点之前的数据&#xff0c;无法…

Redis篇----第一篇

系列文章目录 文章目录 系列文章目录前言一、什么是 Redis?二、Redis 与其他 key-value 存储有什么不同?三、Redis 的数据类型?四、使用 Redis 有哪些好处?五、Redis 相比 Memcached 有哪些优势?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住…

wordpress外贸成品网站模板

首页大图slider轮播&#xff0c;橙色风格的wordpress外贸网站模板 https://www.zhanyes.com/waimao/6250.html 蓝色经典风格的wordpress外贸建站模板 https://www.zhanyes.com/waimao/6263.html

智能汽车专题:智能驾驶2023年度报告

今天分享的是智能汽车系列深度研究报告&#xff1a;《智能汽车专题&#xff1a;智能驾驶2023年度报告》。 &#xff08;报告出品方&#xff1a;量子位智库&#xff09; 报告共计&#xff1a;30页 来源&#xff1a;人工智能学派 顶天立地&#xff1a;技术进阶路线明晰 根据…

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G题解

题目 在一个果园里&#xff0c;多多已经将所有的果子打了下来&#xff0c;而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并&#xff0c;多多可以把两堆果子合并到一起&#xff0c;消耗的体力等于两堆果子的重量之和。可以看出&#xff0c;所…

物联网技术的崛起:驱动智慧景区的新篇章

随着科技的飞速发展&#xff0c;物联网技术逐渐成为推动各行各业创新的重要力量。在旅游业中&#xff0c;物联网的应用为智慧景区的建设提供了有力支持&#xff0c;为游客带来了更加便捷、智能的旅游体验。本文将探讨物联网技术在智慧景区中的应用及其对旅游业的影响&#xff0…

vue3-深入响应式系统

Vue 最标志性的功能就是其低侵入性的响应式系统。组件状态都是由响应式的 JavaScript 对象组成的。当更改它们时&#xff0c;视图会随即自动更新。这让状态管理更加简单直观&#xff0c;但理解它是如何工作的也是很重要的&#xff0c;这可以帮助我们避免一些常见的陷阱。在本节…

DDD爱好者通病-《软件方法》自测题解析37

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 《软件方法》第5章自测题2 5 [ 单选题 ] 我们经常会听到有人说“系统分为几个功能模块”。针对“功能模块”&#xff0c;以下说法正确的是&#xff1a;  A) 它把外部和内部混在一…

蓝桥杯:C++二分算法

在基本算法中&#xff0c;二分法的应用非常广泛&#xff0c;它是一种思路简单、编程容易、效率极高的算法。蓝桥杯软件类大赛中需要应用二分法的题目很常见。 二分法有整数二分和实数二分两种应用场景 二分法的概念 二分法的概念很简单&#xff0c;每次把搜索范围缩小为上一…

Linux POSIX信号量 线程池

Linux POSIX信号量 线程池 一. 什么是POSIX信号量&#xff1f;二. POSIX信号量实现原理三. POSIX信号量接口函数四. 基于环形队列的生产消费模型五. 线程池 一. 什么是POSIX信号量&#xff1f; POSIX信号量是一种用于同步和互斥操作的机制&#xff0c;属于POSIX&#xff08;Po…

WINCC如何新增下单菜单,切换显示页面

杭州工控赖工 首先我们先看一下&#xff0c;显示的效果&#xff0c;通过下拉菜单&#xff0c;切换主显示页面。如图一&#xff1a; 图1 显示效果 第一步&#xff1a; 通过元件新增一个组合框&#xff0c;见图2&#xff1b; 组合框的设置&#xff0c;设置下拉框的长宽及组合数…

[java基础揉碎]数组 值拷贝和引用拷贝的赋值方式

目录 数组的介绍 为什么有数组 数组的三种使用方式 动态初始化: 静态初始化: 数组使用注意事项和细节 值拷贝和引用拷贝的赋值方式 数组反转: 数组拷贝: 数组的介绍 数组可以存放多个同一类型的数据。数组也是一种数据类型&#xff0c;是引用类型。 即&#xff1a;数组…