LeetCode[95] 不一样的二叉搜索树II

第一篇博客先说点题外话。node

从决定转行至今已通过去近4年,然而进步速度却十分堪忧,至今还是菜鸟一枚。算法

目前在某水货学院读研三,在某家还算有点知名度的计算机视觉公司作算法实习生。学习

根据我对算法工程师的理解,工程能力、算法理解能力缺一不可,所以想在这个博客记录本身刷题、学习算法的历程,若有错误的地方但愿各位前辈、同窗不吝指点。spa

这篇是leetcode中的一道medium难度的题目,我会简述本身的思路并附上代码。code

这道题显然是要用动态规划,粗略估计一下暴力搜索应该是指数复杂度,并不可取。blog

虽然一开始就明确是动态规划,但却陷入了另外一个困难——我一开始寄但愿于从底向上构建答案,即试图构建(1,2)、(1,3)......(1,n)。恕我愚钝,我想了一阵没有想明白如何构建,可见这种想法不论对错,起码不够直观、简洁。递归

借助评论区里前辈们的答案,我用递归自上而下地解决了这道题,代码以下。leetcode

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<TreeNode*> generateTrees(int n) {
        if (n == 0) return vector<TreeNode*>{};
        vector<TreeNode*> res = memo(1, n);
        return res;
    }
    vector<TreeNode*> memo(int begin, int end) {
        vector<TreeNode*> res;
        if (begin > end) {
            res.push_back(NULL);
            return res;
        }
        for (int i = begin; i <= end; ++i) {
            vector<TreeNode*> left = memo(begin, i-1);
            vector<TreeNode*> right= memo(i+1, end);
            for (auto& l: left) {
                for (auto& r: right) {
                    TreeNode* root = new TreeNode(i);
                    root->left = l;
                    root->right = r;
                    res.push_back(root);
                }
            }
        }
        return res;
    }
};

若是你耐心地看完上述了代码,你可能会有个疑问,说好的动态规划呢?你这规划了个🔨?博客

是的,这就是暴力算法,暴力递归搜索全部左子树和右子树,结果也很直接,用时40ms,在C++提交中击败了0%的用户。it

其实稍加修改它就是动态规划了,只须要加一个用来记帐的memo,代码以下。

class Solution {
public:
    vector<TreeNode*> generateTrees(int n) {
        if (n == 0) return vector<TreeNode*>{};
        vector<vector<TreeNode*>> memo((n+1)*(n+1), vector<TreeNode*>{});
        vector<TreeNode*> res = helper(1, n, memo);
        return res;
    }
    vector<TreeNode*> helper(int begin, int end, vector<vector<TreeNode*>>& memo) {
        vector<TreeNode*> res;
        if (begin > end) {
            res.push_back(NULL);
            return res;
        }
        int n = sqrt(memo.size());
        if (memo[begin*n+end].size() != 0) return memo[begin*n+end];
        for (int i = begin; i <= end; ++i) {
            vector<TreeNode*> left = helper(begin, i-1, memo);
            vector<TreeNode*> right= helper(i+1, end, memo);
            for (auto& l: left) {
                for (auto& r: right) {
                    TreeNode* root = new TreeNode(i);
                    root->left = l;
                    root->right = r;
                    res.push_back(root);
                }
            }
        }
        memo[begin*n+end] = res;
        return res;
    }
};

只须要引入一个memo用来记录已经计算过的子树,这就是一个还算说得过去的解法了,以上解法用时28ms,在C++提交中击败了7.63%的用户。

这个结果显然也远算不上优秀,若是看到这篇文章的同窗、前辈有更好的方法,而且乐于分享,欢迎指导,个人第一篇博客就到此结束了。

相关文章
相关标签/搜索