二叉树(深度优先)
一.好节点数目
给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。
「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。
所以不会存在下边节点的值比上边节点的值大的情况。
function goodNodes(root){
if(!root) return 0;
function dfs(node, maxSofar){
if(!node) return;
if(node.val>=maxSofar)
{
count=+;
maxSofar = node.val;
}
dfs(node.left,maxSofar);
dfs(node.right,maxSofar);
}
//上边是实现了一个函数,下边给他直接调用,开始递归
dfs(root,root.val);
return count;
}
二.路径总和III
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
前缀和是指从根节点到当前节点的路径上所有节点值的和。
从根节点开始,递归遍历每个节点,计算从根节点到当前节点的前缀和。在遍历过程中,检查是否存在一个前缀和 prefixSum - targetSum。如果存在,说明从某个节点到当前节点的路径和等于 targetSum。
|| 0 的作用
如果 prefixSumMap.get(currentSum - targetSum) 返回 undefined(即不存在这样的前缀和),则使用 0 作为默认值。
count = SumMap.get(currentSum - targetSum) || 0;
//如果前边返回结果是undefined,用0来赋值
哈希表
创建 const prefixSum = new Map();
初始化prefixSum.set(0,1);
查找 prefixSum.get(要查的值)
//返回要查的值的数量
prefixSum.get(curr-targetSum);
更新 prefixSum.set(curr,(prefixSum.get(curr) || 0)+1) //更新前缀和的出现次数
哈希表的键值对存放是 前缀和值->出现次数;
为什么要恢复前缀和出现次数
递归是一种深度优先遍历(DFS)的方式,它会沿着一条路径一直向下遍历,直到叶子节点,然后回溯到上一个节点,再遍历其他分支。
在回溯时,需要确保当前节点的状态不会影响其他分支的计算。
在递归回溯时,将当前前缀和的出现次数减 1,恢复到进入当前节点之前的状态。
这样可以确保 prefixSumMap 只记录从根节点到当前路径的前缀和,而不会影响其他路径。
避免遍历右子树的时候错误地使用左子树的前缀和。
function pathSum(root,targetSum){
const prefixSum = new Map();
prefixSum.set(0,1);
function dfs(node,curr){
if(!node) return 0;
curr+=node.val;
let count = prefixSum.get(curr-targetSum) || 0;
prefixSum.set(curr,(prefixSum.get(curr) || 0)+1);
count+=dfs(node.left,curr);
count+=dfs(node.right,curr);
prefixSum.set(curr,prefixSum.get(curr) -1);
return count;
}
return dfs(root,0);
//因为不仅要调用,还得返回最终的结果。。
}
回溯比较麻烦,
报错是没有处理空节点和。
三.二叉树中的最长交错路径
给你一棵以 root 为根的二叉树,二叉树中的交错路径定义如下:
- 选择二叉树中 任意 节点和一个方向(左或者右)。
- 如果前进方向为右,那么移动到当前节点的的右子节点,否则移动到它的左子节点。
- 改变前进方向:左变右或者右变左。
- 重复第二步和第三步,直到你在树中无法继续移动。
交错路径的长度定义为:访问过的节点数目 - 1(单个节点的路径长度为 0 )。
请你返回给定树中最长 交错路径 的长度。
function longestZigZag(root){
let maxLength = 0;
function dfs(node)
}
四.二叉树的最近公共祖先
如果 p 和 q 分别位于当前节点的左子树和右子树中,则当前节点就是它们的最近公共祖先。
如果 p 和 q 都位于左子树中,则最近公共祖先在左子树中。
如果 p 和 q 都位于右子树中,则最近公共祖先在右子树中。
function lowestCommonAncestor(root, p, q) {
// 如果当前节点为空,或者当前节点就是 p 或 q,直接返回当前节点
if (!root || root === p || root === q) {
return root;
}
// 递归在左子树中查找 p 和 q
const left = lowestCommonAncestor(root.left, p, q);
// 递归在右子树中查找 p 和 q
const right = lowestCommonAncestor(root.right, p, q);
// 如果 p 和 q 分别在当前节点的左右子树中,则当前节点就是 LCA
if (left && right) { 都不为空,说明分别在左右子树
return root;
}
// 如果只有左子树或右子树中有 p 或 q,返回非空的那个
return left || right; 只有一个不为空,说明在那个非空的树里,返回那个非空的
}
3 p=5 q=4
/ \
5 1
/ \ / \
6 2 0 8
/ \
7 4
lowestCommonAncestor(3, 5, 4)
-> lowestCommonAncestor(5, 5, 4)
-> lowestCommonAncestor(6, 5, 4) -> null
-> lowestCommonAncestor(2, 5, 4)
-> lowestCommonAncestor(7, 5, 4) -> null
-> lowestCommonAncestor(4, 5, 4) -> 4
-> return 4
-> return 5
-> lowestCommonAncestor(1, 5, 4)
-> lowestCommonAncestor(0, 5, 4) -> null
-> lowestCommonAncestor(8, 5, 4) -> null
-> return null
-> return 5
五. 二叉树的最长交错路径
使用深度优先搜索(DFS)来遍历树,并在遍历过程中记录当前路径的交错长度。
function longestZigZag(root){
let maxLen = 0;
function dfs(node,isLeft,curr){
if(!node) return;
maxLen=Math.max(maxLen,curr);
if(isLeft){
dfs(node.right,false,curr+1);
dfs(node.left,true,1);
}
else{
dfs(node.right,false,1);
dfs(node.left,true,curr+1);
}
}
dfs(root.left,true,1);
dfs(root.right,false,1);
return maxLen;
}