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

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

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

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

根据我对算法工程师的理解,工程能力、算法理解能力缺一不可,因此想在这个博客记录自己刷题、学习算法的历程,如有错误的地方希望各位前辈、同学不吝指点。

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

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

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

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

/**
 * 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%的用户。

其实稍加修改它就是动态规划了,只需要加一个用来记账的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%的用户。

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

posted @ 2019-02-14 23:07  left4back  阅读(313)  评论(0)    收藏  举报