解决树问题用的比较多的也是DFS算法,接下来介绍一下我之前容易错的几道有关树的遍历的题目:
public int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
depth(root);
return max;
}
public int depth(TreeNode root) {
if (root == null) return 0;
int leftDepth = depth(root.left);
int rightDepth = depth(root.right);
max = Math.max(max, leftDepth + rightDepth);
return Math.max(leftDepth, rightDepth) + 1;
}
public boolean result = true;
public boolean isBalanced(TreeNode root) {
maxDepth(root);
return result;
}
public int maxDepth(TreeNode root) {
if (root == null) return 0;
int l = maxDepth(root.left);
int r = maxDepth(root.right);
if (Math.abs(l - r) > 1) result = false;
return 1 + Math.max(l, r);
}
思路:给定一个二叉树,它的每个结点都存放着一个整数值。找出路径和等于给定数值的路径总数。路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
这一题可以说我已经错过无数次了,主要是因为审题不清,首先该路径不一定是根节点开始叶子节点结束(✿◕‿◕✿),因此,我们在进行递归的时候不可以只从根节点开始!所以我们需要用到双重递归,首先遍历每一个节点,然后把每一个节点作为起点继续遍历其后续节点o(▽)q
public int pathnumber;
public int pathSum(TreeNode root, int sum) {
if(root == null) return 0;
Sum(root,sum);
pathSum(root.left,sum);
pathSum(root.right,sum);
return pathnumber;
}
public void Sum(TreeNode root, int sum){
if(root == null) return;
sum-=root.val;
if(sum == 0){
pathnumber++;
}
Sum(root.left,sum);
Sum(root.right,sum);
}
public boolean isSubtree(TreeNode s, TreeNode t) {
if(s == null && t == null) return true;
if (s == null) return false;
return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t);
}
public boolean isSubtreeWithRoot(TreeNode s, TreeNode t) {
if (t == null && s == null) return true;
if (t == null || s == null) return false;
if (t.val != s.val) return false;
return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right);
}
public int minDepth(TreeNode root) {
return dfs(root);
}
public int dfs(TreeNode root){
if(root == null) return 0;
int left = dfs(root.left);
int right = dfs(root.right);
int min = Math.min(left,right);
return min+1;
}
这又是一个阅读理解题,因为我又又又败在题目上了,我枯了(っ °Д °;)っ。注意,题目要求的是叶子节点!所以测试用例【1,2】我的输出是1就错了,1并不是叶子节点!
所以应该再加一个判断如果根节点的左或右子树为空的话是构不成子树的!
所以完整代码如下:
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
} // null节点不参与比较
if (root.left == null && root.right != null) {
return 1 + minDepth(root.right);
} // null节点不参与比较
if (root.right == null && root.left != null) {
return 1 + minDepth(root.left);
}
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
}
3
/ \
9 20
/ \
15 7
There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.
首先,叶子节点要满足其没有子节点,并且自身也不为空,那么就分为几种情况,若没有右节点,则去左节点找,若有左节点则判断左节点是不是叶子节点,若不是则继续往左节点和右节点中找,完整递归的过程如下:
public int sumOfLeftLeaves(TreeNode root) {
if(root==null){
return 0;
}else if(root.left==null){
return sumOfLeftLeaves(root.right);
}else{
if(root.left.left==null&&root.left.right==null){
return root.left.val+sumOfLeftLeaves(root.right);
}else{
return sumOfLeftLeaves(root.left)+sumOfLeftLeaves(root.right);
}
}
}
其实可以简化一下,因为很多可能可以合并的:
public int sumOfLeftLeaves(TreeNode root) {
if(root==null) return 0;
int res = 0;
if(root.left!=null&&root.left.left==null&&root.left.right==null){
res = res+root.left.val;
}
return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right) + res;
}
1
/ \
4 5
/ \ \
4 4 5
Output : 2
先来展示我的奇葩答案,ԾㅂԾ,:
int max = 0;
public int longestUnivaluePath(TreeNode root) {
if(root == null) return 0;
return Math.max(dfs(root,root.val,1),Math.max(longestUnivaluePath(root.left),longestUnivaluePath(root.right)));
}
public int dfs(TreeNode root,int pre,int sum){
if(root == null) return 0;
if(pre == root.val){
sum++;
return Math.max(dfs(root.left,pre,sum),dfs(root.right,pre,sum))+sum;
}else{
sum = 1;
return Math.max(dfs(root.left,root.val,sum),dfs(root.right,root.val,sum))+sum;
}
}
我的想法是遍历每个节点,然后找他们的最长路径,这个答案错的很明显因为没有考虑左右子树会连成一条通路,执着的我是不会放弃的!坚决不参考别人的答案!( •̀ ω •́ )✧,于是我又修改了代码:
int max = 0;
public int longestUnivaluePath(TreeNode root) {
if(root == null) return 0;
return Math.max(dfs(root,root.val),Math.max(longestUnivaluePath(root.left),longestUnivaluePath(root.right)));
}
public int dfs(TreeNode root,int pre){
if(root == null) return 0;
int left = 0;
int right = 0;
if(root.left != null){
left = root.left.val == pre? dfs(root.left,pre) + 1:0;
}
if(root.right != null){
right = root.right.val == pre? dfs(root.right,pre) + 1:0;
}
max = Math.max(max,left+right);
return Math.max(left,right);
}
但是跑出来还是有问题,因为dfs函数的返回值并不是我们要的最大路径长,但是我们不能将dfs函数后的返回值改为max,因为这会影响更新,所以直接在主函数返回max就好啦!主函数改成这样就可以啦:
public int longestUnivaluePath(TreeNode root) {
if(root == null) return 0;
dfs(root,root.val);
longestUnivaluePath(root.left);
longestUnivaluePath(root.right);
return max;
}
但是这样一想,完全没必要把这三行代码放在主函数中呀,直接放在dfs函数里遍历不是更方便?
但是需要注意的是,删除其中两个调用longestUnivaluePath函数的代码是肯定不够的,还需要对dfs函数体进行修改,因为上面的dfs函数体一直比较的是和最初pre的值,但是如果不用双重递归的话,pre的值必须动态的变化,也就是只和前一个父节点比较,完整代码如下:
int ans;
public int longestUnivaluePath(TreeNode root) {
ans = 0;
arrowLength(root);
return ans;
}
public int arrowLength(TreeNode node) {
if (node == null) return 0;
int left = arrowLength(node.left);
int right = arrowLength(node.right);
int arrowLeft = 0, arrowRight = 0;
if (node.left != null && node.left.val == node.val) {
arrowLeft = left + 1;
}
if (node.right != null && node.right.val == node.val) {
arrowRight = right + 1;
}
ans = Math.max(ans, arrowLeft + arrowRight);
return Math.max(arrowLeft, arrowRight);
}
Input:
2
/ \
2 5
/ \
5 7
Output: 5
首先我们直到根节点就是最小值,所欲从左右子树找最小节点即可:
public int findSecondMinimumValue(TreeNode root) {
if (root == null) return -1;
if (root.left == null && root.right == null) return -1;
int leftVal = root.left.val;
int rightVal = root.right.val;
if (leftVal == root.val) leftVal = findSecondMinimumValue(root.left);
if (rightVal == root.val) rightVal = findSecondMinimumValue(root.right);
if (leftVal != -1 && rightVal != -1) return Math.min(leftVal, rightVal);
if (leftVal != -1) return leftVal;
return rightVal;
}
还可以简化一下:
public int findSecondMinimumValue(TreeNode root) {
return myfun(root, root.val);
}
public int myfun(TreeNode root, int val) {
if (root == null) return -1;
if (root.val > val) return root.val;
int l = myfun(root.left, val);
int r = myfun(root.right, val);
if (l > val && r > val) return Math.min(l,r);
return Math.max(l,r);
}
最后一行返回最大值是因为不满足以上条件的一定是左右子树的值等于根节点的值,此时取一个较大的(这个数一定是等于根节点值或者比根节点值略大一点的,也就是所需结果) 。
public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return root;
}
if (root == p || root == q) {//当找到p或者q的是时候就会返回pq
return root; }
TreeNode left = LowestCommonAncestor(root.left, p, q);//返回的结点进行保存,可能是null
TreeNode right = LowestCommonAncestor(root.right, p, q);//也可能是pq,还可能是公共祖先
if (left != null && right != null) {
return root;
} else if (left != null) {
return left;//还有一种可能就是,由下面返回的公共祖先,并将这个值一路返回到最表层
} else if (right != null) {
return right;
}
return null;
}
这样看起来有些乱,我们整理一下,简化一下代码:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
return left == null ? right : right == null ? left : root;
}