二叉树路径问题
二叉树的路径问题,分为以下几种情况:
- 二叉树的所有路径(从根节点到叶子结点)
- 路径总和
- 二叉树的直径
一、二叉树的所有路径(从根节点到叶子结点)
分析:采用哪种模式?需要注意什么?
我的思考:
- 模式:因为一次遍历便可以得到结果,所以采用遍历模式,由于存在回溯的操作,所以前序代码位置和后序代码位置均应有代码,代码的整体框架类似于求二叉树的最大深度。
- 节点操作:在进入节点时,将该节点的值加到路径里去,离开节点时,剔除该节点的值,进行回溯。
- 注意:在叶子节点处就应该终止,类似于二叉树求最小深度。
代码如下:
class Solution {
LinkedList<String> res = new LinkedList<>();
LinkedList<String> path = new LinkedList<>();
public List<String> binaryTreePaths(TreeNode root) {
traverse(root);
return res;
}
void traverse(TreeNode root) {
if(root == null) return;
if(root.left == null && root.right == null) {
path.add(root.val + "");
res.add(String.join("->",path));//到达叶子结点处,此时path是一条完整路径
path.removeLast();
}
path.add(root.val + "");//前序代码位置,存储节点数值
traverse(root.left);
traverse(root.right);
path.removeLast();//后序代码位置,回溯到上一个节点
}
}
修改代码,该代码更符合本文的思想。
class Solution {
LinkedList<String> res = new LinkedList<>();
LinkedList<String> path = new LinkedList<>();
public List<String> binaryTreePaths(TreeNode root) {
traverse(root);
return res;
}
void traverse(TreeNode root) {
if(root == null) return;
path.add(root.val + "");//前序代码位置,存储节点数值
if(root.left == null && root.right == null) {
res.add(String.join("->",path));//到达叶子结点处,此时path是一条完整路径
}
traverse(root.left);
traverse(root.right);
path.removeLast();//后序代码位置,回溯到上一个节点
}
}
二、路径总和[不够熟练
😵]
1. 路径总和1
判断该树中是否存在根节点到叶子节点路径上所有节点值相加等于目标和 targetSum。
1.1 遍历模式
- 迭代法:
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
LinkedList<TreeNode> stack = new LinkedList<>();
LinkedList<Integer> sum = new LinkedList<>();
//两个栈同时协作,类似于路径题,不过此类型题迭代法更容易
stack.push(root);
sum.push(root.val);
while(!stack.isEmpty()) {
TreeNode cur = stack.pop();
int count = sum.pop();
if(cur.left == null && cur.right == null) {
if(count == targetSum) {
return true;
}
}
if(cur.right != null) {
stack.push(cur.right);
sum.push(count+cur.right.val);
}
if(cur.left != null) {
stack.push(cur.left);
sum.push(count+cur.left.val);
}
}
return false;
}
}
- 递归法:
递归法这里我一开始用的是targetSum -= root.val,但是计算不对,不知是何问题
class Solution {
boolean found = false;
// 记录遍历过程中的路径和
int curSum = 0;
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) return false;
traverse(root,targetSum);
return found;
}
// 二叉树遍历函数
void traverse(TreeNode root, int targetSum) {
if (root == null) return;
// 前序遍历位置
curSum += root.val;
if (root.left == null && root.right == null) {
if (curSum == targetSum) {
found = true;
}
}
traverse(root.left,targetSum);
traverse(root.right,targetSum);
// 后序遍历位置
curSum -= root.val;
}
}
减法操作
class Solution {
boolean find = false;
public boolean hasPathSum(TreeNode root, int targetSum) {
traverse(root,targetSum);
return find;
}
void traverse(TreeNode root, int targetSum) {
if(root == null) return;
targetSum -= root.val;//进入节点时,减去该节点的值
if(root.left == null && root.right == null) {
if(targetSum == 0) {
find = true;
}
}
traverse(root.left,targetSum);
traverse(root.right,targetSum);
}
}
加法、减法操作的区别:
因为你额外递归函数traverse的参数携带了targetSum,所以targetSum只和当前时刻状态的root有关,即targetSum -= root.val不会影响traverse(node,targetSum)中的targetSum,而加法中,curSum是一个全局变量,所以需要先加后减,但是减法中,targetSum是一个当时量,所以只需要减法操作,参数列表中的targetSum会自动将targetSum归位,相当于是traverse(node,targetSum)中的targetSum存储了每一步的值。
1.2 分治模式
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
if(root.left == null && root.right == null) {
return targetSum == root.val;
}
//左右子树有一个成立即可
return hasPathSum(root.left,targetSum-root.val) || hasPathSum(root.right,targetSum-root.val);
}
}
2. 路径总和2
从根节点到叶子节点路径总和等于给定目标和的路径。
2.1 遍历模式
该题可以看做是求所有路径和路径总和1的整合
需要注意的是,进入节点时,对节点进行操作的代码应该放到判断叶子结点之前。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
tarverse(root,targetSum);
return res;
}
void tarverse(TreeNode root, int targetSum) {
if(root == null) return;
path.add(root.val);//path是全局变量,需要先进后出
targetSum -= root.val;//targetSum是参数,保存当前值,只需要进。
if(root.left == null && root.right == null) {
if(targetSum == 0) {
res.add(new LinkedList<Integer>(path));
}
}
tarverse(root.left,targetSum);
tarverse(root.right,targetSum);
path.removeLast();//path是全局变量,需要先进后出
}
}
三、二叉树的直径[再看看
😭]
二叉树的直径:任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
分析:该题归根结底是求最大深度的问题,对二叉树的最大深度问题稍加改进即可。
二叉树的最大直径
:左子树的最大深度 + 右子树的最大深度
class Solution {
int maxDiameter = 0;
public int diameterOfBinaryTree(TreeNode root) {
maxDepth(root);
return maxDiameter;
}
int maxDepth(TreeNode root) {
if(root == null) return 0;
int left = maxDepth(root.left);
int right = maxDepth(root.right);
//去掉此行,该函数就是求二叉树的最大深度
int myDiameter = Math.max(maxDiameter, left+right);
return 1 + Math.max(left, right);//求最大深度的公式
}
}
题目链接
leetcode-257:二叉树的所有路径
leetcode-112:路径总和
leetcode-113:路径总和II
leetcode-543:二叉树的直径
环环无敌大可爱
😄