BAT 经典算法笔试题: 镜像二叉树

二叉树经典算法题详解(灰常详细) 二叉树经典算法题解析 阅读详情

再过不到 2 个月,互联网行业就要再次迎来面试高峰了。为了让大家能顺利通过所有面试环节必经的笔试阶段,我提前给大伙准备了一套常见的算法笔试题。这套算法题来源于 LeetCode,题目都是 BAT、京东头条滴滴美团等大型互联网公司都喜欢考的题目。

算法这个东西很难,纵使你了解了其中的逻辑,用代码写出来仍然不是一件容易的事,内部有太多的细节需要处理。为了便于大伙轻松理解算法逻辑,对于所有的题目,我会使用图文加动画的形式进行讲解,让读者可以轻松理解算法逻辑的同时,还可以留下深刻的影像不容易遗忘。

640?wx_fmt=png

好,下面我们开始今天的算法题:镜像二叉树,就是将一颗二叉树上的左右节点全部交换,就好比镜子里的二叉树,左右方向是反过来的。

二叉树节点表示

640?wx_fmt=png

class Node<T> {
    T value;
    Node<T> left;
    Node<T> right;

    Node(T value) {
        this.value = value;
    }

    Node(T value, Node<T> left, Node<T> right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }
}

,可以容纳各种值类型。

构造二叉树

640?wx_fmt=png

Node<Integer> node2 = new Node<>(2);
Node<Integer> node3 = new Node<>(3);
Node<Integer> node1 = new Node<>(1, node2, node3);

// 一次性构造
Node<Integer> node1 = new Node<>(1, new Node<>(2), new Node<>(3));

呈现二叉树结构

如果我们正确写出了镜像算法,那如何来直观验证镜像的结构是否正确呢?LeetCode 使用的是单元测试,它使用一系列的单元测试和压力测试脚本代码来验证用户编写的算法的正确性和性能。但是我们不要这样做,因为不直观。我们选择对二叉树的结构内容进行直观的呈现,如此就可以使用肉眼来进行快速验证。如何直观呈现呢?我们使用最简单的括号表示法,它并不是最直观的,但是它易于实现。

640?wx_fmt=png

class Node<T> {
  T value;
  Node<T> left;
  Node<T> right;

  public String toString() {
    // 叶节点
    if (left == right) {
        return String.format("[%d]", value);
    }
    // 中间节点
    return String.format("(%d, %s, %s)", value, left, right);
  }
}

Node<Integer> node2 = new Node<>(2);
Node<Integer> node3 = new Node<>(3);
Node<Integer> node1 = new Node<>(1, node2, node3);

System.out.println(node1);
System.out.println(node2);
System.out.println(node3);
---------------------
[2]
[3]
(1, [2], [3])

递归镜像二叉树

镜像二叉树有两种算法,一种是递归,一种是迭代。递归的算法简单易于理解,我们先使用递归算法来求解。递归的思想就是深度遍历,遇到一个节点,先递归镜像它的左子树,再递归镜像它的右子树,然后再交换自己的左右子树。如果遇到的是叶子节点,就不必处理了。为了避免无限递归,一定要及时设置好递归的停止条件,在这里停止条件就是遇到了叶节点。

640?wx_fmt=png

public void mirrorFrom(Node<T> node) {
    // 叶子结点
    if (node.left == node.right) {
        return;
    }
    // 递归镜像左子树
    if (node.left != null)
        mirrorFrom(node.left);
    // 递归镜像右子树
    if (node.right != null)
        mirrorFrom(node.right);
    // 交换当前节点的左右子树
    Node<T> tmp = node.left;
    node.left = node.right;
    node.right = tmp;
}

迭代镜像二叉树

递归算法的优势在于逻辑简单,缺点在于每一次递归调用函数都会增加一个新的函数堆栈,如果树的深度太深,函数的堆栈内存就会持续走高,一不小心就会触发臭名昭著的异常 StackOverflowException。如果二叉树分布比较均匀,那么树就不会太深,但是遇到偏向的二叉树,比如所有的子节点都挂在了右节点上,二叉树就退化成了线性链表,链表的长度就是树的深度,那这颗树的深度就比较可怕了。

