Day20:分治算法:重建二叉树

重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

题目分析

先序遍历的首个元素就是树的根节点。我们在先序遍历中找到根节点,然后就可以根据根节点在中序遍历的数组中(因为题目说了树中没有重复的元素),将其划分为左子树 | 根节点 | 右子树。这样再分别递归其左右子树,即可重建整个二叉树。难点就在于递归需要传入的参数。这里先给出答案,需要的3个参数是1.根节点在前序遍历的索引,2.子树在中序遍历的左边界,3.子树在中序遍历的右边界。这是因为需要左右边界来确定退出条件,需要根节点来在中序遍历中划分界限。左子树的根节点直接就是原来的根节点root+1就好了,右子树的根节点需要先确定左子树的长度,而这个长度在中序遍历里可以知道,就是根节点在中序遍历的位置的左边。

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        // 输入的应该是先序遍历中根节点的位置。还有左子树的起点,右子树的终点。
        // 容易知道的是,先序遍历先是 根节点|左子树|右子树。
        // 这样就可以知道,当遍历左子树时候,这一左子树自己的根节点应该是root+1,
        // 右子树的根节点则在左子树结束后的下一个节点。
        // 先序遍历一开始的节点就是根节点。
        // 找到中序遍历的根节点的位置,从这个位置往前的都是根节点
        // 中序遍历找根节点的位置可以利用hash。
        // 的左子树,往后的都是根节点的右子树。这样在分别递归这两颗子树。
        this->preorder = preorder;
        for (int i = 0; i < inorder.size(); i++){
            map[inorder[i]] = i;
        }
        return recur(0, 0, inorder.size()-1);
    }
    TreeNode* recur(int root, int left, int right){
        //退出条件。
        if (left > right) return nullptr;
        //递归
        TreeNode* node = new TreeNode(preorder[root]);
        int i = map[preorder[root]];
        //返回值。
        node->left = recur(root+1, left, i-1);
        node->right = recur(root-left+i+1, i+1, right);
        return node;
    }
private:
    vector<int> preorder;
    unordered_map<int, int> map;
};

数值的整数次方

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
没啥好说的,看解析吧。
leetcode: https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/57p2pv/.

二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

题目分析

首先这是一颗二叉搜索树。二叉搜索树的左子树的值都小于根节点,右子树的值都大于根节点。其次是后序遍历,其区域划分为左子树 | 右子树 | 根节点。关键来了,最后一个树即是根节点,当我们遍历数组,发现第一个大于根节点的数字,那么此数字就可以划分区域,将问题分而治之。左右子树的最后一个数字便是它们各自的根节点。这样我们可以递归左右子树。递归参数这次挺容易想到的,应该是一棵树的左右边界,加上这个数组。退出条件便是左边界大于或者等于右边界。递归想到了,二叉搜索树的判断条件还没有呢,应该就是上面所说的二叉搜索树的特性,通过两个循环来进行左右子树与根节点的大小比较,而我们划分的时候已经保证过左子树的正确性,所以现在我们只需要判断右子树区间就可以了。自己写的应该跟大佬的差不多,但是估计是测试样例太少了,稍微一点差距就造成只beat了29%。

法1
class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        return dfs(postorder, 0, postorder.size()-1);
    }
    bool dfs(vector<int>& postorder, int i, int j){
        // 根节点总在最后一个,寻找第一个大于根节点的数字,以此为界划分区域假设此数字Index为m。
        // 那么0到m-1为左子树,m到j-1为右子树。
        // 左子树的最后一个又为它的根节点,在据此划分,右子树同理。
        // 退出条件 是什么呢?左子树中的树都应该小于根节点,右子树的数都应该大于根节点。
        if (i>=j) return true;
        int m;
        for (int k = 0; k < j; k++){
            if (postorder[k] > postorder[j]){
                m = k;
                break;
            }
        }
        // for(int l = i; l < m;l++){
        //     if (postorder[l] > postorder[j]) return false;
        // }
        for(int l = m; l < j; l++){
            if (postorder[l] < postorder[j]) return false;
        }
        return dfs(postorder, i, m-1) && dfs(postorder, m+1, j-1);
}
};
法2

有点难懂,看链接吧
链接: https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vwbf6/.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值