把递归想清楚了其实就很简单;
题目描述:
给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。
思路:
二叉树的构建是一个递归的过程,确定根结点,构造左子树,构造右子树;
左子树中,确定根结点,构造左子树的左子树,构造左子树的右子树;
右子树中,确定根结点,构造右子树的左子树,构造右子树的右子树;
。。。后面就是套娃的过程。
我的理解:递归就是套娃的过程,有规律的一个套一个或者套几个的过程。
这个题是每次要用最大值作为根结点,最大值左侧作为左子树的构成,最大值右侧作为右子树的构成,再递归构建左子树和右子树。
首先就需要确定过最大值,作为根结点的值,然后确定左子树的区间,和右子树的区间,便于递归构造左子树和右子树。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
//构建二叉树
TreeNode* dfs(vector<int>& nums, int start, int end)
{
if (start > end) {
return NULL;
}
int maxid = start;
for (int i = start; i <= end; ++i) {
if (nums[i] > nums[maxid]) {
maxid = i;
}
}
TreeNode *root = new TreeNode(nums[maxid]);
root->left = dfs(nums, start, maxid-1);
root->right = dfs(nums, maxid+1, end);
return root;
}
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return dfs(nums, 0, nums.size()-1);
}
};
题目描述:
给定两个整数数组,preorder 和 postorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。
如果存在多个答案,您可以返回其中 任何 一个。
思路:
前序遍历的第一个结点一定是根结点,后续遍历的最后一个结点一定是根结点;
前序遍历从第二个结点开始必然左半部分是左子树,右半部分是右子树。后续遍历也满足同样的条件,那么一定有一个分届点,想办法找到这个分界点,就可以区分左右子树了。
由于结点的值小于等于30,所以可以采用位运算将所有结点组合表示,利用一个掩码来表示一些结点即可,然后分而治之;
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
TreeNode* dfs(vector<int>& preorder, vector<int>& postorder, int prel, int prer, int postl, int postr) {
if (prel > prer) {
return NULL;
}
// 确定根结点
TreeNode* root = new TreeNode(preorder[prel]);//根结点 postorder 左 右 根
if (prel == prer) {
return root;
}
int premask = 0, postmask = 0;
int i = 0;
while(1) {
premask |= (1 << preorder[prel+i+1]);
postmask |= (1 << postorder[postl+i]);
if (postmask == premask) { //前面的点都是左子树上的点
root->left = dfs(preorder, postorder, prel + 1, prel+i+1, postl, postl+i);
root->right = dfs(preorder, postorder, prel+i+1+1, prer, postl+i+1, postr-1);
return root;
}
i++;
}
return NULL;
}
public:
TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
return dfs(preorder, postorder, 0, preorder.size()-1, 0, postorder.size()-1);
}
};
题目描述:
给你一个数组 nums 表示 1 到 n 的一个排列。我们按照元素在 nums 中的顺序依次插入一个初始为空的二叉查找树(BST)。请你统计将 nums 重新排序后,统计满足如下条件的方案数:重排后得到的二叉查找树与 nums 原本数字顺序得到的二叉查找树相同。
比方说,给你 nums = [2,1,3],我们得到一棵 2 为根,1 为左孩子,3 为右孩子的树。数组 [2,3,1] 也能得到相同的 BST,但 [3,2,1] 会得到一棵不同的 BST 。
请你返回重排 nums 后,与原数组 nums 得到相同二叉查找树的方案数。
由于答案可能会很大,请将结果对 10^9 + 7 取余数。
思路:
1、首先,把这颗二叉搜索树构造出来,构造过程直接跳用插入操作即可;
2、假设某个结点x,通过如下的方式得到的方案数是f(x),那么它的左子树的方案数位f(xl),右子树的方案数位f(xr),并且左子树的结点位cxl,右子树的结点个数位cxr,则就是一个排列组合问题;
3、得到f(x)=x[cxl+cxr][cxl]*f(xl)*f(xr), c[i][j]是组合数,表示第i个中选j个的方案数。这里采用的思路就是我们高中学过的插空法;
4、然后用分治法模拟这个过程即可。
class Solution {
#define mod 1000000007
#define maxn 1010
long long c[maxn][maxn];
//二叉搜索树的插入
TreeNode* insert(TreeNode *root, int val) {
if (root == NULL) {
return new TreeNode(val);
}
if (val < root->val) {
root->left = insert(root->left, val);
} else if (val > root->val){
root->right = insert(root->right, val);
}
return root;
}
int count(TreeNode *root) {
if (root == NULL) return 0;
return count(root->left) + count(root->right) + 1;
}
//对数进行遍历,得到的方案数用ans表示
long long dfs(TreeNode *root) {
if (root == NULL) {
return 1;
}
long long ans = dfs(root->left) * dfs(root->right) % mod;
//统计左右子树的个数
int lc = count(root->left);
int rc = count(root->right);
ans *= c[lc+rc][lc];
ans %= mod;
return ans;
}
public:
int numOfWays(vector<int>& nums) {
int i, j;
int n = nums.size();
//构造搜索二叉树
TreeNode *root = NULL;
for (int i = 0; i < n; ++i) {
root = insert(root, nums[i]);
}
for (i = 0; i <= n; ++i) {
for (j = 0; j <= i; ++j) {
if (j == 0 || i == j) {
c[i][j] = 1;
} else {
c[i][j] = (c[i-1][j] + c[i-1][j-1]) % mod;
}
}
}
return dfs(root)-1;
}
};