查找二叉树的两个节点的最小公共祖先,下面分为两种情况来考虑这个问题,第一种是BST
利用二叉搜索树的性质, 左子树和右子树的节点大小关系(递归处理)
无非是两种情况,第一中两个子节点不再同一颗树上,那么最小的公共节点就是两者的根节点,然后采用递归处理方式处理两边节点情况
public TreeNode lcs(TreeNode root, TreeNode p, TreeNode q)
{
if(root==null) return root;
if(root.val>p.val && root.val>q.val)
//两个树都在左边下
return lcs(root.left, p ,q);
if(root.val<p.val && root.val<p.val)
//两个树都在右边的情况下
return lcs(root.right, p ,q);
return root;
}
采用while(去除代替递归方法过程,不断循环下去)
public TreeNode lcs(TreeNode root, TreeNode p, TreeNode q){
//先找到最大值,
int max = p.va;
int min = q.val;
if(min>max){
int temp = max;
max =min;
min = temp;
}
while(true){
if(root.val<min){
root= root.right;
}
else if(root.val>max){
root= root.left;
}
else
return root;
}
}
查找路径,如果两个节点不是在同一颗树上的话,则必然查找一个节点在上面的情况,
Share my understanding of what lowestCommonAncestor() does:
if both p and q exist in Tree rooted at root, then return their LCA
if neither p and q exist in Tree rooted at root, then return null
if only one of p or q (NOT both of them), exists in Tree rooted at root, return it
// If the root is one of a or b, then it is the LCA
// If both nodes lie in left or right then their LCA is in left or right,
// Otherwise root is their LCA
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null;
//root等于p,q其中的一个情况即可
if(root==p || root==q) return root;
TreeNode left = lowestCommonAncestor(root.left, p,q);
TreeNode right = lowestCommonAncestor(root.right, p,q);
//左边两边的值
if(left!= null && right != null)
return root;
if(left!=null)
return left;
if(right!=null)
return right;
return null;
}
}
视频讲解
动态规划和树结合起来,不能同时取父子节点,只能取
只有两种可能,取根节点和根节点的子孙节点,取左右子节点为根的节点
//判断左右节点是否存在
result =max(root.val + dp(root.left.right) + dp(root.left.left)+ dp(root.right.left) +dp(root.right.right)
基础代码如下:
将树抽象根节点,左右子树节点情况
递归方法(出口)
取上面的两种情况最大值,类似于动态规划题目
class Solution {
public int rob(TreeNode root) {
//对于一个节点而言
//有三种情况,第一种是不要这个节点,可以加入左右两个节点即可(可同时加入)
//第二种是不要要加入该节点,这种情况就是不要左右子节点的情况
//程序的跳出接口,来到最后一个节点初
if(root==null) return 0;
//叶节点
int root_val = 0;
//向上层值返回结果即可
if(root.right==null && root.left==null) {
return root.val;
}
//有一边为叶子节点
//下面这种两种情况是
//排除了根节点了的情况
//综合处理该情况
//采用叠加的方式
int left = rob(root.left);
int right = rob(root.right);
//防止空指针异常,左右子树有一种情况
if(root.left != null)
root_val += rob(root.left.right)+ rob(root.left.left);
if(root.right!=null)
root_val += rob(root.right.right) + rob(root.right.left);
return Math.max(root.val+ root_val, left + right);
}
}
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ /
11 13 4
/ \
7 2 1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
给一个定植,给一个树判断有没有一条路径从根节点到叶子节点加起来相等的情况
从根节点到叶子节点有没有一条
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
//递归遍历 , 一定要加上这种情况,判断是否为空
if(root==null) return false;
if(root.right==null && root.left==null && sum==root.val) return true;
// if(sum==0 && root.right==null && root.left==null)
//判断root是否为空值
if(root.right!=null || root.left!=null)
return hasPathSum(root.right,sum-root.val) || hasPathSum(root.left,sum-root.val);
return false;
}
}
采用stack 进行深度优先搜索遍历进行发现情况
public boolean hasPathSum(TreeNode root, int sum) {
Stack <TreeNode> stack = new Stack<> ();
stack.push(root) ;
while (!stack.isEmpty() && root != null){
TreeNode cur = stack.pop() ;
if (cur.left == null && cur.right == null){
if (cur.val == sum ) return true ;
}
//左右两边节点
if (cur.right != null) {
//
cur.right.val = cur.val + cur.right.val ;
stack.push(cur.right) ;
}
if (cur.left != null) {
cur.left.val = cur.val + cur.left.val;
stack.push(cur.left);
}
}
return false ;
}
跟上面的一样,找出所有满足条件的情况,类似于回溯算法,找到所有满足题意的情况
Given a binary tree and a sum, find all root-to-leaf paths where each path’s sum equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
分析类似于普通的回溯算法,包括两个步骤,
注意回溯的点
不同于以往那种回溯点位置
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> result = new ArrayList<>();
if(root==null) return result;
//中间结果
List<Integer> tmp=new ArrayList<>();
return dp(root, result,sum, tmp);
}
private List<List<Integer>> dp(TreeNode root,List<List<Integer>> result,int sum,List<Integer> tmp){
//满足题意的前提条件
if(root==null) return null;
//向下回溯的点实在这里
tmp.add(root.val);
//正式可以添加的结果
if(sum==root.val &&root.right==null && root.left==null ){
// tmp.add(root.val);(在这里回溯是错误点情况下)
result.add(new ArrayList<>(tmp));
//处理根根节点情况,
}
dp(root.left,result,sum-root.val, tmp);
dp(root.right,result,sum-root.val,tmp);
tmp.remove(tmp.size()-1);
return result;
}
}
如果上面这道题目硬要按照以前那种回溯算法写点话
多次回溯(中间结果进行删除节点情况)比如下面这种情况
//第二种回溯方法
private List<List<Integer>> dp_1(TreeNode root,List<List<Integer>> result,int sum,List<Integer> tmp){
//满足题意的前提条件
if(root==null) return null;
//hui
//正式可以添加的结果
if(sum==root.val &&root.right==null && root.left==null ){
//满足题意中间结果可以添加,
tmp.add(root.val);
//最终结果可以添加情况
result.add(new ArrayList<>(tmp));
//回溯素算法
tmp.remove(tmp.size()-1);
//处理根根节点情况,
}
//到根节点下面到节点情况
tmp.add(root.val);
//左右节点到情况
if(root.left!=null) {
dp(root.left,result,sum-root.val,tmp);
tmp.remove(tmp.size()-1);
}
if(root.right!=null) {
dp(root.right,result,sum-root.val,tmp);
tmp.remove(tmp.size()-1);
}
//左右两种情况
return result;
}
第二种方法是自己能想到到回溯算法情况
Given a non-empty binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.
Example 1:
Input: [1,2,3]
1
/ \
2 3
Output: 6
Example 2:
Input: [-10,9,20,null,null,15,7]
Output: 42
二叉树最大路径和
路径可以 :至少有一个节点情况
一条路径部分左右的情况下如何取得最大值,这条路径可以不是从原点
最大值的可能情况,虽然题目说不需要遍历根节点,其实是需要根节点的。
把根节点理解成父节点更好一些,因为需要父节点和别的连成一条线。
问题抽象: 只有三个节点情况(左节点、右节点、根节点)
最大值来自:
当左右两边子节点值>0 取当值root+左边的某条路径+右边的某条路径
== 当root.right.val<0 左边的某条路径 + root==
当root.left.val<0 root + 右边的某条路径
root
判断左右子节点的是否大于0或者小于0,逐步判断大小的情况
由于这是一个很简单的例子,我们很容易就能找到最长路径为7-11-4-13,那么怎么用递归来找出正确的路径和呢?根据以往的经验,树的递归解法一般都是递归到叶节点,然后开始边处理边回溯到根节点。
那么我们就假设此时已经递归到结点7了,那么其没有左右子节点,所以如果以结点7为根结点的子树最大路径和就是7。
然后回溯到结点11,如果以结点11为根结点的子树,我们知道最大路径和为7+11+2=20,此时ret =20 , 但是此时返回给上一层(只能有一般,即是7-11 或者 11-2 这条路径或者11三种情况, 对应于 return Math.max(root.val, Math.max(root.val+left, root.val+right)); (根节点是一定要的,因为路径不是不可能跳过根节点的)
但是当回溯到结点4的时候,对于结点11来说,就不能同时取两条路径了,只能取左路径,或者是右路径,所以当根结点是4的时候,那么结点11只能取其左子结点7,因为7大于2。所以,对于每个结点来说,我们要知道经过其左子结点的path之和大还是经过右子节点的path之和大。
所以,对于每个结点来说,我们要知道经过其左子结点的path之和大还是经过右子节点的path之和大。那么我们的递归函数返回值就可以定义为以当前结点为根结点,到叶节点的最大路径之和,然后全局路径最大值放在参数中,用结果res来表示。
在递归函数中,如果当前结点不存在,那么直接返回0。否则就分别对其左右子节点调用递归函数,由于路径和有可能为负数,而我们当然不希望加上负的路径和,所以我们和0相比,取较大的那个,就是要么不加,加就要加正数。然后我们来更新全局最大值结果res,就是以左子结点为终点的最大path之和加上以右子结点为终点的最大path之和,还要加上当前结点值,这样就组成了一个条完整的路径。而我们返回值是取left和right中的较大值加上当前结点值,因为我们返回值的定义是以当前结点为终点的path之和,所以只能取left和right中较大的那个值,而不是两个都要,
==每一个节点处都保存一个最大值,每处理一个节点处都要更新这个最大值,返回是结果作为处理上一层节点的路径即可
下面谈论一种比较特殊的情况即可:
比较难处理的一种情况,就是三个节点同时为负数的时候如何进行处理
helper(-2) 和helper(-4)的返回值为-2(最大值为-2),helper(-4)的返回值为-4 这条路径,最大值为-2,-4进行比较
所以最大值是不会进行改变的,所以最大值依然是-2
==-2 和-4的返回值为解决-3处的值提供来路径进行处理的情况下 ==
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int ret = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
if (root == null) {
return 0;
}
helper(root);
return ret;
}
private int helper(TreeNode root) {
if (root == null) {
return 0;
}
//ret 记录的是最大值在该点处可以达到的值, 后期return 是向上返回的路径意思
int left = helper(root.left);
int right = helper(root.right);
// Computes the max ret it could get in current recursion
if (left < 0) {
ret = Math.max(ret, Math.max(root.val, root.val+right));
} else if (right < 0) {
ret = Math.max(ret, Math.max(root.val, root.val+left));
} else {
ret = Math.max(ret, left+root.val+right);
}
// Returns the largest path starting with current node,
// It could be the single node, or the node + left or right path
比如在上面的情况 在上述处理 11时,
return Math.max(root.val, Math.max(root.val+left, root.val+right));
}
}
判断一个树是否是平衡二叉树的情况下,采用向下递归的方法
Top-down,就是从上到下,先求出节点的depth,然后再看子节点是否balance。这里其实求depth已经计算过节点了,然后还要做两个isBalanced,多做了运算。这个算法的效率是很低的,每个节点都重新计算高度,浪费了大量信息。
属于先求出每个节点的左右高度,再进行判断的情况,
存在重复计算的情况下解决:
如果某个字节点下面以后, 先判断以根节点左右
class solution {
public:
int depth (TreeNode *root) {
if (root == NULL) return 0;
return max (depth(root -> left), depth (root -> right)) + 1;
}
bool isBalanced (TreeNode *root) {
if (root == NULL) return true;
int left=depth(root->left);
int right=depth(root->right);
return abs(left - right) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
};
Time Complexity - O(n2), Space Complexity - O(n)
与上述不同的是,down-up, 采用的方法是计算左右高度的同时,判断是否满足题意(是否是)
避免重复计算多次,采用从下面开始到上面进行标志,如果某个子树不满足题意的,
直接标志高度为-1,直接不满足题意,可以不用再进行计算即可,
如果满足题意,按照正常情况下进行计算即可
计算高度的同时进行判断是否满足题意的情况,如果不满足直接返回一个-1,不用再进行计算即可
该道题目的思路视频
https://www.youtube.com/watch?v=TWDigbwxuB4
讲解
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
return height(root)!=-1;
}
private int height(TreeNode root){
//下到上了,下面的如果不满题的话,
if(root==null) return 0;
int l=height(root.left);
//先进行判断,如果左子树都不满足bst,,则不用再次进行计算,直接返回错误的结果即可
// - 1标志是否满足题意的情况
if(l==-1)
return -1;
int r = height(root.right);
if(r==-1)
return -1;
//如果计算处理的条件不满足的话
if(Math.abs(l-r)>1)
return -1;
return Math.max(l,r)+1;
}
}