进入第3节:二叉树的修改与构造
例题1:翻转二叉树
给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。
这题很简单,前序遍历和后序遍历都能做, 即先翻转根结点还是先翻转左右结点都可以;但是中序遍历的话,某些节点的左右孩子会翻转两次。
后序遍历代码:
class Solution {
public:
void invert(TreeNode* root)
{
if(root == NULL)//终止条件
return;
invert(root->left);//左
invert(root->right);//右
swap(root->left,root->right);//根
}
TreeNode* invertTree(TreeNode* root) {
invert(root);
return root;
}
};
例题2:从中序与后序遍历序列构造二叉树
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
由中序遍历和后序遍历构造一颗树, 数据结构课的时候会教解决流程,先找出根结点,再划分其左右子树的数组,并把然后递归地去找左右子树中的根结点和左右子树....将上一层的根节点与本层的左右子树通过 left, right指针联系起来。
此时应该注意确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭,这个就是不变量,要在递归中保持这个不变量。
终止条件:
若中序遍历数组为空,返回NULL;
单层处理逻辑:
-
第一步:如果数组大小为零的话,说明是空节点了。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
//终止条件
if(inorder.size() == 0) return nullptr;//inorder和postorder是一样长的,判断一个数组酒啊后二零
//对于两个数组,先划分出根,左子树,右子树
int mid = postorder[postorder.size()-1];
TreeNode *root = new TreeNode(mid);
int i = 0;
for(i; i < inorder.size(); i++)
{
if(inorder[i] == mid)
break;
}
//获得了mid在中序遍历数组中的位置 而且是左闭右开区间
//切割中序数组
vector<int> linorder(inorder.begin(), inorder.begin() + i);
vector<int> rinorder(inorder.begin() + i + 1, inorder.end());
//切割后序数组
//postorder.resize(postorder.size() - 1);
vector<int> lpostorder(postorder.begin(), postorder.begin() + linorder.size());
vector<int> rpostorder(postorder.begin() + linorder.size(), postorder.end()-1);
root -> left = buildTree(linorder,lpostorder);
root-> right = buildTree(rinorder,rpostorder);
return root;
}
例题3:最大二叉树
给定一个不重复的整数数组 nums
。 最大二叉树 可以用下面的算法从 nums
递归地构建:
- 创建一个根节点,其值为
nums
中的最大值。 - 递归地在最大值 左边 的 子数组前缀上 构建左子树。
- 递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums
构建的 最大二叉树
跟上一题很相似,可以写出代码如下:
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if(nums.size() == 0) return NULL;
int max = -1;
int maxpos = 0;
for(int i = 0; i < nums.size(); i++)
{
if (nums[i] > max)
{
maxpos = i;
max = nums[i];
}
}
TreeNode *root = new TreeNode(max);
vector<int> lsub(nums.begin(), nums.begin() + maxpos);
vector<int> rsub(nums.begin() + maxpos + 1, nums.end());
root -> left = constructMaximumBinaryTree(lsub);
root -> right = constructMaximumBinaryTree(rsub);
return root;
}
//第一步 找出数组中的最大值 作为根结点
//第二步 将数组分为左子数组和右子数组,递归建树。
//将根结点与左右子树连接
};
代码随想录里还有其他的解法,我们上述的代码在每一层中都要定义一个新的数组,其实可以用类似于数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,可以节约时间和空间上的开销。
class Solution {
private:
// 在左闭右开区间[left, right),构造二叉树
TreeNode* traversal(vector<int>& nums, int left, int right) {
if (left >= right) return nullptr;
// 分割点下标:maxValueIndex
int maxValueIndex = left;
for (int i = left + 1; i < right; ++i) {
if (nums[i] > nums[maxValueIndex]) maxValueIndex = i;
}
TreeNode* root = new TreeNode(nums[maxValueIndex]);
// 左闭右开:[left, maxValueIndex)
root->left = traversal(nums, left, maxValueIndex);
// 左闭右开:[maxValueIndex + 1, right)
root->right = traversal(nums, maxValueIndex + 1, right);
return root;
}
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return traversal(nums, 0, nums.size());
}
};
例题4:合并二叉树
给你两棵二叉树: root1
和 root2
。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1 == NULL)
return root2;
if(root2 == NULL)
return root1;
root1->val += root2->val;
root1 -> left = mergeTrees(root1->left, root2->left);
root1 -> right = mergeTrees(root1->right, root2->right);
return root1;
}
};