640?wx_fmt=png

所以下面我来介绍第二种算法 —— 迭代算法。迭代的基本思想就是将递归算法转换成循环算法,用一个 for 循环来交换所有节点的左右子树。我们需要再重新理解一下算法的目标,这个目标非常简单,就是遍历整颗二叉树,将遍历途中遇到的所有中间节点的左右指针交换一下。

那如何设计这个循环呢?一个很明显的方法是分层循环,第一次循环处理第 1 层二叉树节点,也就是唯一的根节点。下一个循环处理第 2 层二叉树节点,也就是根节点的两个儿子。如此一直处理到最底层,循环的终止条件就是后代节点没有了。所以我们需要使用一个容器来容纳下一次循环需要处理的后代节点。

640?wx_fmt=png

public MirrorBinaryTree<T> mirrorByLoop() {
    // 空树不必处理
    if (root == null) {
        return this;
    }
    // 当前循环需要处理的节点
    LinkedList<Node<T>> expandings = new LinkedList<>();
    expandings.add(root);
    // 没有后台节点就可以终止循环
    while (!expandings.isEmpty()) {
        // 下一次循环需要处理的节点
        // 也就是当前节点的所有儿子节点
        LinkedList<Node<T>> nextExpandings = new LinkedList<>();
        // 遍历处理当前层的所有节点
        for (Node<T> node : expandings) {
            // 将后代节点收集起来,留着下一次循环
            if (node.left != null) {
                nextExpandings.add(node.left);
            }
            if (node.right != null) {
                nextExpandings.add(node.right);
            }
            // 交换当前节点的左右指针
            Node<T> tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        }
        // 将后代节点设置为下一轮循环的目标节点
        expandings = nextExpandings;
    }
    return this;
}

完整代码

下面的完整代码可以拷贝过去直接运行,如果读者还是不够明白欢迎在留言区及时提问。

package leetcode;

import java.util.LinkedList;

public class MirrorBinaryTree<T> {

    static class Node<T> {
        T value;
        Node<T> left;
        Node<T> right;

        Node(T value) {
            this.value = value;
        }

        Node(T value, Node<T> left, Node<T> right) {
            this(value);
            this.left = left;
            this.right = right;
        }

        public String toString() {
            if (left == right) {
                return String.format("[%d]", value);
            }
            return String.format("(%d, %s, %s)", value, left, right);
        }

    }

    private Node<T> root;

    public MirrorBinaryTree(Node<T> root) {
        this.root = root;
    }

    public MirrorBinaryTree<T> mirrorByLoop() {
        if (root == null) {
            return this;
        }
        LinkedList<Node<T>> expandings = new LinkedList<>();
        expandings.add(root);
        while (!expandings.isEmpty()) {
            LinkedList<Node<T>> nextExpandings = new LinkedList<>();
            for (Node<T> node : expandings) {
                if (node.left != null) {
                    nextExpandings.add(node.left);
                }
                if (node.right != null) {
                    nextExpandings.add(node.right);
                }
                Node<T> tmp = node.left;
                node.left = node.right;
                node.right = tmp;
            }
            expandings = nextExpandings;
        }
        return this;
    }

    public MirrorBinaryTree<T> mirrorByRecursive() {
        mirrorFrom(root);
        return this;
    }

    public void mirrorFrom(Node<T> node) {
        if (node.left == node.right) {
            return;
        }

        if (node.left != null)
            mirrorFrom(node.left);
        if (node.right != null)
            mirrorFrom(node.right);

        Node<T> tmp = node.left;
        node.left = node.right;
        node.right = tmp;
    }

    public String toString() {
        if (root == null) {
            return "()";
        }
        return root.toString();
    }

