543. 二叉树的直径
思路:
首先题目中说可以不穿过根节点,我自然就想到了双重递归,把每个节点都作为起点然后再遍历:
int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
if(root == null) return 0;
dfs(root);
diameterOfBinaryTree(root.left);
diameterOfBinaryTree(root.right);
return max;
}
public int dfs(TreeNode root){//错误
if(root == null) return 0;
int left = dfs(root.left);
int right = dfs(root.right);
max = Math.max(max,left+right+2);
return Math.max(left,right)+1;
}
这样写就出错了,因为假如遇到一个叶子节点max仍然会被更新为2,所以这样是不对的,我们只需要提前判断一下当前节点的左右子节点是否为空即可:
int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
if(root == null) return 0;
dfs(root);
diameterOfBinaryTree(root.left);
diameterOfBinaryTree(root.right);
return max;
}
public int dfs(TreeNode root){
if(root == null) return 0;
int left = root.left == null? 0: dfs(root.left) + 1;
int right = root.right == null? 0: dfs(root.right) + 1;
max = Math.max(max,left+right);
return Math.max(left,right);
}
但是还有一个细节需要注意,这一题真的需要双重递归吗?实际上不需要,因为在对根节点的子节点进行遍历的时候都会求出其对应的max,所以已经隐式的把每个节点作为起点遍历过了!!
所以不要把这一题和 113. 路径总和 II、437. 路径总和 III 弄混了~
687. 最长同值路径
思路:
这一题也是类似的,理论上是不需要双重递归的:
int max = 0;
public int longestUnivaluePath(TreeNode root) {
if(root == null) return 0;
dfs(root);
//longestUnivaluePath(root.left);
//longestUnivaluePath(root.right);
return max;
}
public int dfs(TreeNode root){
if(root == null) return 0;
int left = 0;
int right = 0;
if(root.left != null && root.val == root.left.val){
left = dfs(root.left) + 1;
}
if(root.right != null && root.val == root.right.val){
right = dfs(root.right) + 1;
}
max = Math.max(max,right + left);
return Math.max(right,left);
}
但是我没想到去掉这个双重递归竟然错了!!
问题在哪里呢,先看一个正确的代码:
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);
}
乍一看,我觉得我和他写的完全一样啊,凭什么我是错的!除了我们唯一的区别就是我的dfs放在了if的执行体中,实际上这就是我的错误原因。
首先我们不用双重递归的初衷就是在只遍历根节点的时候顺带更新遍历所有节点,但是如果我们把dfs函数放到if执行体中:
if(root.left != null && root.val == root.left.val){
left = dfs(root.left) + 1;
}
if(root.right != null && root.val == root.right.val){
right = dfs(root.right) + 1;
}
这就导致并不是所有节点都有机会进入dfs函数中更新max(不满足root.val == root.left.val的节点甚至没有机会被dfs函数遍历,但是root.left != null这个条件并不会影响什么),所以在这种情况下只能与双重递归配合来遍历更新所有的节点!!
因此,要想省去双重递归,就得把dfs函数放在判断外,无论如哈必须执行!!所以应改为:
int max = 0;
public int longestUnivaluePath(TreeNode root) {
if(root == null) return 0;
dfs(root);
//longestUnivaluePath(root.left);
//longestUnivaluePath(root.right);
return max;
}
public int dfs(TreeNode root){
if(root == null) return 0;
int left = dfs(root.left);
int right = dfs(root.right);
int left1 = 0;
int right1 = 0;
if(root.left != null && root.val == root.left.val){
left1 = left + 1;
}
if(root.right != null && root.val == root.right.val){
right1 = right + 1;
}
max = Math.max(max,right1 + left1);
return Math.max(right1,left1);
}
同样的,543. 二叉树的直径 这题也可以把dfs遍历写在条件判断外:
int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
dfs(root);
return max;
}
public int dfs(TreeNode root){
if(root == null) return 0;
int left = dfs(root.left);
int right = dfs(root.right);
int sum = left + right;
max = Math.max(sum,max);
return Math.max(left,right) + 1;
}
但是这里是可写可不写的,因为int left = root.left == null? 0: dfs(root.left) + 1;中的条件只筛掉了叶子节点,而叶子节点本身我们也不需要对其dfs遍历~只是要样成这个好习惯呀(●ˇ∀ˇ●)