【六月集训】day29 分而治之

把递归想清楚了其实就很简单;

654. 最大二叉树

题目描述:

给定一个不重复的整数数组 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);
    }
};

889. 根据前序和后序遍历构造二叉树

题目描述:

给定两个整数数组,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);
    }
};

1569. 将子数组重新排序得到同一个二叉查找树的方案数

题目描述:

给你一个数组 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;

    }
};

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

ilovejujube

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值