二叉树之自底向上递归

  • 236. 二叉树的最近公共祖先
  • 652. 寻找重复的子树

二叉树我们知道使用递归代码会比较简洁,而这些递归题型中大部分题目都是自上向底(正常思维)进行递归,也有自底往上进行递归的题目,以下汇总两道比较典型的:

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

解法:自底往上递归
思路:先深度遍历改树。当你遇到节点 p 或 q 时,返回一些布尔标记。 该标志有助于确定是否在任何路径中找到了所需的节点。最不常见的祖先将是两个子树递归都返回真标志的节点。它也可以是一个节点,它本身是p或q中的一个,对于这个节点,子树递归返回一个真标志。
算法实现:

  • 1 从根节点开始遍历树。
  • 2 如果当前节点本身是 p 或 q 中的一个,我们会将变量 mid 标记为 true,并继续搜索左右分支中的另一个节点。
  • 3 递归(下探):如果左分支或右分支中的任何一个返回 true,则表示在下面找到了两个节点中的一个。
  • 4 process: 如果在遍历的任何点上,左、右或中三个标志中的任意两个变为 true,这意味着我们找到了节点 p 和 q 的最近公共祖先。

时间复杂度:O(N),N 是二叉树中的节点数,最坏情况下,我们需要访问二叉树的所有节点。
空间复杂度:O(N),这是因为递归堆栈使用的最大空间位 N,斜二叉树的高度可以是 N。

代码:
class Solution {    private TreeNode ans = null; //用于存储找到的最近公共祖先public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){// 遍历整个树recurseTree(root,p,q);return ans;}private boolean recurseTree(TreeNode currentNode, TreeNode p, TreeNode q){//1 递归终止条件:如果下探左右子树到达最后一个节点时,返回false,if (currentNode == null){return false;} // 这里注意是先下探,然后在process(自底向上的关键)int left = recurseTree(currentNode.left,p,q) ? 1 : 0;int right = recurseTree(currentNode.right,p,q) ? 1 :0;int mid = (currentNode == p || currentNode == q) ? 1 : 0;//5 如果标志位left、right、mid中有两个及以上为1,表示currentNode就是最近公共祖先    processif (mid+left+right >=2) {ans = currentNode;}//6 三个标志位只要有一个为true,递归时则返回true,表示找到至少一个return (mid + left + right ) > 0;}
}

652. 寻找重复的子树

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根
结点即可。两棵树重复是指它们具有相同的结构以及相同的结点值。示例 1:1/ \2   3/   / \4   2   4/4
下面是两个重复的子树:2/4
和4
因此,你需要以列表的形式返回上述重复子树的根结点。

解法:自底往上递归
思路:自底向上获得每个节点的序列化路径,存入map中,如果出现了相同的路径值且map中该路径值只出现了一次(防止重复加入路径),则将对应Node结果集。
算法实现:

  • 1 从根节点开始遍历树。在遍历中对节点的路径进行序列化和记忆。
  • 2 递归(下探):自底向上获得每个节点的序列化路径。
  • 3 process:如果备忘录中存在该路径,同时路径出现的次数为1,那么这就是我们要找的结果,将该处的根节点加入全局变量res中。
  • 4 将序列化的路径保存到备忘录map中,并同时返回。

时间复杂度:O(N),N 是二叉树中的节点数。
空间复杂度:O(N),这里不仅有递归堆栈使用的空间位N,还需要借助数组和HashMap实现备忘录。

代码:
class Solution {private List<TreeNode> res = new ArrayList<>();private Map<String, Integer> map = new HashMap<>();public List<TreeNode> findDuplicateSubtrees(TreeNode root) {if (root == null) return res;saveRoute(root);return res;}//递归获取每个子树的路径,保存于Map中private String saveRoute(TreeNode node) {if (node == null) return "";//自底向上获取每个节点的序列化值,先递归,再processString route = node.val + "," + saveRoute(node.left) + "," + saveRoute(node.right);//将结果放入map,判断是否有相同子树,同时避免出现多次相同子树if (map.containsKey(route) && map.get(route) == 1) {res.add(node);}map.put(route, map.getOrDefault(route, 0) + 1);return route;}
}

总结:
自上向下的递归:先process,在递归下探;
自底向上的递归:先递归下探,在进行process。

