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%的用戶。

這個結果顯然也遠算不上優秀,若是看到這篇文章的同窗、前輩有更好的方法,而且樂於分享,歡迎指導,個人第一篇博客就到此結束了。

相關文章
相關標籤/搜索