代码随想录算法训练营第十八天|513.找树左下角的值、112. 路径总和、113.路径总和ii、106.从中序与后序遍历序列构造二叉树、105.从前序与中序遍历序列构造二叉树
513.找树左下角的值
问题简述:找出最下面一层元素最左面的元素值。
思考:直接用队列层次遍历比较简单,递归较为复杂,和获得所有路径的题有点相似。然后层次遍历其实就是广度优先,递归就是深度优先。递归法哪种遍历都可以,毕竟哪种遍历都是先遍历左结点,再右结点。
层次遍历算法思路:队列存入每层元素。不断将队头元素值存入List
,再将队头的左右节点再次存入List
,最后推出队头。此时队列中为下一层元素,不断循环,记录每层的第一个元素。最终获得最后一层的第一个元素。
import java.util.ArrayDeque;
class Solution {
public int findBottomLeftValue(TreeNode root) {
ArrayDeque<TreeNode> dq = new ArrayDeque<>();
dq.add(root);
//存储每层第一个元素
int first = 0;
while (!dq.isEmpty()){
int size = dq.size();
first = dq.getFirst().val;
for (int i = 0; i < size; i++) {
if(dq.getFirst().left != null) dq.add(dq.getFirst().left);
if(dq.getFirst().right != null)dq.add(dq.getFirst().right);
dq.removeFirst();
}
}
return first;
}
}
递归算法思路:递归遍历二叉树,同时记录每个节点深度,当遍历到叶子结点的时候,判断当前结点深度是否为第一次出现的最大深度,如果是则更新返回结果。然后依次遍历左右结点,直到遍历完所有叶子结点。
class Solution {
//记录最终结果
int res = Integer.MIN_VALUE;
//记录最大深度
int maxdeep = 0;
//记录当前深度
int deep = 0;
public int findBottomLeftValue(TreeNode root) {
deep++;
//到叶子结点时
if (root.left == null && root.right == null){
//如果当前深度大于最大深度,则更改res,并更改最大深度
if (deep > maxdeep){
res = root.val;
maxdeep = deep;
}
//如果跟结点左右为空,返回根结点的值
return root.val;
}
if (root.left != null){
findBottomLeftValue(root.left);
//回溯
deep--;
}
if (root.right != null){
findBottomLeftValue(root.right);
//回溯
deep--;
}
return res;
}
}
112. 路径总和、113.路径总和ii
问题简述:求出二叉树是否存在某个路径和;求出二叉树路径和为某个值的所有路径。
思考:和遍历所有路径很像,关键是学会回溯。
112算法思路:先序遍历每一个结点,记录sum为遍历过路径的和,如果遍历到叶子结点时,将判断sum是否等于tar getSum。如果不是叶子结点,则遍历左结点,递归添加左面结点值,判断所有路径是否满足要求后进行回溯,将sum剪去当前节点的值。再递归添加右面结点,判断所有路径没有满足要求后,最后返回fasle。
import java.lang.annotation.Target;
import java.util.List;
class Solution {
//存储路径和
int sum = 0;
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) return false;
//sum中加入当前节点值
sum += root.val;
//到达叶子结点
if (root.left == null && root.right == null){
if (sum == targetSum) return true;
}
//左面有结点先添加左面结点
if (root.left != null){
//递归添加左面结点添加
if (hasPathSum(root.left, targetSum)) return true;
//回溯到添加这个左结点之前(开始一直没想明白为什么回溯只需要删除一个结点,而不是删除当前root后的所有结点:是因为每次递归都会删除当前添加的一个结点,所以这时候path就只剩下多余的一个结点了)
sum -= root.left.val;
}
if (root.right != null){
//递归添加左面结点添加
if (hasPathSum(root.right, targetSum)) return true;
//回溯到添加这个左结点之前(开始一直没想明白为什么回溯只需要删除一个结点,而不是删除当前root后的所有结点:是因为每次递归都会删除当前添加的一个结点,所以这时候path就只剩下多余的一个结点了)
sum -= root.right.val;
}
//遍历完了所有路径,仍未返回,则返回fasle
return false;
}
}
113算法思路:先序遍历每一个结点,将结点放入path,如果遍历到叶子结点时,判断路径和是否为目标值,如果是则记录路径。如果不是叶子结点,则遍历左结点,递归添加左面结点,存入所有路径后进行回溯。再递归添加右面结点,存入所有路径后进行回溯。最后返回。
import java.util.ArrayList;
import java.util.List;
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
//用于存储最终结果
List<List<Integer>> res = new ArrayList<>();
//用于存储每条路径值的List
List<Integer> path = new ArrayList<>();
traversal(root, path, res, targetSum);
return res;
}
public void traversal(TreeNode root, List<Integer> path, List<List<Integer>> res, int targetSum){
if (root == null) return;
//path中加入当前节点
path.add(root.val);
//到达叶子结点
if (root.left == null && root.right == null){
int sum = 0;
for (int i: path) {
sum += i;
}
if (sum == targetSum) res.add(new ArrayList<>(path));
return;
}
//左面有结点先添加左面结点
if (root.left != null){
//递归添加左面结点添加
traversal(root.left, path, res, targetSum);
//回溯到添加这个左结点之前(开始一直没想明白为什么回溯只需要删除一个结点,而不是删除当前root后的所有结点:是因为每次递归都会删除当前添加的一个结点,所以这时候path就只剩下多余的一个结点了)
path.remove(path.size() - 1);
}
if (root.right != null){
//递归添加左面结点
traversal(root.right, path, res, targetSum);
//回溯到添加这个右结点之前
path.remove(path.size() - 1);
}
//遍历完了所有路径
return;
}
}
105.从前序与中序遍历序列构造二叉树、106.从中序与后序遍历序列构造二叉树
105:题目链接
106:题目链接
问题简述:将无重复元素的前序与中序遍历序列构造二叉树;将无重复元素的中序与后序遍历序列构造二叉树。
思考:看了解析,直接定义一个指向新的preorder的头和尾的指针就好,不需要每次新定义一个数组了;其次想找到某个元素下标用map简单很多,只写了105题,以后再写优化后的106。
105算法思路:每次找到前序遍历第一个元素在中序序列中的位置,用这个位置把preorder和inorder的元素重新分割为左子树的preorder和inorder与右子树的preorder和inorder。然后递归链接跟结点的左右子树。直到遍历完所有节点。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
//如果无元素,则返回空结点
if (preorder.length == 0 && inorder.length == 0) return null;
//创建当前元素结点
TreeNode root = new TreeNode(preorder[0]);
//记录跟结点在中序遍历位置
int a = 0;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == root.val){
a = i;
break;
}
}
//左子树构造
int[] newpreorder1 = new int[a];
int[] newinorder1 = new int[a];
//左子树新的前序数组赋值
for (int i = 0; i < a; i++) {
newpreorder1[i] = preorder[i + 1];
}
//左子树新的中序数组赋值
for (int i = 0; i < a; i++) {
newinorder1[i] = inorder[i];
}
root.left = buildTree(newpreorder1, newinorder1);
//右子树构造
int n = preorder.length - a - 1;
int[] newpreorder2 = new int[n];
int[] newinorder2 = new int[n];
//右子树新的前序数组赋值
for (int i = 0; i < n; i++) {
newpreorder2[i] = preorder[a + 1 + i];
}
//左子树新的中序数组赋值
for (int i = 0; i < n; i++) {
newinorder2[i] = inorder[a + 1 + i];
}
root.right = buildTree(newpreorder2, newinorder2);
return root;
}
}
感想
我觉得今天很难过,很焦虑,几天没怎么学习;女朋友都找到了工作还在考虑我,我配吗?同龄人的朋友圈丰富多彩,考上研的旅游,没考上的有了工作,早早工作的也有了自己的乐趣生活;已经读研的也有时间旅游。我觉得我就是废物,想似。