二叉树的递归套路
-
假设以X节点为头,假设可以向X左子树和X右子树要任何信息
-
在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)也就是以答案结果跟X有没有关系为划分讨论
-
列出所有可能性后,确定到底需要向左子树和右子树要什么信息
-
把左子树信息和右子树信息求全集,就是任何一颗子树都需要返回的信息info
-
递归函数都返回info,每一棵子树都这么要求
-
写代码,在代码中考虑如何把左子树和右子树信息整合出整棵树的信息
例一:平衡二叉树
什么是平衡二叉树:
在一棵二叉树中,每一棵子树的左子树和右子树的高度差小于2,分解可得到:
左子树必须是平衡的
右子树必须是平衡的
左子树和右子树的高度差小于2
给定一棵二叉树的头节点head,返回这棵二叉树是不是平衡二叉树
根据套路(1)我们可以写代码
假设我们可以获取到左右子树的信息:是否为平衡二叉树、高度是多少
//信息类,代表,能从子树上获取什么信息 class Info{ private boolean blance;//当前子树是否平衡 private int height;//当前子树的高度,用来返回给头节点判断是否平衡的 public Info(boolean blance, int height){ this.blance = blance; this.height = height; } public boolean isBlance(){ return blance; } public int getHeight(){ return height(); } public void setBlance(boolean blance){ this.blance = blance; } public void setHeight(int height){ this.height = height; } }
class getBlanceTree{ public Info process(Node X){ //如果一个空树,肯定是平衡的,并且高度为0,返回 if(X == null){ return new Info(true, 0); } //在左子树上递归,得到左子树的信息:是否平衡,高度是多少 Info leftInfo = process(X.left); //在右子树调用递归,获取右子树的信息 Info leftRight = process(X.right); //接下来的代码就是在当前节点上进行判断 //当前的节点的高度=左/右子树中最大的的高度+1 int height = Math.max(leftInfo.getHeight, right.getHeight) + 1; //设置当前是否平衡的变量为真 boolean blance = true; //如果左子树、右子树上、当前的子树上,有一个不平衡的,当前x为头节点子树就不平衡 //判断当前子树是不是平衡:它左右子树的高度差是否小于2 if(!leftInfo.isBlance() || !rightInfo.isBlance() || Math.abs(left.height - right.height) >= 2){ blance = false; } return new Info(blance, height); } }
例二:最大距离
给定一棵二叉树的头节点head,任何两个节点之间都存在着距离,返回整个二叉树的最大距离
什么是距离
一个节点通过树杈走到另一个节点,中间最短需要经过的节点数,比如如下的树,要从a走到b,经过3个节点,ab距离就是3
最大距离的两种情况
假如和当前头节点X无关,整棵树的最大距离为左子树的最大距离或者右子树的最大距离,谁大就是谁
假如和当前头节点X有关,整棵树的最大距离为左子树的高度+右子树的高度+1
根据套路(1)我们可以写代码
假设我们可以获取到左右子树的信息:当前节点的最大距离是多少、高度是多少
class Info{ public int maxDistance;//当前节点的最大距离 public int height;//高度是多少 public Info(int maxD, int height){ this.maxDistance = maxD; this.height = height; } }
class getMaxDistance{ public Info process(Node X){ if(X == null){ return new Info(0, 0); } //在左子树调用递归,获取左子树的信息,左子树上的最大距离,左子树的高度 Info leftInfo = process(X.left); //在右子树调用递归,获取右子树的信息 Info rightInfo = process(X.right); int height = Math.max(leftInfo.height, rightInfo.height) + 1;//当前X节点为头节点的子树的高度 //整个树的最大距离就是两种情况中最大的那个 int maxDistance = Math.max( //假如和X无关就是左/右子树的最大距离之间的较大值 Math.max(leftInfo.maxDistance, rightInfo.maxDistance), //假如和X有关,就是左子树高度+右子树高度+1 leftInfo.height + rightInfo.height + 1); return new Info(maxDistance, height); } }
求满足搜索二叉子树的最大子树 —— 把左子树信息和右子树信息求全集
给定一棵二叉树的头节点head,返回这棵二叉树中最大的二叉搜索子树的头节点
什么是整体搜索二叉树
-
整个树上没有重复值
-
左树的值比当前的节点小,右树的值比当前节点大
-
从当前节点为头节点的每一棵子树都如此
分情况解决
-
如果最大搜索二叉树与X无关,就是X左 / 右子树上最大二叉树的较大树,这一情况永远成立!
-
如果与X有关,直接返回X,就是说X就是最大搜索二叉树的头节点,满足这个需要有如下条件
-
左边的子树必须是搜索二叉树
-
右边的子树必须是搜索二叉树
-
左边的最大值小于X,右边的最小值大于X
-
需要从左右子树上获取什么信息
左树
-
是否是最大搜索二叉树
-
最大搜索二叉树的Size
-
最大值
右树
-
是否是最大搜索二叉树
-
最大搜索二叉树的Size
-
最小值
为了方便代码编写,可以总结为子树的信息
-
是否是最大搜索二叉树
-
最大搜索二叉树的Size
-
最小值
-
最大值
class Info{ public boolean isAllBST;//是否是最大搜索二叉树 public int maxSubBSTSize;//最大搜索二叉树的节点个数 public int min;//整个树最小值 public int max;//整个树最大值 }
class maxBST{ public void process(Node X){ if(X == null){ return null; } //在当前节点的左右子树上调用递归函数,获取需要的值 Info leftInfo = process(X.left); Info rightInfo = process(X.right); //当前节点X为头节点的子树的最小值 int min = X.value; //当前节点X为头节点的子树的最大值 int max = X.value; //当左子树不为空,更新当前子树的最大最小值 if(leftInfo != null){ max = Math.max(max, leftInfo.max); min = Math.min(min, leftInfo.min); } //当右子树不为空,更新当前子树的最大最小值 if(rightInfo != null){ max = Math.max(max, rightInfo.max); min = Math.min(min, rightInfo.min); } //设置当前节点的最大搜索二叉树节点数为0 int maxSubBSTSize = 0; //有左子树先设置进去,减少运行时间 if(leftInfo != null){ maxSubBSTSize = leftInfo.maxSubBSTSize; } //再去和右子树进行比较,得到更大的搜索二叉树的节点数 if(rightInfo != null){ maxSubBSTSize = Math.max(maxSubBSTSize, rightInfo.maxSubBSTSize); } boolean isAllBST = false; //X节点为头节点的二叉树为搜索二叉树必须同时满足三个条件 if( //条件1,它的左子树为搜索二叉树,注意:空树也算搜索二叉树 (leftInfo == null ? true :leftInfo.isAllBST) //((leftInfo != null && leftInfo.isAllBST == true) || leftInfo == null) && //条件2,它的右子树为搜索二叉树 (rightInfo == null? true : rightInfo.isAllBST) //((rightInfo != null && leftInfo.isAllBST == true) || rightInfo == null) && //条件3,左子树最大值小于X.value,右子树最小值大于X.value,注意左右为空树的最大值最小值不用判断但是也要写上 (leftInfo == null ? true : leftInfo.max < X.value && rightInfo == null ? true : rightInfo.min > X.value) /*(leftInfo != null && leftInfo.max < X.value || leftInfo == null) && (rightInfo != null && rightInfo.min > X.value || rightInfro == null) )*/ { //当前搜索二叉树的最大值,首先判断左右儿子是否为空 maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1; isAllBST = true; } return new Info(isAllBST, maxSubBSTSize, min, max); } }
leetcode:
使用二叉树的递归思想解决,假设当前头节点为X
首先我们确定我们要在左右两子树上要什么信息——这棵以X为头节点的子树上是否有p,q节点(包括X自己)isHave,当前最近公共祖先LCANode
class Info{ public boolean isHave; public TreeNode LCANode; public Info(boolean have, TreeNode L){ isHave = have; LCANode = L; } }
然后我们分类讨论,这个问题是否跟X有关,得到的答案——是的
-
和X无关
-
也就是说X点不是p/q节点
-
我们需要获取X的左右子树上是否有p和q节点,如果有,设置子树的Info.isHave为true
-
如果p、q两点,分别在X的左右子树上,则X就是最近公共祖先,LCANode = X
-
如果X的左右子树上产生了LCANode,就弹出对应的信息
-
-
和X有关
-
也就是说X是p/q节点
-
我们需要获取X的左右子树上是否有p/q节点,如果有子树info.isHave为true
-
如果p / q点在X的左/右子树上,X就是最近公共祖先,LCANode = X
-
如果X的左右子树上产生了LCANode,就弹出对应的信息
-
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return process(root, p, q).LCANode; } public Info process(TreeNode X, TreeNode p, TreeNode q){ if(X == null){ return new Info(false, null); } Info leftInfo = process(X.left, p, q); Info rightInfo = process(X.right, p, q); Boolean isHave = false; TreeNode LCANode = null; //如果在左右子树找到了LCANode了就弹出对应的信息,不用再往下继续了 if(leftInfo.LCANode != null){ return new Info(leftInfo.isHave, leftInfo.LCANode); }else if(rightInfo.LCANode != null){ return new Info(rightInfo.isHave, rightInfo.LCANode); } if(X == p || X == q){//如果X自己就是p/q //如果X自己就是p/q isHave必定为True isHave = true; //这种情况下左右子树上必定不能产生LCANode,因为pq其中一个点就是X LCANode = leftInfo.isHave || rightInfo.isHave ? X : LCANode; }else{//如果X自己不是p/q //X的左右子树上是否有p和q节点,如果有,设置子树的Info.isHave为true isHave = leftInfo.isHave || rightInfo.isHave ? true : isHave; //如果p、q两点,分别在X的左右子树上,则X就是最近公共祖先,LCANode = X LCANode = leftInfo.isHave && rightInfo.isHave ? X : LCANode; } return new Info(isHave, LCANode); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律