二叉树深度优先DFS算法题

65 阅读4分钟

二叉树(深度优先)

一.好节点数目

给你一棵根为 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)
​
​
}
​

四.二叉树的最近公共祖先

如果 pq 分别位于当前节点的左子树和右子树中,则当前节点就是它们的最近公共祖先。

如果 pq 都位于左子树中,则最近公共祖先在左子树中。

如果 pq 都位于右子树中,则最近公共祖先在右子树中。

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;
   }