这道题目要求左叶子之和,其实是比较绕的,因为不能判断本节点是不是左叶子节点。
此时就要通过节点的父节点来判断其左孩子是不是左叶子了。
平时我们解二叉树的题目时,已经习惯了通过节点的左右孩子判断本节点的属性,而本题我们要通过节点的父节点判断本节点的属性。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
//传参:传root,返回(int)sum
//终止条件
//1.null 2.叶子结点
if(root == null) return 0;
if(root.left==null && root.right == null) return 0;//因为在叶子处无法判断是不是左叶子,所以求和这个操作只能放在每层逻辑里
//每一层的逻辑:判断是否有左叶子结点,递归求解左子树和右子树左叶子结点和
int left=0;
if(root.left!=null && root.left.left==null && root.left.right==null){
left=root.left.val;
}
int leftsum= sumOfLeftLeaves(root.left);
int rightsum = sumOfLeftLeaves(root.right);
return left+leftsum+rightsum;
}
}
层序遍历迭代实现:
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new ArrayDeque<>();
List<Integer> ans = new ArrayList<>();
queue.add(root);
int len = 0;
while(!queue.isEmpty()){
len = queue.size();
for(int i=0 ; i<len ;i++){
TreeNode node = queue.remove();
ans.add(node.val);
if(node.left!=null) queue.add(node.left);
if(node.right!=null) queue.add(node.right);
}
}
return ans.get(ans.size()-len);
}
或
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int findBottomLeftValue(TreeNode root) {
List<Integer> result=new ArrayList<>();
Queue<TreeNode> q=new ArrayDeque<>();
q.add(root);
while(!q.isEmpty()){
TreeNode node=q.poll();
result.add(node.val);
if(node.right!=null) q.add(node.right);
if(node.left!=null) q.add(node.left);
}
return result.get(result.size()-1);
}
}
递归dfs实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private int maxDepth =-1;
private int result;
public int findBottomLeftValue(TreeNode root) {
dfs(root,0);
return result;
}
public void dfs(TreeNode root, int depth){
if(root.left==null && root.right==null){
if(depth > maxDepth){
result = root.val;
maxDepth = depth;
}
return ;
}
if(root.left!=null){
depth++;
dfs(root.left,depth);
depth--;
}
if(root.right!=null){
depth++;
dfs(root.right,depth);
depth--;
}
return;
}
}
maxDepth要初始化成-1,避免一个结点的情况(深度为0的情况)修改不了
3.112.路径总和
本题探究递归函数返回值的问题。
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
图中可以看出,遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
return dfs(root,targetSum-root.val);
}
public boolean dfs(TreeNode root, int targetSum){
if(root.left == null && root.right == null){
if(targetSum == 0) return true;
else return false;
}
if(root.left!=null){
targetSum-=root.left.val;
if(dfs(root.left,targetSum)) return true;
targetSum+=root.left.val;
}
if(root.right!=null){
targetSum-=root.right.val;
if(dfs(root.right,targetSum)) return true;
targetSum+=root.right.val;
}
return false;
}
}
如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> resultSet = new ArrayList<>();
List<Integer> result = new ArrayList<>();
if(root == null) return resultSet;
result.add(root.val);
dfs(root,targetSum-root.val,resultSet,result);
return resultSet;
}
private void dfs(TreeNode root, int targetSum, List<List<Integer>> resultSet, List<Integer> result) {
if(root.left == null && root.right == null && targetSum == 0){
resultSet.add(new ArrayList<>(result));
return;
}
if(root.left == null && root.right == null) return;
if(root.left!=null){
result.add(root.left.val);
dfs(root.left,targetSum-root.left.val,resultSet,result);
result.remove(result.size()-1);
}
if(root.right!=null){
result.add(root.right.val);
dfs(root.right,targetSum-root.right.val,resultSet,result);
result.remove(result.size()-1);
}
return ;
}
}
思路
-
第一步:如果数组大小为零的话,说明是空节点了。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
代码框架:
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
// 第一步
if (postorder.size() == 0) return NULL;
// 第二步:后序遍历数组最后一个元素,就是当前的中间节点
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;
}
// 第四步:切割中序数组,得到 中序左数组和中序右数组
// 第五步:切割后序数组,得到 后序左数组和后序右数组
// 第六步
root->left = traversal(中序左数组, 后序左数组);
root->right = traversal(中序右数组, 后序右数组);
return root;
}
难点在于如何切割数组。
应该注意确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭,这个就是不变量,要在递归中保持这个不变量。
采用左闭右开原则。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
Map<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
for(int i = 0 ; i < inorder.length; i++){
map.put(inorder[i],i);
}
return build(inorder,0,inorder.length,postorder,0,postorder.length);
}
public TreeNode build(int[] inorder, int inBegin , int inEnd , int[] postOrder, int postBegin, int postEnd){
if(postBegin >= postEnd) return null; // No rootValue
int rootValue = postOrder[postEnd - 1];
int rootIndex = map.get(rootValue);
TreeNode root = new TreeNode(rootValue);
int leftLength = rootIndex - inBegin;
root.left = build(inorder,inBegin,rootIndex,postOrder,postBegin,postBegin+leftLength);
root.right = build(inorder,rootIndex+1,inEnd,postOrder,postBegin+leftLength,postEnd-1);
return root;
}
}
这里左闭右开边界的确定令人混淆,建议:
(1)在纸上画一下,结合左闭右开,确定要传递的范围
(2)打印调试信息
同理可解(且下面的代码更清晰地表示出了左闭右开递归下标的计算过程,可以在纸上画一下)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
Map<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for(int i=0; i < inorder.length ; i++){
map.put(inorder[i],i);
}
return build(preorder,0,preorder.length,inorder,0,inorder.length);
}
public TreeNode build(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin , int inEnd){
if(preBegin == preEnd) return null;
int rootValue = preorder[preBegin];
int rootIndex_inorder = map.get(rootValue);
TreeNode root = new TreeNode(rootValue);
int inLeftBegin = inBegin;
int inLeftEnd = rootIndex_inorder;
int leftLength = rootIndex_inorder-inLeftBegin;
int preLeftBegin = preBegin+1;
int preLeftEnd = preLeftBegin+leftLength;
root.left=build(preorder,preLeftBegin,preLeftEnd,inorder,inLeftBegin,inLeftEnd);
int inRightBegin = rootIndex_inorder+1;
int inRightEnd = inEnd;
int preRightBegin = preLeftEnd;
int preRightEnd = preEnd;
root.right = build(preorder,preRightBegin,preRightEnd,inorder,inRightBegin,inRightEnd);
return root;
}
}
按照递归的思想,结合左闭右开的原则,代码解答如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return build(nums,0,nums.length);
}
public TreeNode build(int[] nums,int begin, int end){
if(begin >= end) return null;
int maxIndex = findMaxIndex(nums,begin,end);
int maxValue = nums[maxIndex];
TreeNode root = new TreeNode(maxValue);
int leftBegin = begin;
int leftEnd = maxIndex;
root.left = build(nums,leftBegin,leftEnd);
int rightBegin = maxIndex+1;
int rightEnd = end;
root.right = build(nums,rightBegin,rightEnd);
return root;
}
public int findMaxIndex(int[] nums, int begin, int end){
int maxIndex =-1;
int maxValue =-1;
for(int i=begin; i<end ;i++){
if(maxValue<nums[i]){
maxValue=nums[i];
maxIndex=i;
}
}
return maxIndex;
}
}
注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销。
总结参考:本周小结!(二叉树系列三)