95. Unique Binary Search Trees II**

95. Unique Binary Search Trees II**

https://leetcode.com/problems/unique-binary-search-trees-ii/

题目描述

Given an integer n, generate all structurally unique BST’s (binary search trees) that store values 1 ... n.

Example:

Input: 3
Output:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]

Explanation:
The above output corresponds to the 5 unique BST’s shown below:

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

C++ 实现 1

可以先了解一下 96. Unique Binary Search Trees** 这道题. 相比前一道题, 此题要求找出所有的 Unique BST. 下面代码中, 使用 generate(start, end) 来产生 [start .... end] 范围内的 BST. 思路是, 先用 [start ..., i - 1] 产生左子树, 使用 [i + 1, ..., end] 产生右子树, 最后保存产生的结果. 由于 [i + 1, ..., end] 的值始终要比 [start, ..., i - 1] 中的值大, 所有这符合 BST 的性质, 即所有的右子树节点值要大于左子树节点值. 这是个递归问题, 所以任何子树都满足这条性质.

class Solution {
private:
    vector<TreeNode*> generate(int start, int end) {
        if (start > end) return {nullptr};
        vector<TreeNode*> res;
        for (int i = start; i <= end; ++ i) {
            auto ltree = generate(start, i - 1);
            auto rtree = generate(i + 1, end);
            for (auto &lc : ltree) {
                for (auto &rc : rtree) {
                    TreeNode *root = new TreeNode(i);
                    root->left = lc;
                    root->right = rc;
                    res.push_back(root);
                }
            }
        }
        return res;
    }
public:
    vector<TreeNode*> generateTrees(int n) {
        if (n <= 0) return {};
        return generate(1, n);
    }
};

C++ 实现 2

两年前的代码… 我现在来看简直震惊 🤪🤪🤪, 还好当时做了笔记, 否则现在要弄懂的成本太高了. 记录如下:

思路: 要解决这道题, 可以使用动态规划的方法. 如果我们知道了保存 1 ~ n - 1 的所有 BST, 假设为 f(n - 1), 那么当考虑 n 时, 就要判断怎样将 n 插入到 f(n - 1) 中的每棵 BST 中, 这样就能得到 f(n) 了. 举个例子, 比如 n = 3 时, 上面可以产生 5 棵 BST, 那这 5 棵 BST 是怎么得到的呢? 先考虑 f(2).

如果 n = 2 时, 我们可以如下的 BST:

   1         2       
   	\        /
   	 2      1

即 f(2) 时产生了两棵 BST.

情况 1: 那么当要插入 n = 3 时, 由于 n 总比 f(n - 1) 中每棵 BST 中的最大值(即 n-1) 都要大, 那么可以将那些 BST 作为 n 的左子树, 即:

    3       3
    /       /
   1       2
    \      /
     2     1

情况二: 可以将 n 作为 f(n - 1) 中每棵 BST 的右子树, 但这里有个问题要注意, 先看例子:

 1          2
  \        / \
   2      1   3
    \
     3

但是如何得到下面这个呢?

   1
    \
     3
    /
   2

这时, 我们就知道了, 如果将 3 沿着根节点的右子节点一直走, 如果右子节点为空, 那么就将 3 插入进去; 如果右子节点不为空, 那么就需要将 3 插入到右子节点的上面(所谓"上面"自己体会), 并将右子节点以及后面的内容作为 3 的左子树. 根据这个思路, 我们从根节点开始, 依次判断右子节点是否为空, 然后插入节点 3. 按照顺序画图如下:

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

这样就得到了 5 个不同的 BST.

class Solution {
private:
  	// 由于最后的结果是返回所有的不同子树, 那么在求 f(n) 的
  	// 时候, 需要拷贝 f(n - 1) 中的树, 再进行操作. 拷贝使用
  	// 前向遍历即可.
    TreeNode* copyTree(TreeNode *root) {
        if (!root)
            return nullptr;
        
        TreeNode *newroot = new TreeNode(root->val);
        newroot->left = copyTree(root->left);
        newroot->right = copyTree(root->right);
        return newroot;
    }
public:
    vector<TreeNode*> generateTrees(int n) {
        if (n < 1)
            return vector<TreeNode*>{};
        if (n == 1)
            return vector<TreeNode*>{new TreeNode(n)};
		
        auto treeSet = generateTrees(n - 1);
        vector<TreeNode*> res;
        for (auto &subroot : treeSet) {
            TreeNode *root = new TreeNode(n);
          	// 情况 1: 将 f(n - 1) 中的所有BST 作为 root 的左子树
            root->left = subroot;
            res.push_back(root);
          	// 情况 2: 
          	// 使用 ptr2 来记录 root 应插入的位置
          	// 另一方面还要用 ptr 来记录产生下一棵BST时, ptr2 要移动的位置
          	// 这个可以画图体会.
            auto ptr = subroot;
            while (ptr) {
                TreeNode *node = new TreeNode(n);
                TreeNode *root = copyTree(subroot);
                auto ptr2 = root;
                while (ptr2->val != ptr->val)// 使ptr2移动到和 ptr 对应的位置
                    ptr2 = ptr2->right;
                auto temp = ptr2->right;
                ptr2->right = node;
              	// 将 temp 作为 n 的左子树.
                node->left = temp;
                res.push_back(root);
                ptr = ptr->right;
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值