C++刷题笔记(23)——leetcode106、105、654

题目1:106.从中序与后序遍历序列构造二叉树

在这里插入图片描述

解法:递归法

解题思路:
后序遍历是左右中,也就是说后续遍历的最后一个元素为二叉树的根节点
中序遍历是左中右,即中序遍历中根节点的左边是左子树,右边是左子树

那么就可以由后续遍历的最后一个元素去切割中序数组
在这里插入图片描述
然后再根据中序数组去切割后续数组
在这里插入图片描述
层层切割,直到构造出二叉树。
在这里插入图片描述

class Solution {
public:
    TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
        if (postorder.size() == 0) return NULL;                //如果后续数组大小为0,说明是空节点
        int rootvalue = postorder[postorder.size() - 1];        //后续数组最后一个元素为当前的根节点
        TreeNode* root = new TreeNode(rootvalue);
        if (postorder.size() == 1) return root;                 //后续数组只有一个元素,则为叶子节点
        int delimiterindex;                                     //在中序数组中找根节点作为切割点
        for (delimiterindex = 0; delimiterindex < inorder.size(); delimiterindex++) {
            if (inorder[delimiterindex] == rootvalue) break;
        }
        vector<int> leftinorder(inorder.begin(), inorder.begin() + delimiterindex);           //切割中序数组,左闭右开,中序左数组[0, delimiterIndex)
        vector<int> rightinorder(inorder.begin() + delimiterindex + 1, inorder.end());        //切割中序数组,左闭右开,中序右数组[delimiterIndex + 1, end)
        postorder.resize(postorder.size() - 1);                 //舍弃后续数组最后一个元素
        vector<int> leftpostorder(postorder.begin(), postorder.begin() + leftinorder.size()); //切割后续数组,左闭右开,后续左数组[0, leftInorder.size)
        vector<int> rightpostorder(postorder.begin() + leftinorder.size(), postorder.end());  //切割后续数组,左闭右开,后续右数组[leftInorder.size(), end)
        root->left = traversal(leftinorder, leftpostorder);     //递归处理左子树
        root->right = traversal(rightinorder, rightpostorder);  //递归处理右子树
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0 || postorder.size() == 0) return NULL;
        return traversal(inorder, postorder);
    }
};

代码优化后:

class Solution {
private:
    // 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
    TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
        if (postorderBegin == postorderEnd) return NULL;

        int rootValue = postorder[postorderEnd - 1];
        TreeNode* root = new TreeNode(rootValue);

        if (postorderEnd - postorderBegin == 1) return root;

        int delimiterIndex;
        for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }
        // 切割中序数组
        // 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex;
        // 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;

        // 切割后序数组
        // 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
        int leftPostorderBegin =  postorderBegin;
        int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
        // 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
        int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
        int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了

        root->left = traversal(inorder, leftInorderBegin, leftInorderEnd,  postorder, leftPostorderBegin, leftPostorderEnd);
        root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);

        return root;
    }
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0 || postorder.size() == 0) return NULL;
        // 左闭右开的原则
        return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
    }
};

理解了上面的代码,就不难理解官方的题解:

class Solution {
    int post_idx;
    unordered_map<int, int> idx_map;    //用哈希表存储中序序列,<元素,下标>
public:
    TreeNode* traversal(int in_left, int in_right, vector<int>& inorder, vector<int>& postorder) { 
        if (in_left > in_right) {  //in_left、in_right为中序数组的左右边界,in_left > in_right说明子树为空
            return nullptr;
        }
        //选择 post_idx 位置的元素作为当前子树根节点
        int root_val = postorder[post_idx];
        TreeNode* root = new TreeNode(root_val);

        // 根据 root 所在位置分成左右两棵子树
        int index = idx_map[root_val];

        // 下标减一
        post_idx--;
        // 递归构造右子树,从 index + 1 到 in_right 属于右子树
        //这里要先构造右子树,在后续遍历是左右中的顺序,如果按每次选择「后序遍历的最后一个节点」为根节点,则先被构造出来的应该为右子树
        root->right = traversal(index + 1, in_right, inorder, postorder);
        // 递归构造左子树,从 in_left 到 index - 1 属于左子树
        root->left = traversal(in_left, index - 1, inorder, postorder);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        // 从后序遍历的最后一个元素开始
        post_idx = (int)postorder.size() - 1;

        // 建立(元素,下标)键值对的哈希表
        int idx = 0;
        for (auto& val : inorder) {
            idx_map[val] = idx++;
        }
        return traversal(0, (int)inorder.size() - 1, inorder, postorder);
    }
};

