【算法刷题 DAY03】剑指offer树相关算法题总结2

news2025/1/16 4:04:12

JZ7 重建二叉树

描述
给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
img

提示:
1.vin.length == pre.length
2.pre 和 vin 均无重复元素
3.vin出现的元素均出现在pre里

解题思路1(使用递归方法):这个题就是在数据结构中根据前序遍历和中序遍历构造二叉树的经典题目。

  • 如果先序遍历数组为空,或者为null,则直接返回null
  • 如果先序遍历数组中只有一个元素,那么直接返回该元素构成的TreeNode即可。
  • 如果先序遍历数组长度大于1,那么从先序遍历数组中获取第一个元素,这个元素为树的根。
  • 然后已这个元素在中序遍历中的元素作为划分,把中序遍历数组分为两部分。
  • 分别在这两部分上继续上述方法

图片说明

public TreeNode reConstructBinaryTree(int[] pre, int[] vin) {
    if (pre == null || pre.length == 0) return null;

    // 获取到前序遍历的第一个元素
    int root = pre[0];
    if (pre.length == 1) {
      return new TreeNode(root);
    }

    // 获取到root在中序遍历数组中的下标
    List<Integer> vinList = Arrays.stream(vin).boxed().collect(Collectors.toList());
    int i = vinList.indexOf(root);

    TreeNode treeRoot = new TreeNode(root);
    // 左
    TreeNode left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(vin, 0, i));
    TreeNode right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(vin, i + 1, vin.length));

    treeRoot.left = left;
    treeRoot.right = right;

    return treeRoot;
}

需要注意的是opyOfRange函数的两个下标是左闭右开的

JZ26 树的子结构

描述

输入两棵二叉树A,B,判断B是不是A的子结构。(我们约定空树不是任意一个树的子结构)

假如给定A为{8,8,7,9,2,#,#,#,#,4,7},B为{8,9,2},2个树的结构如下,可以看出B是A的子结构

img

解题思路(利用递归)

  • 首先判断当前结点是否和子结构的根结点相同。
  • 如果相同,则继续判断左右子树是否也相同,如果相同则存在子结构。
  • 如果不同,则继续以root1的左子树为一个新的树,判断这个树中是否包含子结构。
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
    if (root2 == null) return false;
    if (root1 == null && root2 != null) return false;
    // 判断当前传入的结点是否相同
    boolean flag = false;
    if (root1.val == root2.val) {
      // 如果当前结点相同,那么继续判断以当前结点的左右子树是否相同
      flag = isSubTree(root1, root2);
    }
    if (!flag) {
      flag = HasSubtree(root1.left, root2);
      // 在左子树上找不到子结构,那么去右子树上找子结构
      if (!flag) {
        flag = HasSubtree(root1.right, root2);
      }
    }
    return flag;
}

public boolean isSubTree(TreeNode root1, TreeNode root2) {
    if (root2 == null)
      return true;
    if (root1 == null && root2 != null)
      return false;
    if (root1.val == root2.val)
      return isSubTree(root1.left, root2.left) && isSubTree(root1.right, root2.right);
    else
      return false;
}

HasSubtree是判断root1是否包含root2的子结构,如果不包含,则继续判断以root1的左右子树为根的树是否包含root2的子结构

其中isSubTree是判断以输入结点root1为根的树是否包含root2的子结构。

