题目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());
}
};