题目2:105.从前序与中序遍历序列构造二叉树

在这里插入图片描述

解法:递归法

解题思路:
前序遍历是中左右,即前序遍历的第一个元素为二叉树的根节点
中序遍历是左中右,即中序遍历中根节点的左边是左子树,右边是左子树
那么接下来的解题步骤就和106一样了

但是代码不能和106一样,错误写法

class Solution {
public:
    TreeNode* traversal(vector<int>& preorder, vector<int>& inorder) {
        if (preorder.size() == 0) return NULL;
        int rootvalue = preorder.front();  //这里索引不会变所以出现问题
        TreeNode* root = new TreeNode(rootvalue);
        if (preorder.size() == 1) return root;
        int delimiterindex;
        for (delimiterindex = 0; delimiterindex < inorder.size(); delimiterindex++) {
            if (inorder[delimiterindex] == rootvalue) break;
        }
        // 切割中序数组
        // 左闭右开区间:[0, delimiterIndex)
        vector<int> leftinorder(inorder.begin(), inorder.begin() + delimiterindex);
        // [delimiterIndex + 1, end)
        vector<int> rightinorder(inorder.begin() + delimiterindex + 1, inorder.end());

        // 切割前序数组
        // [1, leftInorder.size)

        vector<int> leftpreorder(preorder.begin() + 1, preorder.begin() + 1 + leftinorder.size());
        // [leftInorder.size(), end)
        vector<int> rightpreorder(preorder.begin() + leftinorder.size() + 1, preorder.end());
        rootvalue = rightpreorder.front();    //没用,进入递归之后又被更新了

        root->left = traversal(leftinorder, leftpreorder);
        root->right = traversal(rightinorder, rightpreorder);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (inorder.size() == 0 || preorder.size() == 0) return NULL;
        return traversal(preorder, inorder);
    }
};

为了解决前序遍历第一个元素的问题,需要多传入几个参数:

class Solution {
private:
    TreeNode* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
        if (preorderBegin == preorderEnd) return NULL;

        int rootValue = preorder[preorderBegin]; // 注意用preorderBegin 不要用0
        TreeNode* root = new TreeNode(rootValue);

        if (preorderEnd - preorderBegin == 1) return root;

        int delimiterIndex;
        for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }
        // 切割中序数组
        // 中序左区间,左闭右开[leftInorderBegin, leftInorderEnd)
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex;
        // 中序右区间,左闭右开[rightInorderBegin, rightInorderEnd)
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;

        // 切割前序数组
        // 前序左区间,左闭右开[leftPreorderBegin, leftPreorderEnd)
        int leftPreorderBegin = preorderBegin + 1;
        int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 终止位置是起始位置加上中序左区间的大小size
        // 前序右区间, 左闭右开[rightPreorderBegin, rightPreorderEnd)
        int rightPreorderBegin = preorderBegin + 1 + (delimiterIndex - inorderBegin);
        int rightPreorderEnd = preorderEnd;

        root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, preorder, leftPreorderBegin, leftPreorderEnd);
        root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);

        return root;
    }

public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (inorder.size() == 0 || preorder.size() == 0) return NULL;
        return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());

    }
};

优化后的代码:

