第一篇博客先说点题外话。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%的用户。
这个结果显然也远算不上优秀,若是看到这篇文章的同窗、前辈有更好的方法,而且乐于分享,欢迎指导,个人第一篇博客就到此结束了。