代码随想录算法训练营第18天
今日任务
● 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树
链接:https://docs.qq.com/doc/DUFFiVHl3YVlReVlr (opens in a new tab)
513.找树左下角的值
自己想法
使用层序遍历,代码改自day15:层序遍历 (opens in a new tab)
class Solution {
public int findBottomLeftValue(TreeNode root) {
int ans = -1;
List<Integer> layer = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
// 初始判断:树空?
if(root != null) {
queue.offer(root);
queue.offer(null); // 压入null表示隔开这一层
}
else return ans;
// 遍历
while(!queue.isEmpty()) {
if(queue.peek() != null) {
// 顶部非空,正在遍历这一层
layer.add(queue.peek().val); // 当前节点入layer
// System.out.println(layer);
if(queue.peek().left != null) queue.offer(queue.peek().left); // 压入左
if(queue.peek().right != null) queue.offer(queue.peek().right); // 压入右
queue.poll(); // 弹出顶
}
else {
// 顶部空,这一层遍历完毕
// System.out.println(layer);
ans = layer.get(0);
layer = new ArrayList<>(); // 当前layer清空
queue.poll(); // 弹出空
if(!queue.isEmpty()) queue.offer(null);
}
}
return ans;
}
}112.路径总和
自己想法
使用递归,代码改自day17:257-二叉树的所有路径 (opens in a new tab)
class Solution {
public boolean ans;
public int targetSum;
public int nowSum;
public boolean hasPathSum(TreeNode root, int targetSum) {
this.ans = false;
this.targetSum = targetSum;
this.nowSum = 0;
if(root != null) invert(root);
return ans;
}
// 1.返回值:无
public void invert(TreeNode node) {
// 2.结束条件:到达叶子
if(node.left == null && node.right == null) {
this.nowSum += node.val;
if(this.nowSum == this.targetSum) this.ans = true;
this.nowSum -= node.val;
return;
}
// 3.递归逻辑
this.nowSum += node.val;
if(node.left != null) invert(node.left);
if(node.right != null) invert(node.right);
this.nowSum -= node.val; // 回溯
}
}113.路径总和ii
自己想法
使用递归,代码改自day17:257-二叉树的所有路径 (opens in a new tab)
class Solution {
public List<List<Integer>> ans;
public List<Integer> path;
public int targetSum;
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
this.ans = new ArrayList<>();
this.path = new ArrayList<>();
this.targetSum = targetSum;
if(root != null) invert(root);
return ans;
}
// 1.返回值:无
public void invert(TreeNode node) {
// 2.结束条件:到达叶子
if(node.left == null && node.right == null) {
this.path.add(node.val);
if(path.stream().mapToLong(Integer::intValue).sum() == this.targetSum) {
List<Integer> copyOfPath = new ArrayList<>(path);
this.ans.add(copyOfPath);
}
this.path.remove(this.path.size() - 1);
return;
}
// 3.递归逻辑
this.path.add(node.val);
if(node.left != null) invert(node.left);
if(node.right != null) invert(node.right);
this.path.remove(this.path.size() - 1); // 回溯
}
}106.从中序与后序遍历序列构造二叉树
题解想法
以 后序数组的最后一个元素为切割点,先切中序数组, 根据中序数组,反过来再切后序数组。 一层一层切下去,每次后序数组最后一个元素就是节点元素。
边界问题统一定义:切割区间都是前开后闭,即 [start, end)

以下为题解源码copy,建议看105.从前序与中序遍历序列构造二叉树 (opens in a new tab),有我自己写的代码和注释
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(postorder.length == 0 || inorder.length == 0)
return null;
return buildHelper(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
private TreeNode buildHelper(int[] inorder, int inorderStart, int inorderEnd, int[] postorder, int postorderStart, int postorderEnd){
if(postorderStart == postorderEnd)
return null;
int rootVal = postorder[postorderEnd - 1];
TreeNode root = new TreeNode(rootVal);
int middleIndex;
for (middleIndex = inorderStart; middleIndex < inorderEnd; middleIndex++){
if(inorder[middleIndex] == rootVal)
break;
}
int leftInorderStart = inorderStart;
int leftInorderEnd = middleIndex;
int rightInorderStart = middleIndex + 1;
int rightInorderEnd = inorderEnd;
int leftPostorderStart = postorderStart;
int leftPostorderEnd = postorderStart + (leftInorderEnd - leftInorderStart);
int rightPostorderStart = leftPostorderEnd;
int rightPostorderEnd = postorderEnd - 1;
root.left = buildHelper(inorder, leftInorderStart, leftInorderEnd, postorder, leftPostorderStart, leftPostorderEnd);
root.right = buildHelper(inorder, rightInorderStart, rightInorderEnd, postorder, rightPostorderStart, rightPostorderEnd);
return root;
}
}105.从前序与中序遍历序列构造二叉树
题解想法
以 前序数组的第一个元素为切割点,先切中序数组, 根据中序数组,反过来再切前序数组。 一层一层切下去,每次前序数组第一个元素就是节点元素。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return invert(preorder, 0, preorder.length, inorder, 0, inorder.length);
}
// 1.返回值:当前子树的根节点
public TreeNode invert(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
// 2.结束条件:到达空(遍历数组长度为0),返回空
if(preStart == preEnd) return null;
// 3.递归逻辑
// 3.1.找当前节点(中间节点):前序的第一个必是中间节点
TreeNode root = new TreeNode(preorder[preStart], null, null);
// 3.2.用中间结点切分中序数组为左右两半
int i;
for(i = inStart; i < inEnd; i++) {
if(inorder[i] == root.val) break;
}
int inLeftStart = inStart;
int inLeftEnd = i;
int inRightStart = i + 1;
int inRightEnd = inEnd;
// 3.3.用切开的中序数组长度决定前序数组怎么切(记得去掉当前前序的第一个元素,因为这个元素已经被当作中间节点拿走了)
int preLeftStart = preStart + 1;
int preLeftEnd = preLeftStart + (inLeftEnd - inLeftStart);
int preRightStart = preLeftEnd;
int preRightEnd = preEnd;
// 3.4.用切开的数组递归,其返回值接到当前节点的左右
root.left = invert(preorder, preLeftStart, preLeftEnd, inorder, inLeftStart, inLeftEnd);
root.right = invert(preorder, preRightStart, preRightEnd, inorder, inRightStart, inRightEnd);
// 3.5.返回root
return root;
}
}回顾
-
后序和中序可以唯一确定一棵二叉树。每次后序数组最后一个元素就是节点元素
-
前序和中序可以唯一确定一棵二叉树。每次前序数组第一个元素就是节点元素
-
前序和后序不能唯一确定一棵二叉树!因为没有中序遍历无法确定左右部分,也就是无法分割