    public static void main(String[] args) {
        Node<Integer> root = new Node<>(
                1, 
                new Node<>(
                        2, 
                        new Node<>(4), 
                        new Node<>(
                                5, 
                                new Node<>(8),
                                null)),
                new Node<>(
                        3, 
                        new Node<>(
                                6, 
                                null, 
                                new Node<>(9)), 
                        new Node<>(7)));
        MirrorBinaryTree<Integer> tree = new MirrorBinaryTree<>(root);
        System.out.println(tree);
        tree.mirrorByRecursive();
        System.out.println(tree);
        tree.mirrorByLoop();
        System.out.println(tree);
    }

}
---------------
(1, (2, [4], (5, [8], null)), (3, (6, null, [9]), [7]))
(1, (3, [7], (6, [9], null)), (2, (5, null, [8]), [4]))
(1, (2, [4], (5, [8], null)), (3, (6, null, [9]), [7]))

640?wx_fmt=png

别再一个个BAPI调了!用CVI_EI_INBOUND_MAIN一次性搞定SAP BP主数据(附完整ABAP代码) 本文详细介绍了如何使用SAP标准函数CVI_EI_INBOUND_MAIN高效维护业务伙伴(BP)主数据,替代传统的多BAPI调用方式。通过完整ABAP代码示例,展示了如何一站式处理供应商、客户、公司代码等所有视图的创建与更新,提升数据一致性和开发效率。 阅读详情

相关推荐

英飞凌TC3xx实战:PWM中点触发ADC采集+DMA自动搬运完整配置流程(基于ILLD库)

本文详细介绍了在英飞凌TC3xx系列微控制器上使用ILLD库实现PWM中点触发ADC采集并通过DMA自动搬运数据的完整配置流程。该方案适用于电机控制、电源管理等需要高精度定时采集的场景,通过GTM生成PWM信号、ADC采集和DMA传输实现全硬件自动化流程,显著提升系统效率。

weixin_42536058的博客 535

二叉树经典算法题目

思路:套用层序遍历的模板,然后记录当前层的最后一个node,和下一层的最后一个node.(如果知道当前层的最后一个node是哪一个,在把node的左右节点发放入队列的时候,下一层的最后一个node就能得出)。和上一题的不同的一点是,还要知道当前层的第一个节点是谁。1.遇到左子节点为空,右子节点不为空的情况,不满足 2.遇到第一个左子节点不为空,右子节点为空或者在右子节点都为空的时候,下面的节点子节点均为叶。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

qq_42575907的博客 2053

手把手教你用SNAP做全极化SAR图像分类

引言 最近在研究极化SAR图像分类方面的内容,发现用SNAP软件可以很方便的实现图像的分类。因为在入门过程中踩了很多坑,浪费了一些时间,为了不让大家踩坑,所以决定写下这篇教程。 前期准备 软件下载 SNAP是欧空局旗下的一个SAR图像处理软件,可以点击下载地址进行下载。 数据下载 数据下载地址:https://mdacorporation.com/sample_data/RS2_OK76385_...

zz651044657的博客 1万+

二叉树算法题

