09 计算机是怎么执行递归的?

160 阅读3分钟

灵神【基础算法精讲】视频的个人笔记。

问题

  1. 如何思考二叉树相关问题?
  2. 为什么需要使用递归?
  3. 为什么这样写就一定能算出正确答案?
  4. 计算机是怎么执行递归的?
  5. 另一种递归思路

求二叉树的最大深度

104. 二叉树的最大深度 - 力扣(LeetCode)

flowchart TB
	3 --> 9 & 20
	20 --> 15 & 7

不要一开始就陷入细节,而是思考子问题:整棵树与其左右子树的关系

flowchart TB
	3 --> 左子树 & 右子树

整棵树的最大深度 = max(左子树的最大深度, 右子树的最大深度) + 1

此时将视角放到右子树上,同样可以用该公式来求出右子树的最大深度,计算的方式一样

flowchart TB
	20 --> 15 & 7

  • 原问题:计算整棵树的最大深度
  • 子问题:计算左、右子树的最大深度 此时原问题拆解成了两个子问题,子问题还能继续拆解成两个问题, 一直到没有结点为止,这就是边界条件(base case)。

计算方式一样,那么代码肯定也一样,这种问题就适合用递归

int maxDepth(TreeNode* root) {
	if(root == nullptr) return 0;
	
	return max(maxDepth(root -> left), maxDepth(root -> right)) + 1;
}

另一种递归思路

结果不用返回值,用全局变量

递归过程传参数当前层数,更新结果。

int res = 0;

void dfs(TreeNode* root, int cnt) {
	if(root == nullptr) return;
	cnt++;
	res = max(res, cnt);

	dfs(root -> left, cnt);
	dfs(root -> right, cnt);
}

课后作业

111. 二叉树的最小深度 - 力扣(LeetCode)

class Solution {
public:
    int DFS(TreeNode* root) {
        if(root == nullptr) return 0;
        //左右子树都为空
        if(root->left == nullptr && root->right == nullptr)
            return 1;
        //左右子树其中一个为空
        if(root->left == nullptr || root->right == nullptr)
            return DFS(root->left) + DFS(root->right) + 1;
        //左右子树都不为空
        return min(DFS(root->left), DFS(root->right)) + 1;
    }

    int minDepth(TreeNode* root) {
        return DFS(root);
    }
};

112. 路径总和 - 力扣(LeetCode)

class Solution {
public:
    bool dfs(TreeNode* root, int t) {
        if(root == nullptr) return false;

        if(root->left == nullptr && root->right == nullptr) //叶子节点
            if(t == root->val)
                return true;
        
        //左右子树 其中一子树满足条件就行
        return dfs(root->left, t - root->val) || dfs(root->right, t - root->val);
    }

    bool hasPathSum(TreeNode* root, int targetSum) {
        return dfs(root, targetSum);
    }
};

113. 路径总和 II - 力扣(LeetCode)

该题用了递归的第二种思路,结果路径是类变量

class Solution {
public:
    vector<int> path; //路径
    vector<vector<int>> ans;

    void dfs(TreeNode* root, int t) {
        if(root == nullptr) return;
        
        path.push_back(root->val); //保存路径
        
        if(root->left == nullptr && root->right == nullptr) //叶子节点
            if(root->val == t)
                ans.push_back(path);
        
        dfs(root->left, t - root->val);
        dfs(root->right, t - root->val);

        path.pop_back(); //退出路径
    }

    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        dfs(root, targetSum);
        return ans;
    }
};

129. 求根节点到叶节点数字之和 - 力扣(LeetCode)

这题跟上一题很像,可以继续用一样的思路。 LeetCode官方题解中,有不使用全局变量的解法。

class Solution {
public:
    int path = 0; //路径
    int ans = 0;

    void dfs(TreeNode* root) {
        if(root == nullptr) return;

        path *= 10;
        path += root->val; //保存路径

        //叶子节点
        if(root->left == nullptr && root->right == nullptr) {
            ans += path;
        }

        dfs(root->left);
        dfs(root->right);

        path /= 10; //退出路径
    }

    int sumNumbers(TreeNode* root) {
        dfs(root);
        return ans;
    }
};

257. 二叉树的所有路径 - 力扣(LeetCode)

官方题解中的深搜,先加root->val,不为叶子结点才加->。 我就把val->一起加,会麻烦一点。 这题字符串回溯比较麻烦,就作为参数了。

class Solution {
public:
    vector<string> ans;

    void dfs(TreeNode* root, string path) {
        if(root == nullptr) return;

        if(root->left == nullptr && root->right == nullptr) { //叶子节点
            ans.emplace_back(path + "->" + to_string(root->val));
        }

        string newPath = path == "" ?
                to_string(root->val) :
                path + "->"+ to_string(root->val);

        dfs(root->left, newPath);
        dfs(root->right, newPath);

    }

    vector<string> binaryTreePaths(TreeNode* root) {
        if(root->left == nullptr && root->right == nullptr) {
            return vector<string>{to_string(root->val)};
        }

        dfs(root, "");
        return ans;
    }
};