第十四周LeetCode算法题两道

第一道

题目名称:96. Unique Binary Search Trees

题目难度:Medium

题目描述:Given n, how many structurally unique BST’s (binary search trees) that store values 1…n?

For example,
Given n = 3, there are a total of 5 unique BST’s.

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

题目分析:
题目要求是,给定一个数n,这个数代表一个序列1…n,给出中序遍历后的结果是1…n的树的数量。(中序遍历这里其实题目中没有明确说出来,不过博主从给出的例子中推测本题的遍历指的是中序遍历,事实上也的确如此)
这是一道可以用动态规划解决的题目。解决问题的思路如下:

这些所有的树由1到n分别作为根的树构成。
假设当前是i为根,则i-1在左子树中,i+1在右子树中。然后再递归地算出这些子树的个数,子树的个数就是一个subproblem。
result[n]表示中序遍历结果为1...n的序列的树的数量。
引入一个中间变量F(i,n)表示以i为根的中序遍历结果为1...n的序列的树的数量。

result[n] = F(1,n) + F(2,n) + F(3,n) + ... + F(n,n)

且有result[0] = result[1] = 1
F(i,n)的值是其可能的左子树的个数和可能的右子树的数量的乘积。即:
F(i,n) = result[i-1] * result[n-i]
举个例子F(3,7) = result[2] * result[4],即[1,2,3,4,5,6,7]被拆分成[1,2]、[4,5,6,7],即求[1,2]、[4,5,6,7]构成的树的所有情况的数量。虽然[4,5,6,7]和[1,2,3,4]构成的树肯定是不同的,但是其构成的树的总量是一样的,本题只要求求数量,故可以看成是相等的。
于是我们有:
result(n) = result(0) * result(n-1) + result(1) * result(n-2) + … + result(n-1) * result(0)

于是最后的代码为:

class Solution {
public:
    int numTrees(int n) {
        int result[n+1];
        memset(result, 0, sizeof(result));
        result[0] = result[1] = 1;
        for (int i = 2; i <= n; ++i) {
            for (int j = 1; j <= i; ++j) {
                result[i] += result[j - 1] * result[i - j];
            }
        }
        return result[n];
    }
};

第二道

题目名称:95. Unique Binary Search Trees II

题目难度:Medium

题目描述:Given an integer n, generate all structurally unique BST’s (binary search trees) that store values 1…n.

For example,
Given n = 3, your program should return all 5 unique BST’s shown below.

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

题目分析:
本题跟上一题有点类似,区别只是本题要求我们将所有的树都构造出来。
根据上一题的思路,所有的情况均是由从1到n分别作为根的树构成。假设当前的根节点为i,则1到i-1是其左子树,i+1到n是其右子树。这样分别递归下去即可。
需要注意的是,虽然有多棵树的根节点的值一样,但是并不代表它们的根一样。因此应该每一个都使用一个新的副本表示根节点。
又,每一个相同的根值可能有多棵左子树和右子树,可以将这些子树放在一个数组中,按顺序调用。注意左子树和右子树之间的关系是笛卡尔积的关系。

最后AC的代码是:

/**
 * 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*> getSub(int low, int high) {
        vector<TreeNode*> sub;
        if (low > high) return sub;
        if (low == high) {
            sub.push_back(new TreeNode(low));
            return sub;
        }
        for (int i = low; i <= high; ++i ) {
            vector<TreeNode*> subLeft = getSub(low, i-1);
            vector<TreeNode*> subright = getSub(i+1, high);
            if (subLeft.size() == 0) {
                for (int k = 0; k < subright.size(); ++k) {
                    TreeNode * tempHead = new TreeNode(i);
                    tempHead->left = NULL;
                    tempHead->right = subright[k];
                    sub.push_back(tempHead);
                }
            } else if (subright.size() == 0) {
                for (int k = 0; k < subLeft.size(); ++k) {
                    TreeNode * tempHead = new TreeNode(i);
                    tempHead->right = NULL;
                    tempHead->left = subLeft[k];
                    sub.push_back(tempHead);
                }
            } else {
                for (int j = 0; j < subLeft.size(); ++j) {
                    for (int k = 0; k < subright.size(); ++k) {
                        TreeNode * tempHead = new TreeNode(i);
                        tempHead->left = subLeft[j];
                        tempHead->right = subright[k];
                        sub.push_back(tempHead);
                    }
                }
            }
        }
        return sub;
    }
    vector<TreeNode*> generateTrees(int n) {
        vector<TreeNode*> result;
        if (n <= 0) return result;
        if (n == 1) {
            result.push_back(new TreeNode(1));
            return result;
        }
        for (int i = 1; i <= n; ++i) {
            vector<TreeNode*> subLeft = getSub(1, i-1);
            vector<TreeNode*> subright = getSub(i+1, n);
            if (subLeft.size() == 0) {
                for (int k = 0; k < subright.size(); ++k) {
                    TreeNode * tempHead = new TreeNode(i);
                    tempHead->left = NULL;
                    // cout << subright[k] << endl;
                    tempHead->right = subright[k];
                    result.push_back(tempHead);
                }
            } else if (subright.size() == 0) {
                for (int k = 0; k < subLeft.size(); ++k) {
                    TreeNode * tempHead = new TreeNode(i);
                    tempHead->right = NULL;
                    tempHead->left = subLeft[k];
                    result.push_back(tempHead);
                }
            } else {
                for (int j = 0; j < subLeft.size(); ++j) {
                    for (int k = 0; k < subright.size(); ++k) {
                        TreeNode * tempHead = new TreeNode(i);
                        tempHead->left = subLeft[j];
                        tempHead->right = subright[k];
                        result.push_back(tempHead);
                    }
                }
            }
        }
        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值