力扣 | 动态规划 | 动态规划在树的应用

文章目录

  • [一、96. 不同的二叉搜索树](#一、96. 不同的二叉搜索树)
  • [二、95. 不同的二叉搜索树 II](#二、95. 不同的二叉搜索树 II)
  • [三、337. 打家劫舍 III](#三、337. 打家劫舍 III)

一、96. 不同的二叉搜索树

LeetCode:96. 不同的二叉搜索树

只求个数实际上比较简单,定义dp[i]表示结点个数为i的二叉搜索树的种树。(其实和记忆化搜索+dfs差不多)

那么有 d p [ i ] = ∑ k = 0 i − 1 d p [ i − k − 1 ] ∗ d p [ k ] dp[i] = \sum_{k=0}^{i - 1}{dp[i-k-1]*dp[k]} dp[i]=∑k=0i−1dp[i−k−1]∗dp[k],即枚举左右子树的所有情况,个数的乘积就是这种情况的个数。

cpp 复制代码
class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n + 1, 0);
        dp[0] = 1, dp[1] = 1;

        for(int i = 2; i <= n; ++ i){
            for(int k = 0; k < i; ++ k){
                dp[i] += dp[k] * dp[i - k - 1];
            }
        }
        return dp[n];
    }
};

二、95. 不同的二叉搜索树 II

LeetCode:95. 不同的二叉搜索树 II

这个题和之前的唯一区别就是这里维护一个真实的数,而不仅仅是个数。我们仍然可以使用相同的方法,只是这里是创建树,并且要关注值。

并且我们需要特别注意,dp[0]表示空树,空树并不是dp[0] = {}而是dp[0]={nullptr},原因是空树为nullptr,而不是没有元素。相当于{∅}的区别

以下是一种动态规划的写法,不像官解的回溯解法那么难理解:

我们将之前的dp[i] += dp[k] * dp[i - k - 1]改为了addTree(dp[i], dp[k], dp[i - k - 1], k),从只关注个数到需要创建所有情况。注意这个参数k一定是需要的,因为这代表左子树的个数,而dp[k]表示的是左子树的所有可能情况。

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<TreeNode*> generateTrees(int n) {
        vector<vector<TreeNode *>> dp(n + 1);//dp[i]表示结点个数为i 的所有可能情况
        dp[0] = {nullptr};
        dp[1] = {new TreeNode(1)};

        for(int i = 2; i <= n; ++ i){
            for(int k = 0; k < i; ++ k){//k表示左边有多少个,i - k - 1是右边的个数,右边以及根的大小从k + 1开始
                addTree(dp[i], dp[k], dp[i - k - 1], k);//将dp[k] - root - dp[i - k - 1]创建 加入到dp[i]
            }
        }

        return dp[n];
    }
private:
    void addTree(vector<TreeNode *> & total, vector<TreeNode *> & left, vector<TreeNode *> & right, int k){//将这种情况下的所有可能树连接起来
        for(int i = 0; i < left.size(); ++ i){
            for(int j = 0; j < right.size(); ++ j){
                TreeNode * root = new TreeNode(k + 1);
                root->left = createTree(left[i], 0);
                root->right = createTree(right[j], k + 1);
                total.emplace_back(root);
            }
        }
        return;
    }
    TreeNode * createTree(TreeNode * root, int bias){//创建树,加上偏置
        if(!root) return nullptr;
        TreeNode * cur = new TreeNode(root->val + bias);
        cur->left = createTree(root->left, bias);
        cur->right = createTree(root->right, bias);

        return cur;
    }
};

三、337. 打家劫舍 III

LeetCode:337. 打家劫舍 III

很明显这是一个动态规划题,树形dp,如何定义?

定义 dp[i]为以i为根的树的最高金额?

那么,i可以被偷,也可以不被偷:

  • dp[i] = max(儿子的最高金额,孙子的最高金额 + i的金额) //这样就可以确保不报警的情况下,拿到最高金额

并且,我们认为dp[i]就是对的最高金额,通过状态转移就能保证,每个都是最高金额。

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    unordered_map<TreeNode *, int> dp;
    int rob(TreeNode* root) {
        Getans(root);

        return dp[root];
    }
private:
    void Getans(TreeNode * root){
        if(!root) return;
        Getans(root->left);
        Getans(root->right);
        int ans = root->val;
        ans = max(ans, dp[root->left] + dp[root->right]);

        int temp = root->val;
        if(root->left){
            temp += dp[root->left->left] + dp[root->left->right];
        }
        if(root->right){
            temp += dp[root->right->left] + dp[root->right->right];
        }

        dp[root] = max(temp, ans);
        return;
    }
};
相关推荐
未来之窗软件服务6 小时前
自己写算法(九)网页数字动画函数——东方仙盟化神期
前端·javascript·算法·仙盟创梦ide·东方仙盟·东方仙盟算法
豐儀麟阁贵6 小时前
基本数据类型
java·算法
乐迪信息7 小时前
乐迪信息:基于AI算法的煤矿作业人员安全规范智能监测与预警系统
大数据·人工智能·算法·安全·视觉检测·推荐算法
hsjkdhs8 小时前
C++之多层继承、多源继承、菱形继承
开发语言·c++·算法
立志成为大牛的小牛8 小时前
数据结构——十七、线索二叉树找前驱与后继(王道408)
数据结构·笔记·学习·程序人生·考研·算法
星空下的曙光8 小时前
Node.js crypto模块所有 API 详解 + 常用 API + 使用场景
算法·node.js·哈希算法
StarPrayers.10 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
爱吃橘的橘猫10 小时前
嵌入式系统与嵌入式 C 语言(2)
c语言·算法·嵌入式
2351610 小时前
【LeetCode】146. LRU 缓存
java·后端·算法·leetcode·链表·缓存·职场和发展
weixin_3077791311 小时前
使用Python高效读取ZIP压缩文件中的UTF-8 JSON数据到Pandas和PySpark DataFrame
开发语言·python·算法·自动化·json