【6月合宿】day29 分割統治

再帰について明確に考えるのは実際には非常に簡単です。

654. 最大二分木

タイトル説明:

整数 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;

    }
};

おすすめ

転載: blog.csdn.net/ilovejujube/article/details/125518432