第一篇博客先說點題外話。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%的用戶。
這個結果顯然也遠算不上優秀,若是看到這篇文章的同窗、前輩有更好的方法,而且樂於分享,歡迎指導,個人第一篇博客就到此結束了。