后,我们了解了二叉树的相关概念和性质,并且实现了顺序结构二叉树——堆和链式结构二叉树,那么在本篇中我们就来试着解决一些二叉树相关的算法题,相信通过这些练习之后你会对二叉树有更深的理解。在之前二叉树的相关章节(

mljy.博客 2740

电子取证之服务器取证思路

服务器取证是一个技术性很强的领域,涉及到多个环节,包括网站重构、网站代码分析、容器分析等。数据库密码的获取通常通过查看配置文件、日志文件、备份等途径,同时可以通过一些绕过技术如禁用身份验证来获取权限。对于不同的数据库,需要根据其特性采取相应的应对方法和命令。

2401_84830066的博客 2491

VTK:vtkAssembly用法实战

VTK:vtkAssembly用法实战程序输出程序完整源代码 程序输出 程序完整源代码 #include <vtkActor.h> #include <vtkAssembly.h> #include <vtkAxesActor.h> #include <vtkCubeSource.h> #include <vtkNamedColors.h> #include <vtkNew.h> #include <vtkOrientation

希望我的博客,能帮上你解决学习中工作中所遇到的问题 937

二叉树的经典算法题

LeetCode100:给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。输入:p = [1,2,3], q = [1,2,3]输出:true输入:p = [1,2], q = [1,null,2]输出:false两个二叉树同时进行前序遍历, 先判断根节点是否相同, 如果相同再分别判断左右子节点是否相同, 判断的过程中只要有一个不相同就返回 false, 如果全部相同才返回 true。

Mamba_Stan的博客 396

Transformer实战(32)——Transformer模型压缩

在本节中,我们学习了如何在硬件资源受限的情况下减轻运行大模型的负担。我们介绍了如何通过蒸馏、剪枝和量化从训练好的模型中提取高效模型。预训练轻量级的通用语言模型(如 DistilBERT) 可以在各种任务上进行微调,并表现出与非蒸馏模型相当的性能。随着数据量的不断增加,我们希望模型能够更快地运行,在这方面,高效 Transformer 起着至关重要的作用。

盼小辉丶的博客 2611

二叉树算法题汇总

目录 二叉树的遍历 前序遍历 中序遍历 后序遍历 层次遍历 基础算法 求二叉树中的节点个数 求二叉树的深度(高度) 求二叉树第k层的节点个数 求二叉树中叶子节点的个数 判断两棵二叉树是否相同的树 判断二叉树是不是平衡二叉树 求二叉树的镜像 判断两个二叉树是否互相镜像 判断是否为二分查找树BST 本文的实例代码基于JAVA编写 首先给出节点的数据结构 publi...

一念永恒 1万+

国科大作业考试资料《人工智能原理与算法》2024新编-第二次作业整理

1、对于下列活动,分别给出任务环境的PEAS描述, 并按2.3.2节列出的性质进行分析: 3、为图2.2中描述的吸尘器世界实现一个可以进行性能度量的环境模拟器。你的实现是必须是模块化的,以使传感器、执行器和环境特征(大小、形状、灰尘放置等)便于修改。 4、实现上述题目真空吸尘器环境中的简单反射Agent。用所有可能的初始灰尘分布和Agent位置,运行环境模拟器和Agent。记录Agent在每种情况下的性能评分和总体平均评分。

MatsumotoChrikk 1187

彻底解决JDK安装包点击后无反应

jdk双击无法安装,jdk双击没反应,jdk安装包双击后没反应。

renkai721的专栏 8939

二叉树----算法题

1. 单值二叉树。

2402_87113148的博客 1001

新手必看!一步步教您如何获取可用的D音Cookie(为什么我复制的D音Cookie无法正常使用?)

新手必看!一步步教您如何获取可用的抖音Cookie(为什么我复制的抖音Cookie无法正常使用,无法下载无水印视频?)

XiaoqiangClub的博客 1495

二叉树算法题目总结

二叉树题目总结

maple的博客 500

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

二叉树的经典例题—数据结构

二叉树

m0_61963422的博客 4885

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

算法刷题记录——二叉树(递归合集)

题目:思考:这个题本身并不难,只是我递归用得少用不熟,发现二叉树真的好爱递归!

m0_69064462的博客 363

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

二叉树相关算法汇总(详细)

二叉树基本知识 二叉树(Binary Tree)是n(n≥0)个有限元素的集合,该集合或者为空,或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成。当集合为空时,称该二叉树为空二叉树。 二叉树常见概念: 结点的度。结点所拥有的子树的个数称为该结点的度。 叶子结点。度为0的结点称为叶子结点,或者称为终端结点。 分支结点。度不为0的结点称为分支结点,或者称为非终端结点。一棵树的结点除叶子结点外,其余的都是分支结点。 左孩子、右孩子、双亲。树中一个结点的子树的根结点称为这个结

泛泛之素 1万+

二叉树常见算法题(单值二叉树、二叉树的最大深度、反转二叉树、判断两颗二叉树是否相同、对称二叉树、一颗二叉树是否是另一颗二叉树的子树)

这里的代码都是递归实现的,再强调下二叉树的概念,二叉树的构成有两种情况: 1、空树 2、根节点 + 左子树 + 右子树(这里的左子树和右子树又是一颗二叉树) 可以看出二叉树就是递归定义的,下面的算法题中大多都要用到这个思想。 一、单值二叉树 1、题目要求: 2、解法: 这个也就是遍历二叉树,然后判断它是否和根节点的值相同,前序、中序、后序遍历都可以。 3、代码: bool _isUnivalTree(struct TreeNode* root,int num){ if(root.

weixin_62042704的博客 1800

二叉树算法解题思路总结(一)

第一步:先考虑用递归的方式解决问题,注意递归的终止条件。有的递归需要用到回溯法和全局变量 第二步:如果递归无法解决问题,考虑迭代法,具体的迭代方式参考层序遍历和非递归遍历 本篇文章只介绍第一步相关的内容,题目来自leetcode和牛客网的剑指offer 一、找结点 1.二叉搜索树的最近公共祖先:leetcode236 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百...

meihuai7538的博客 2610

详解二叉树经典基础算法

二叉树是我们平时学习当中一种常见的数据结构。在面试和学习当中我们难免会遇到一些跟二叉树有关的算法题。今天我为大家带来了几题经典的二叉树基础算法题,我们一起来看看吧! 目录 1.检查两棵树是否相同 ①题目描述 ②思路分析 ③题解代码 2.另一颗树的子树 ①题目描述 ②思路分析 ③题解代码 3.二叉树最大的深度 ①题目描述 ②思路分析 ③题解代码 4.平衡二叉树 ①题目描述 ②思路分析 ③题解代码 5.对称二叉树 ①题目描述 ②思路分析 ③题解代码 6....

lingxu6的博客 1662

二叉树习题汇总

所以,其实这道题和相同的树有点类似,都是先比较根节点,再比较根节点的左右子树。对称二叉树是左子树和右子树进行比较, 左子树的左子树与右子树的右子树进行比较,以及左子树的右子树与右子树的左子树进行比较。可以通过比较根节点的左右子节点是否相等,以及递归地比较左子树的左子节点与右子树的右子节点,左子树的右子节点与右子树的左子节点,来判断二叉树是否对称。今天我们学习了7道二叉树的练习题,说句心里话,第一次我做题的时候,也是迷迷糊糊,磕磕碰碰,但是多做几遍,多画一画图,我相信,小伙伴们都能学会~Ready Go!

夏天的阳光吖的博客 1408

【算法】利用递归dfs解决二叉树算法题(C++)

我们曾学过前序遍历,其就是深度优先搜索的一种应用。在二叉树的前序遍历中,我们首先访问根节点,然后递归对左子树进行前序遍历,最后递归地对右子树进行前序遍历。在深度优先搜索算法中,我们从起始节点开始,递归地探索每个可达节点,直到没有未访问的相邻节点为止。因此,前序遍历也可以看作是对图或树进行深度优先搜索的一种方式。它遵循先访问根节点,然后递归地访问左子节点和右子节点的顺序。

卜及中的博客 1314

二叉树的遍历和算法题

如果是完全二叉树,那么这时候队列的所有结点都应该是NULL,当有不为NULL的结点就说明树不为完全二叉树。4.若规定二叉树的根节点的层数为1, 具有n个结点的二叉树,其高度h为log(n+1)利用队列先进先出的特性,队头pop的时候将队头的左右子树push进队列,上面带下面。首先创建一个队列,队列存放二叉树结点的指针,然后将二叉树的根结点push进队列。1.若规定二叉树的根结点的层数为1,那么二叉树的第i层有2^(i-1)棵结点。2.若规定二叉树的根结点的层数为1,那么一颗二叉树最多有2^(h)-1。

qq_64105689的博客 1480
上一篇: SpringBoot 究竟是如何跑起来的?
下一篇: LevelDB 代码撸起来!
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值