这个题一开始我认为采用先序遍历遍历两个树,然后判断root1的先序遍历是否包含root2的先序遍历序列即可。但是实际上不可行。考虑下面这种情况

  • root1 {1,2,3} root2 {2,3,#} root1先序遍历为123,root2先序遍历为23 序列是包含的,但是root2不是root1的子结构

这里学习了Java中如何判断一个字符串是另一个字符串的子串方法

  • 可以使用str.Contains(str1)

此外,对于ArrayList中的ContainsAll方法,判断的是list1中是否包含list2的全部元素,不是我们所说的那种子list。

JZ33 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回 true ,否则返回 false 。假设输入的数组的任意两个数字都互不相同。

解题思路1(利用递归):BST树后序遍历序列中最后的结点是根结点。

  • 利用根结点,将序列分为左右子树。从头遍历序列,找到第一个不小于根结点的值,这个值左边就是左子树结点。从这个位置开始到序列长度-1为右子树元素。
  • 在判断的时候,需要判断右子树这边元素值是否有小于根结点值的元素,如果有就不满足条件。否则就递归的去检查左右子树是否满足,直到剩下一个元素为止。
public boolean VerifySequenceOfBST(int[] sequence) {
    if (sequence == null) return false;
    // 调用函数 判断输入序列是否满足BST
    return f(sequence, 0, sequence.length - 1);
}

public boolean f(int[] postorder, int i, int j) {
    // 当输入序列中是有一个元素的时候,那么满足条件
    if (i >= j) return true;
    // 从序列中获取到根结点元素
    int root = postorder[j];
    // 从头开始遍历整个序列,找到第一个不小于根结点的结点,这个结点就是以root为根的子树的右子树的第一个遍历的结点
    int p = i;
    while (postorder[p] < root) p++;
    // 此时rootIndex记录的就是以root为根的子树的右子树的第一个遍历的结点
    // 下面开始从右子树的第一个结点遍历序列,判断是否存在一个结点的值小于root的值,如果小于,则不满足BST
    for (int k = p; k < j; k++) {
      if (postorder[k] < root)
        return false;
    }
    // 递归的求左右子树是否满足条件
    return f(postorder, 0, p - 1) && f(postorder, p, j - 1);
}

总结,如何判断二叉树后序遍历的序列是否满足BST

  • 序列最后一个元素是根结点
  • 根据根结点,划分左右子树。
  • 判断右子树中是否有结点小于根结点,如果有就不满足
  • 否则递归求解左右子树是否满足BST

JZ34 二叉树中和为某一值的路径(二)

描述

输入一颗二叉树的根节点root和一个整数expectNumber,找出二叉树中结点值的和为expectNumber的所有路径。

1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点

2.叶子节点是指没有子节点的节点

3.路径只能从父节点到子节点,不能从子节点到父节点

4.总节点数目为n

如二叉树root为{10,5,12,4,7},expectNumber为22

img

则合法路径有[[10,5,7],[10,12]]

解题思路1(利用层序遍历):我们可以使用层序遍历。使用两个队列来记录,一个队列用来实现层序遍历,另一个队列用来记录经过的路径。当遍历到叶子结点的时候,根据其路径求和来判断结果是不是expectedNumber即可。

WechatIMG165

public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int expectNumber) {
    ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    if (root == null) return ret;

    // 创建两个队列,一个队列用于中序遍历,一个用于记录路径
    Queue<ArrayList<Integer>> pathQ = new LinkedList<>();
    Queue<TreeNode> nodeQ = new LinkedList<>();
    // 根结点入队
    nodeQ.offer(root);
    pathQ.offer(new ArrayList<>(Arrays.asList(root.val)));

    // 开始遍历
    while (!nodeQ.isEmpty()) {
      // 出队
      ArrayList<Integer> currentPath = pathQ.poll();
      TreeNode node = nodeQ.poll();
      // 判断是否有左右子树
      if (node.left != null) {
        nodeQ.offer(node.left);
        ArrayList<Integer> left = new ArrayList<>(currentPath);
        left.add(node.left.val);
        pathQ.offer(left);
      }

      if (node.right != null) {
        nodeQ.offer(node.right);
        ArrayList<Integer> right = new ArrayList<>(currentPath);
        right.add(node.right.val);
        pathQ.offer(right);
      }

      if (node.left == null && node.right == null) {
        // 是叶子结点
        // 遍历经过的路径,求和看看是不是等于sum
        Integer total = 0;
        for (Integer integer : currentPath) {
          total += integer;
        }

        if (total == expectNumber)
          ret.add(currentPath);
      }
    }
    return ret;
}

这个方法还可以用来输出二叉树从根结点到叶子结点经过的路径。只需要修改当判断当前结点是叶子结点后,直接输出currentPath列表中的值即可。

解题思路2(使用递归方法):利用DFS进行遍历。一直遍历到叶子结点,然后判断是否满足expectNumber。判断完当前结点后,要回退到上一个结点,然后继续遍历其他的结点。

img

img

public class Solution {
  
    private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    private LinkedList<Integer> path = new LinkedList<>();

    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int expectNumber) {
        dfs(root, expectNumber);
        return ret;
    }

    private void dfs(TreeNode root, int expectNumber) {
        if (root == null) return;
        int num = expectNumber - root.val;
        path.add(root.val);
        // 判断当前结点是否是叶子
        if (root.left == null && root.right == null && num == 0) {
            ret.add(new ArrayList<>(path));
        }
        // 如果不是叶子结点,继续遍历左右子树
        dfs(root.left, num);
        dfs(root.right, num);
        // 当遍历结束后,那么当前结点就完全遍历完了,然后返回到上一个结点继续遍历
        path.removeLast();
    }
}

Z84 二叉树中和为某一值的路径(三)

描述

给定一个二叉树root和一个整数值 sum ,求该树有多少路径的的节点值之和等于 sum 。

1.该题路径定义不需要从根节点开始,也不需要在叶子节点结束,但是一定是从父亲节点往下到孩子节点

2.总节点数目为n

3.保证最后返回的路径个数在整形范围内

