再帰について明確に考えるのは実際には非常に簡単です。
タイトル説明:
整数 nums の一意の配列が与えられます。最大二分木は、次のアルゴリズムを使用して nums から再帰的に構築できます。
値が nums の最大値であるルート ノードを作成します。
最大値の左側にある部分配列プレフィックスの左側のサブツリーを再帰的に構築します。
サブ配列サフィックスの最大値の右側にある右側のサブツリーを再帰的に構築します。
nums によって構築された最大の二分木を返します。
アイデア:
バイナリ ツリーの構築は再帰的なプロセスであり、ルート ノードを決定し、左側のサブツリーを構築し、右側のサブツリーを構築します。
左のサブツリーで、ルート ノードを決定し、左のサブツリーの左のサブツリーを構築し、左のサブツリーの右のサブツリーを構築します。
右のサブツリーで、ルート ノードを決定し、右のサブツリーの左のサブツリーを構築し、右のサブツリーの右のサブツリーを構築します。
. . . 後ろは人形を入れ子にする工程。
私の理解: 再帰とは、人形を入れ子にするプロセスであり、規則的な方法で 1 つまたは複数を入れ子にするプロセスです。
この質問は、毎回最大値をルートノードとして使用し、最大値の左側を左側の部分木の構成として使用し、最大値の右側を右側の部分木の構成として使用し、そして、左部分木と右部分木が再帰的に構築されます。
まず、ルートノードの値として最大値を決定し、次に左部分木の間隔と右部分木の間隔を決定して、左部分木の再帰的な構築と右のサブツリー。
/**
* 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);
}
};
889. プレオーダーとポストオーダーのトラバーサルに基づいて二分木を構築する
タイトル説明:
preorder と postorder という 2 つの整数配列が与えられた場合、preorder は重複値のないバイナリ ツリーの preorder トラバーサルであり、postorder は同じツリーの postorder トラバーサルであり、バイナリ ツリーを再構築して返します。
複数の回答が存在する場合は、それらのいずれかを返すことができます。
アイデア:
事前順序トラバーサルの最初のノードはルート ノードでなければならず、後続のトラバーサルの最後のノードはルート ノードでなければなりません; 事前順序トラバーサルは 2 番目のノードから開始し、左半分は左
サブツリーでなければなりません。右半分は右サブツリーです。その後のトラバーサルも同じ条件を満たしているため、必ず分割点が存在し、この分割点を見つける方法を見つけると、左右のサブツリーを区別できます。
ノードの値が 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);
}
};
1569. サブアレイを並べ替えて同じ二分探索木を取得するソリューションの数
タイトル説明:
1 から n までの順列を表す配列 nums が与えられます。最初は空の二分探索木 (BST) に、要素が nums に表示される順序で要素を順番に挿入します。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;
}
};