class Solution {
private:
        TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
        if (preorderBegin == preorderEnd) return NULL;

        int rootValue = preorder[preorderBegin]; // 注意用preorderBegin 不要用0
        TreeNode* root = new TreeNode(rootValue);

        if (preorderEnd - preorderBegin == 1) return root;

        int delimiterIndex;
        for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }
        // 切割中序数组
        // 中序左区间,左闭右开[leftInorderBegin, leftInorderEnd)
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex;
        // 中序右区间,左闭右开[rightInorderBegin, rightInorderEnd)
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;

        // 切割前序数组
        // 前序左区间,左闭右开[leftPreorderBegin, leftPreorderEnd)
        int leftPreorderBegin =  preorderBegin + 1;
        int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 终止位置是起始位置加上中序左区间的大小size
        // 前序右区间, 左闭右开[rightPreorderBegin, rightPreorderEnd)
        int rightPreorderBegin = preorderBegin + 1 + (delimiterIndex - inorderBegin);
        int rightPreorderEnd = preorderEnd;

        root->left = traversal(inorder, leftInorderBegin, leftInorderEnd,  preorder, leftPreorderBegin, leftPreorderEnd);
        root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);

        return root;
    }

public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (inorder.size() == 0 || preorder.size() == 0) return NULL;

        // 参数坚持左闭右开的原则
        return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());
    }
};

官方的题解比较简洁:

class Solution {
private:
    unordered_map<int, int> index;

public:
    TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return nullptr;
        }
        
        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = index[preorder[preorder_root]];
        
        // 先把根节点建立出来
        TreeNode* root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        // 构造哈希映射,快速定位根节点
        for (int i = 0; i < n; ++i) {
            index[inorder[i]] = i;
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
};

题目3:654.最大二叉树

在这里插入图片描述
在这里插入图片描述

解法:递归法

从示例一中可以看出,最大二叉树的构造无非就是从数组中找最大数,创建根节点 ,然后最大数的左右两侧为左右子树,递归建立最大二叉树。

class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        TreeNode* node = new TreeNode(0);
        if (nums.size() == 1) {     //数组大小为1时说明遍历到了叶子节点
            node->val = nums[0];
            return node;
        }
        int maxvalue = 0;           //数组中最大值
        int maxvalueindex = 0;      //数组中最大值的索引
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] > maxvalue) {
                maxvalue = nums[i];
                maxvalueindex = i;
            }
        }
        node->val = maxvalue;       //根据最大值构造根节点
        //最大值所在的下标左区间 构造左子树
        if (maxvalueindex > 0) {    //判断maxValueIndex > 0,保证左区间至少有一个数值。
            vector<int> newvec(nums.begin(), nums.begin() + maxvalueindex);
            node->left = constructMaximumBinaryTree(newvec);
        }
        //最大值所在的下标右区间 构造右子树
        if (maxvalueindex < (nums.size() - 1)) {        //判断maxValueIndex < (nums.size() - 1),确保右区间至少有一个数值
            vector<int> newvec(nums.begin() + maxvalueindex + 1, nums.end());
            node->right = constructMaximumBinaryTree(newvec);
        }
        return;
    }
};

优化后的代码:

class Solution {
public:
    TreeNode* traversal(vector<int>& nums, int left, int right) {
        if (left >= right) return NULL;    //在左闭右开区间[left, right)构造二叉树
        int maxvalueindex = left;
        for (int i = left + 1; i < right; i++) {
            if (nums[i] > nums[maxvalueindex]) maxvalueindex = i;
        }
        TreeNode* root = new TreeNode(nums[maxvalueindex]);       
        root->left = traversal(nums, left, maxvalueindex);          // 左闭右开:[left, maxValueIndex)
        root->right = traversal(nums, maxvalueindex + 1, right);    // 左闭右开:[maxValueIndex + 1, right)
        return root;
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return traversal(nums, 0, nums.size());
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值