假如二叉树root为{1,2,3,4,5,4,3,#,#,-1},sum=6,那么总共如下所示,有3条路径符合要求

img

解题思路(利用递归方法):思路就是首先计算以当前结点为根结点的路径中满足条件的个树。然后求以当前结点的左右子树为根结点的满足条件的路径个数。

private int count = 0;
public int FindPath(TreeNode root, int sum) {
    // write code here
    // 如果结点为空,则直接返回
    if (root == null)
      return count;

    // 计算以当前结点为根结点的满足sum条件的路径个树
    dfs(root, sum);

    // 查找以root左右子树为根的树是否有满足条件的路径
    FindPath(root.left, sum);
    // 这里是sum而不是sum-root.val是因为这是以root左右子树为根结点重新计算
    FindPath(root.right, sum);
    return count;
}

private void dfs(TreeNode root, int sum) {
    if (root == null)
      return;
    if (root.val == sum)
      count++;
    // 继续遍历左右子树
    dfs(root.left, sum - root.val);
    dfs(root.right, sum - root.val);
}

JZ78 把二叉树打印成多行

描述

给定一个节点数为 n 二叉树,要求从上到下按层打印二叉树的 val 值,同一层结点从左至右输出,每一层输出一行,将输出的结果存放到一个二维数组中返回。

例如:
给定的二叉树是{1,2,3,#,#,4,5}
img
该二叉树多行打印层序遍历的结果是

[

[1],

[2,3],

[4,5]

]

解题思路1(层序遍历):这个题实际上考的就是二叉树的层序遍历,但是和层序遍历有一点不一样,就是需要按层输出。我们可以在出队之前获取到队的长度,然后使用循环同时将这一层的都出队,这样就可以了。

public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
    ArrayList<ArrayList<Integer>> list = new ArrayList<>();
    if (pRoot == null)
      return list;
    // 创建对列
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(pRoot);

    while (!queue.isEmpty()) {
      int size = queue.size();
      ArrayList<Integer> levelList = new ArrayList<>();
      for (int i = 0; i < size; i++) {
        // 出队列
        TreeNode node = queue.poll();
        levelList.add(node.val);
        if (node.left !=null)
          queue.offer(node.left);
        if (node.right != null)
          queue.offer(node.right);
      }
      // for循环结束后,说明这一层就结束了
      list.add(levelList);
    }
    return list;
}

这个题的思路同样可以作用于非递归方法求解二叉树的层数

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

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

相关文章

CSS入门二、美化页面元素

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

【高速数字化仪应用案例系列】虹科数字化仪在光纤领域的应用

光纤应用 光纤越来越多地应用于各种领域。它们能够以光速长距离传输信息&#xff0c;并且损耗低&#xff0c;这使它们成为大容量远程数据通信的主要媒介。因此&#xff0c;光纤网络可以在电信系统中找到&#xff0c;它们用于传输和接收的目的。它们还用于提供各种数字服务&…

Docker命令-常用命令讲解

Docker常用命令 一&#xff1a;帮助命令二&#xff1a;镜像命令1. docker images 查看所有本地的主机上的镜像2. docker search 镜像名3. docker pull 下载镜像4. docker rmi三&#xff1a;容器命令1.docker run 新建容器并启动2.从容器返回到主机&#xff1a;3.docker ps 列出…

收银软件哪家强?2023年收银软件排行榜新鲜出炉!

每家实体店都少不了收银的程序&#xff0c;每个实体店老板都离不开收银的工具。随着信息技术的发展&#xff0c;收银的工具不再只有收银机&#xff0c;更高效、更方便的收银软件&#xff0c;已经成为了零售店老板们的新宠。收银机和收银软件有什么区别&#xff1f;收银机只能对…

1.11 LED灯点亮串口解析器

LED点灯实验 一&#xff0e;电路图&#xff1a; 三极管&#xff1a; NPN类型&#xff1a; PNP类型&#xff1a; NPN类型当基极为高电平时&#xff0c;集电极和发射极导通 PNP类型当基极为低电平时&#xff0c;集电极和发射极导通 由电路图可知LED电路图中三极管为NPN类型&am…

我在CSDN的2022---2023Flag

一、加入CSDN我是在2020年12月注册的CSDN&#xff0c;大一上学期就听同学给我讲了这个软件&#xff0c;然后就下载了&#xff0c;里面确实很多优质文章&#xff0c;对于当时向我们这样的初学者来说就是很实用。还记得都是搜什么&#xff0c;求最大值&#xff0c;最小值&#xf…

Redis热点数据处理

1、概念热点数据就是访问量特别大的数据。2、热点数据引起的问题流量集中&#xff0c;达到物理网卡上限。请求过多&#xff0c;缓存分片服务被打垮。redis作为一个单线程的结构&#xff0c;所有的请求到来后都会去排队&#xff0c;当请求量远大于自身处理能力时&#xff0c;后面…

RabbitMQ消息可靠性问题、死信交换机、延迟消息、惰性队列

目录消息可靠性生产者确保将消息成功送入队列消息确认消息回执消费者确保消息成功从队列中取出并成功消费消费确认机制消费失败重试机制失败策略使用第三种方式&#xff1a;消费者指定失败后转发的交换机使用第一种方式&#xff1a;在队列中指定死信交换机消息持久化问题交换机…

软件测试常见性能问题案例分析

在用户场景不确定的情况下&#xff0c;我们为了保障软件的正常运行就必须对软件的性能进行测试。下面我们一起来看看在软件测试中常见的性能问题&#xff0c;希望大家可以通过这七个比较典型的案例分析&#xff0c;充分掌握各种性能问题的解决方法。 案例一&#xff1a;某次压…

Spring Cloud 03 --- Nacos注册中心

前言 注册中心以Map形式存储消费者与生产者的IP和端口 基本概念 &#xff08;1&#xff09;Nacos 是阿里巴巴推出来的一个新开源项目&#xff0c;是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供…

2023年使用率会很高的9个SSH远程连接工具有这些!网工、运维你们用的是哪个?

网络工程师和运维工程师我想每天做的最多的一件事就是远程连接了&#xff0c;例如远程连接网络设备、远程连接服务器&#xff08;物理服务器或者云服务器&#xff09;&#xff0c;这个时候大多数用的工具都是基于SSH协议的&#xff0c;每位工程我想都有自己熟悉或者青睐的SSH工…

时序图文献精度——5.2019-IJCIA-Node Embedding over Temporal Graphs

Node Embedding over Temporal Graphs Abstract 作者提出了一种在时间图中嵌入节点的方法。学习时间图的节点和边随时间的演变&#xff0c;并将这种动态整合到时间节点嵌入框架中&#xff0c;用于不同的图预测任务。作者也提出了一个联合损失函数&#xff0c;它通过学习组合节…

【java算法】稀疏数组/队列/单双链表

文章目录线性和非线性结构稀疏数组前言代码刷类型题队列非环形队列环形队列刷题单链表单链表的定义案例演示--代码1.按照顺序添加2.按英雄排名插入3.根据no编号来修改节点信息4.删除节点单链表刷题1.求单链表中有效节点的个数2.查找单链表中的倒数第k个节点3.单链表的反转4.从尾…

SAP入门技术分享三:OPEN SQL

OPEN SQL1. 概要&#xff08;1&#xff09;R/3体系结构&#xff08;2&#xff09;SQL定义&#xff08;3&#xff09;OPEN SQL经常使用的命令2. OPEN SQL&#xff08;1&#xff09;SELECT 语句&#xff08;2&#xff09;INTO语句3. FROM语句&#xff08;1&#xff09;选择静态表…

JSONArray

目录1. 需求2. 测试3. 实现需求4. 相关操作1. 将JSONObject装入JSONArray2. JSONArray与String的相互转换1. 需求 最近有个需求&#xff1a; 要接收某个接口的 JSON 数据&#xff0c;而这个JSON数据有可能是一个 JSON 对象&#xff0c;也有可能是一个 JSON数组。 "{name…

python数据结构之字符串

一、字符串的格式化输出 1.1、格式化运算符 print("我跑完了第" str(lap 1) "圈")上面这段输出的代码使用了两个加号做了字符串拼接&#xff0c;并且将整形转换成了字符串。也可以使用一种更好的办法&#xff0c;格式化输出来打印这句话。 print(&quo…

内存取证——基础知识(volatility内存取证)

目录 一、基本概念 二、运行内存镜像的获取 2.1 Windows内存镜像获取 2.1.1 Magnet RAM Capture获取内存镜像 2.1.2 AccessData FTK Imager软件获取内存镜像 2.1.3 DumpIt软件获取内存镜像 2.1.4 额外知识补充&#xff1a; 2.2 Linux\Mac OS 下内存镜像获取方法 三、内…

什么是云渲染?云渲染速度快吗?

近年来随着计算机技术的逐步发展&#xff0c;万物上‘’云‘’的趋势越发明显&#xff0c;一种基于云计算的SAAS服务平台——云渲染农场开始走入CG行业。而且云渲染农场&#xff08;如Renderbus瑞云渲染&#xff09;也在众多CG小伙伴的眼里成为了不可或缺的一部分。有人问云渲染…

[ docker相关知识 ] 删除 docker 拉取的容器 -- 解决删除镜像报错问题

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

CSS入门八、CSS3动画

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…