二叉树之自底向上递归相关推荐

  1. 刷题:二叉树的非递归遍历方式

    二叉树的非递归的遍历方式 上篇博客记录了二叉树的递归遍历方式以及根据二叉树的遍历结果还原二叉树的内容. 本篇博客记录二叉树的非递归的遍历方式. 二叉树的非递归遍历需要借助栈来实现,而且三种遍历的方式的 ...

  2. 二叉树的非递归遍历(c/c++)

    由于递归算法相对于非递归算法来说效率通常都会更低,递归算法会有更多的资源需要压栈和出栈操作(不仅仅是参数,还有函数地址等)由于编译器对附加的一些栈保护机制会导致递归执行的更加低效,使用循环代替递归算法 ...

  3. 创建的二叉树后续非递归遍历结果为_一入递归深似海,从此offer是路人

    前言 今天我们来总结二叉树的前中后序以及层次遍历的递归与非递归的写法.我们都知道二叉树遍历的递归写法很简单,但是面试的时候面试官往往考察的不是我们递归的写法,他们满怀期待你写出非递归的解法,而当你只会 ...

  4. 二叉树的非递归遍历(统一的模板)

    二叉树的非递归遍历 前言 树的存储结构 先序遍历 先序的递归遍历 先序的非递归遍历 中序遍历 中序的递归遍历 中序遍历的非递归算法 后序遍历 后序的递归遍历 后序的非递归遍历 层次遍历 层次遍历获得每 ...

  5. 数据结构-二叉树的非递归遍历

    前面的章节我们实现了二叉树最基本的遍历方式:递归遍历,代码是如此的简洁:辣么我们为什么还要去学习二叉树的非递归遍历方式呢?众所周知,递归优点是将可以将复杂的问题简单化即大问题拆分成一个个小问题,那么它 ...

  6. 剑指offer之中序打印二叉树(非递归实现)

    1 问题 中序打印二叉树(非递归实现),比如二叉树如下 /* 2* 3 5 * 1 4 2 3 * 3 2 1 5 1 4 2 3 中序:按左中右来打印二叉树,结果如下 3 1 2 3 1 4 5 2 ...

  7. c语言以顺序结构存储的二叉树的非递归遍历,C语言二叉树的非递归遍历实例分析...

    本文以实例形式讲述了C语言实现二叉树的非递归遍历方法.是数据结构与算法设计中常用的技巧.分享给大家供大家参考.具体方法如下: 先序遍历: void preOrder(Node *p) //非递归 { ...

  8. 6-4 二叉树的非递归遍历 (25分)_本周小结!(二叉树)

    给「代码随想录」一个星标吧! ❝ 以后每周加上一个本周小结怎么样? ❞ 本周小结 发现大家周末的时候貌似都不在学习状态,周末的文章浏览量和打卡情况照工作日差很多呀,可能是本周日是工作日了,周六得好好放 ...

  9. 树:二叉树的非递归遍历算法

    二叉树的递归遍历 二叉树的递归遍历算法,写法很简单,比如说前序遍历树,如下: //前序遍历 void PreOrderTraverse(BiTree tree) {if (NULL != tree){ ...

  10. 二叉树的遍历-递归与非递归 - 海子

    二叉树的遍历-递归与非递归 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就是递归定义,因此采用递归的方 ...

最新文章

  1. PCL_common模块api代码解析
  2. full paper(long paper),short paper,oral,poster,workshop,findings
  3. 实验报告-python文库_Python实验报告
  4. 设计模式16_策略模式
  5. 什么是锁PHP,PHP简单分布式锁-PHP中的锁
  6. windows Server 2016 开启远程登录和多用户同时远程登录
  7. yum安装mysql5.7
  8. MySQL chartset
  9. linux sftp 增加用户(centos)
  10. 车牌定位html5,车牌识别(一)——车牌定位(附详细代码及注释)
  11. 算法自动生成迷宫地图
  12. [Jetson TX2] NVIDIA Jetson TX2 参数介绍
  13. Java中boolean类型占几个字节,你知道吗?
  14. Android手机开发常用数据库,android开发常用的数据库
  15. 观察 | 家长焦虑,教培着急,暑期“培训热”今年还会持续吗?
  16. android mp3 wav转换工具,音频提取转换工具app
  17. 黑马程序员-黑马程序员训练营基础测试
  18. Python--Matplotlib(基本用法)
  19. 11款Java工具:源代码优化与分析
  20. 第一章 教育基础(07 心理学基础知识)

热门文章

  1. Python使用matplotlib绘制龟兔赛跑中兔子和乌龟的行走轨迹
  2. Vegas2020注册机
  3. 预加重、去加重和均衡
  4. VUE引入JsBarcode组件异常记录
  5. 【f1c200s/f1c100s】全志f1c200s开发板设计(含原理图和PCB)
  6. CI520只有SPI通讯接口,支持读写A卡,PIN对PIN直接替换CV520软硬件兼容
  7. mac 思科 链路聚合_思科交换机配置链路聚合的方法
  8. 单片机双击 长按 c语言,51单片机按键检测源程序(实现单击、双击、长按等功能)...
  9. app账号退不出去_2021个人所得税退税时间是什么时候?还可以退吗?
  10. 1.STC15W408AS单片机硬件资源