15. 左子叶之和
思路: 递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。使用bool变量判断是否为左子叶。
class Solution {
public:
int postorder(TreeNode* t, bool isleft) { // 返回t的所有左子叶的和,后序遍历
if (t == NULL) return 0;
int l = postorder(t->left, true); // 统计左子树的所有左子叶的和
int r = postorder(t->right, false); // 统计右子树的所有左子叶之和
if (isleft && t->left == NULL && t->right == NULL) // 当当前结点为左子叶时,返回当前结点值
return t->val;
else
return l + r; // 若不为左子叶,返回当前结点的所有左子叶之和
}
int sumOfLeftLeaves(TreeNode* root) {
if (root == NULL) return 0;
return postorder(root, false);
}
};
16.找树左下角的值
思路: 使用层序遍历,保存最后一层的最左结点值即可。
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
int res = 0;
que.push(root);
while (!que.empty()) {
int size = que.size();
res = que.front()->val; // 记录当层的最左结点
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
if (cur->left != NULL) que.push(cur->left);
if (cur->right != NULL) que.push(cur->right);
}
}
return res;
}
};
路径总和
思路: 递归+回溯,保存当前路径和,找到到叶结点的路径和是否与target相同,若相同,则直接返回true;若不相同,则继续找其他路径,直至遍历完所有路径。
递归函数什么时候需要返回值?什么时候不需要返回值?
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。
写法1:无返回值
class Solution {
public:
void preorder(TreeNode* t,bool &isExist,int &count, int target) {
count += t->val;
if (t->left == NULL && t->right == NULL) { // 退出递归条件
if (count == target)
isExist = true;
return;
}
if (t->left) {
preorder(t->left, isExist, count, target);
count -= t->left->val; // 回溯
}
if (t->right) {
preorder(t->right, isExist, count, target);
count -= t->right->val;
}
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return false;
int count = 0;
bool isExist = false;
preorder(root, isExist, count, targetSum);
return isExist;
}
};
写法2:有返回值
class Solution {
public:
bool traversal(TreeNode* t,int count) { // count 为减过t->val后的值,在上层已经减去
// 跳出循环条件
if (t->left == NULL && t->right == NULL) {
if (count == 0)
return true;
return false;
}
// 当左右路线存在符合条件的路径,返回true
if (t->left) {
count -= t->left->val;
if (traversal(t->left, count)) return true;
count += t->left->val; // 回溯
}
if (t->right) {
count -= t->right->val;
if (traversal(t->right, count)) return true;
count += t->right->val;
}
// 当左右孩子均不在符合条件的路径,返回false
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return false;
return traversal(root, targetSum - root->val);
}
};
18.从中序与后序遍历序列构造二叉树
思路: 递归遍历,递归得到当前中序数组和后序数组生成的二叉树。就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。
说到一层一层切割,就应该想到了递归。
来看一下一共分几步:
- 第一步:如果数组大小为零的话,说明是空节点了。
- 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
- 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
- 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
- 第五步:切割后序数组,切成后序左数组和后序右数组
- 第六步:递归处理左区间和右区间
class Solution {
public:
// 返回传入中序数组和后序数组组成的二叉树
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
// 如果数组大小为零的话,说明是空节点了。
if (postorder.size() == 0) return NULL;
// 遍历后序数组,最后一个元素,即为当前的根节点
int rootval = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootval);
// 如果当前结点为叶结点,也直接返回
if (postorder.size() == 1) return root;
// 找到切割点(在中序遍历中的下标)
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < postorder.size(); delimiterIndex++)
if (inorder[delimiterIndex] == rootval)
break;
// 切割中序数组(中序遍历的根节点在中间,便于分割)
// 切割时,统一保证左闭右开,[0,delimiterIndex)
vector<int> inorderLeft(inorder.begin(),inorder.begin() + delimiterIndex);
vector<int> inorderRight(inorder.begin() + delimiterIndex + 1, inorder.end());// 中间元素舍去
// 切割后序数组(通过中序数组两边的大小来进行切割)
// 舍弃末尾元素
postorder.pop_back();
vector<int> postorderLeft(postorder.begin(), postorder.begin() + inorderLeft.size());
vector<int> postorderRight(postorder.begin() + inorderLeft.size(), postorder.end());
// 递归遍历后序数组
root->left = buildTree(inorderLeft, postorderLeft);
root->right = buildTree(inorderRight, postorderRight);
return root;
}
};
但这样写代码性能并不好,每层递归均创建了新的vector。使用下标进行递归,对代码进行改进。
class Solution {
public:
// begin与end是区间左闭右开的index
TreeNode* travseral(vector<int> &inorder,int inorderBegin,int inorderEnd,vector<int> &postorder,int postorderBegin,int postorderEnd) {
// 如果数组大小为零的话,说明是空节点了
if (postorderBegin == postorderEnd) return NULL;
//遍历后序数组,最后一个元素,即为当前的根节点
int rootval = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootval);
// 若当前结点为叶结点,返回root
if (postorderEnd - postorderBegin == 1) return root;
// 找到分割点
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++)
if (inorder[delimiterIndex] == rootval)
break;
// 分割中序数组(左闭右开) 中间元素被移除
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 分割后序数组,末尾元素被移除
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin;
int rightPostorderBegin = postorderBegin + delimiterIndex - inorderBegin;
int rightPostorderEnd = postorderEnd - 1;
// 递归
root->left = travseral(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
root->right = travseral(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
return root;
}
// 返回传入中序数组和后序数组组成的二叉树
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0) return NULL;
// 输入时,区间左闭又开
return travseral(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};
class Solution {
public:
// begin与end是左闭右开的下标
TreeNode* traversal(vector<int> &preorder,int preorderBegin,int preorderEnd,vector<int> &inorder,int inorderBegin,int inorderEnd) {
if (preorderBegin == preorderEnd) return NULL; // 当子树为空时,返回null
int rootval = preorder[preorderBegin];
TreeNode* root = new TreeNode(rootval);
if (preorderEnd - preorderBegin == 1) return root; // 仅有一个结点时,返回当前结点
// 找到分割点(下标)
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++)
if (inorder[delimiterIndex] == rootval)
break;
// 分割中序数组
int leftinorderBegin = inorderBegin;
int leftinorderEnd = delimiterIndex;
int rightinorderBegin = delimiterIndex + 1;
int rightinorderEnd = inorderEnd;
// 分割先序数组
int leftpreorderBegin = preorderBegin + 1;
int leftpreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin;
int rightpreorderBegin = leftpreorderEnd;
int rightpreorderEnd = preorderEnd;
// 递归
root->left = traversal(preorder, leftpreorderBegin, leftpreorderEnd, inorder, leftinorderBegin, leftinorderEnd);
root->right = traversal(preorder, rightpreorderBegin, rightpreorderEnd, inorder, rightinorderBegin, rightinorderEnd);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() == 0) return NULL;
return traversal(preorder, 0, preorder.size(), inorder, 0, inorder.size());
}
};
19. 最大二叉树
思路: 与上题构造二叉树类似,将数组分为两个区间分别构造左右子树,依照数组的最大值分割数组,然后递归循环构造二叉树。
class Solution {
public:
// begin 与 end分别为左闭右开的下标
TreeNode* traversal(vector<int> &nums,int begin,int end) {
if (end - begin == 0) return NULL;
int max = INT_MIN;
int maxIndex = 0;
// 找到最大值,及对应下标,为根节点
for (int i = begin; i < end; i++) {
if (nums[i] > max) {
max = nums[i];
maxIndex = i;
}
}
TreeNode* root = new TreeNode(max);
if (end - begin == 1) return root;
root->left = traversal(nums, begin, maxIndex);
root->right = traversal(nums, maxIndex + 1, end);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if (nums.size() == 0) return NULL;
return traversal(nums, 0, nums.size());
}
};