一、一些对于二叉树遍历的递归写法的体会
对于大部分题而言,二叉树遍历的递归写法可以自顶向下来写,也可以自底向上来写,因为既需要考虑父节点和左右子节点的关系,同时左右子节点也存在依赖关系。
对于必须考虑根结点或父节点,或左右子节点之间无依赖的情况,只能采取自顶向下来写。
对于左右子树之间有依赖关系,或存在无法考虑包含根结点或父节点的情况,只能采取自底向上来写。
在写自底向上方向的递归时,建议从左子树和右子树的方向上考虑,将总问题拆分成两个子问题,无需过多考虑递归过程。
可以将要求取的变量作为全局变量,在执行递归时更新全局变量,递归结束后直接返回全局变量作为答案。
可以将要求取的变量作为递归函数返回值,在执行递归时计算并将结果回传,递归结束后的返回值为答案。
对于二叉树,一般可以从自身节点是否为空或左右子节点是否为空来进行考虑,当从左右子节点方向考虑时,需要先判断左右子节点是否为空,若存在左子结点或右子节点时,再进行递归计算。
二、一些二叉树的经典习题
Leetcode965 单值二叉树
该解法为自顶向下考虑
class Solution {
public boolean isUnivalTree(TreeNode root) {
if (root == null) {
return true;
}
if (root.left != null) {
if (root.val != root.left.val || !isUnivalTree(root.left)) {
return false;
}
}
if (root.right != null) {
if (root.val != root.right.val || !isUnivalTree(root.right)) {
return false;
}
}
return true;
}
}
Leetcode563 二叉树的坡度
二叉树的坡度为左子树之和与右子树之和的差的绝对值,与左右子树存在依赖,推荐自底向上来写。
class Solution {
int ans = 0;
public int findTilt(TreeNode root) {
dfs(root);
return ans;
}
public int dfs(TreeNode node) {
if (node == null) {
return 0;
}
int sumLeft = dfs(node.left);
int sumRight = dfs(node.right);
ans += Math.abs(sumLeft - sumRight);
return sumLeft + sumRight + node.val;
}
}
Leetcode687 最长同值路径
题目中说该路径可能不经过根结点,所以只能自底向上的方式书写递归。
注意递归函数回传的是该点左右子树分别计算后符合题意的返回的值,所以不应该加上当前节点的值。
class Solution {
int res;
public int longestUnivaluePath(TreeNode root) {
res = 0;
dfs(root);
return res;
}
public int dfs(TreeNode root) {
if (root == null) {
return 0;
}
int left = dfs(root.left), right = dfs(root.right);
int left1 = 0, right1 = 0;
if (root.left != null && root.left.val == root.val) {
left1 = left + 1;
}
if (root.right != null && root.right.val == root.val) {
right1 = right + 1;
}
res = Math.max(res, left1 + right1);
return Math.max(left1, right1);
}
}
Leetcode124 二叉树中的最大路径和
题目中说该路径可能不经过根结点,所以只能自底向上的方式书写递归。
与687题差不多,注意该题需要计算和返回的是路径之和。
注意递归函数回传的是该点左右子树分别计算后符合题意的返回的值,所以不应该加上当前节点的值。
class Solution {
int maxSum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxGain(root);
return maxSum;
}
public int maxGain(TreeNode node) {
if (node == null) {
return 0;
}
// 递归计算左右子节点的最大贡献值
// 只有在最大贡献值大于 0 时,才会选取对应子节点
int leftGain = Math.max(maxGain(node.left), 0);
int rightGain = Math.max(maxGain(node.right), 0);
// 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
int priceNewpath = node.val + leftGain + rightGain;
// 更新答案
maxSum = Math.max(maxSum, priceNewpath);
// 返回节点的最大贡献值
return node.val + Math.max(leftGain, rightGain);
}
}