树 从上至下 回溯

171 阅读3分钟

这也是看的灵茶山艾府 b站视频:看到递归就晕?带你理解递归的本质!【基础算法精讲 09】+ 如何灵活运用递归?【基础算法精讲 10】 这俩个视频的观后感。最大的感悟是 从上至下的掌法 太帅太酷啦。

目标:培养从根节点开始看起的思维方式。

模版:

b站回溯视频 一般情况下 我怎么思考:

一般情况下 我的回溯问题 都是从下到上:前序遍历怎么遍历,我怎么思考这个问题。

在实际情况中,有一种对于回溯问题更好解更减少思考成本的思考方式:从上至下的思考方式:

截屏2025-02-23 上午10.40.00.png

root 为 1 维护全局变量,更新全局变量的最大值,最后返回全局变量。

这就是个好用的模版。对于我来说 tree的做法 从上至下更好思考。

我不喜欢叫这种解回溯 感觉和先xu遍历分不开 能不能帮我想个好名字

104. 二叉树的最大深度

class Solution { int min_e = INT_MAX; void dfs(TreeNode* root, int dep){ if(!root){ min_e = min(min_e, dep); return; } dfs(root->left, dep+1); dfs(root->right, dep + 1); } public: int maxDepth(TreeNode* root) { if(!root) return 0; dfs(root, 1); return min_e; } };

从下至上法

class Solution { public: int maxDepth(TreeNode* root) { if(!root) return 0; return max(maxDepth(root->left), maxDepth(root->right))+1; } };

从上至下的模版:

维护全局变量,从上至下的思考,假设到了叶子节点-》怎么处理, 然后直接dfs(root->left) dfs(root->right)

class Solution {
    int ans = 0;
    
    void dfs(TreeNode* root, int **, int**){
       if(!root) // if(!root->left && !root->right){} 后者情况需要加上if(root->left, root->right)//base case 叶子节点一定是base case 很多时候 用if(!root)只是因为如果非叶子节点,仅是某个节点的空左节点,空右节点,不影响回溯答案。 但叶子节点 一定是base case 如果走的是叶子节点的思考路径 那么一定要加上 if(root->left) dfs(); if(root->right) dfs(); 防止溢出。
       dfs(root->left,...);
       dfs(root->right,...);
    }
public:
    int maxAncestorDiff(TreeNode* root) {
        if(!root) return ...
        dfs(root, asdlkj);
        return ans;// return 全局变量
    }
};

例题: 111. 二叉树的最小深度

class Solution {
   int min_d = INT_MAX;
   void dfs(TreeNode* root, int depth){
       if(!root->left && !root->right){
           min_d = min(min_d, depth);
           return;
       }
       if(root->left) dfs(root->left, depth+1);
       if(root->right) dfs(root->right, depth+1);
   }
public:
   int minDepth(TreeNode* root) {
       if(!root) return 0;
       dfs(root, 1);
       return min_d;
   }
};

leetcode 1026:


class Solution {
    int ans = 0;
    void findAns(TreeNode* root, int mn, int mx){
        mn = min(root->val, mn);
        mx = max(root->val, mx);
        if(!root->left && !root->right){
            ans = max(ans, mx - mn);
            return; 
        }
        if(root->left) findAns(root->left, mn, mx);
        if(root->right) findAns(root->right, mn, mx);
    }
public:
    int maxAncestorDiff(TreeNode* root) {
        if(!root) return 0;
        findAns(root, root->val, root->val);
        return ans;
    }
};

LeetCode 98

灵神提供的思路 但我的写法比灵神简单干净利落 oh yeah。 注意范围LLONG_MIN, LLONG_MAX;


class Solution {
    bool beAGoodTree(TreeNode* root, long long mn, long long mx){
        if(!root) return true;
        if(root->val >= mx || root->val <= mn) return false;
        return beAGoodTree(root->right, root->val, mx) && beAGoodTree(root->left, mn, root->val);
    }
public:
    bool isValidBST(TreeNode* root) {
        return beAGoodTree(root, LLONG_MIN, LLONG_MAX);
    }
};

微难题: leetcode 1080: 出的题目有点绕。

1372. 二叉树中的最长交错路径