Day17
今日任务
- 110.平衡二叉树
-
- 二叉树的所有路径
- 404.左叶子之和
代码实现
110.平衡二叉树
这里的巧妙之处在于发现不是一颗平衡二叉树的时候返回了-1,这样把计算高度和判断是否是平衡二叉树可以组合起来,这里我昨天想了半天,想不出,然后看题解,感觉会了,第二天再写,还是写不出,巧妙
public boolean isBalanced(TreeNode root) {
return getHeight(root) > 0;
}
int getHeight(TreeNode cur) {
//左 右 中 后序遍历
if (cur == null) return 0;
int leftHeight = getHeight(cur.left);
if (leftHeight == -1) {
return -1;
}
int rightHeight = getHeight(cur.right);
if (rightHeight == -1) {
return -1;
}
return Math.abs(leftHeight - rightHeight) > 1 ? -1 : 1 + Math.max(leftHeight, rightHeight);
}
- 二叉树的所有路径
这里主要是需要记录路径,说实话,第一次写一点思路也没,现在都不会思考了
public List<String> binaryTreePaths(TreeNode root) {
if (root == null) return new ArrayList<>();
List<String> result = new ArrayList<>();
List<Integer> values = new ArrayList<>();
getBinaryPath(root, values, result);
return result;
}
void getBinaryPath(TreeNode cur, List<Integer> values, List<String> paths) {
//这里这样写我认为是为了 当cur.left 和 cur.right 都为null的时候,下边会进入两次递归调用
//中 左 右 先序遍历
values.add(cur.val);
if (cur.left == null && cur.right == null) {
paths.add(getPath(values));
return;
}
if (cur.left != null) {
getBinaryPath(cur.left, values, paths);
//每次处理完成后要移除掉最后一个元素也就是cur.left,这是因为在递归中已经获取到cur.left下面所有叶子节点的路径了,现在要往上返回
values.remove(values.size() - 1);
}
if (cur.right != null) {
getBinaryPath(cur.right, values, paths);
values.remove(values.size() - 1);
}
}
String getPath(List<Integer> values) {
return values.stream().map(Object::toString).collect(Collectors.joining("->"));
}
404.左叶子之和
这道题用递归的话其实比较简单,关键在于如何定义左叶子,要敢于用上一层的节点去判断,不局限于递归的current节点中
public int sumOfLeftLeaves(TreeNode root) {
//
if (root == null) return 0;
if (root.left != null && root.left.left == null && root.left.right == null) {
return root.left.val + sumOfLeftLeaves(root.right);
}
return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
}
Day18
今日任务
- 513.找树左下角的值
-
- 路径总和 113.路径总和ii
- 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树
代码实现
513.找树左下角的值
这里用层序遍历的话比较简单,用递归的话需要你定义两个全局变量,这个不太好想
//层序遍历
public int findBottomLeftValue(TreeNode root) {
int result = root.val;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode poll = queue.poll();
if (i == 0) {
result = poll.val;
}
if (poll.left != null) queue.add(poll.left);
if (poll.right != null) queue.add(poll.right);
}
}
return result;
}
//递归,只给出递归体,没写调用
int maxDepth = Integer.MIN_VALUE; // 全局变量 记录最大深度
int result; // 全局变量 最大深度最左节点的数值
public void findLeftMost(TreeNode cur, int depth) {
if (cur == null) return;
if (depth > maxDepth) {
result = cur.val;
}
findLeftMost(cur.right, depth + 1);
findLeftMost(cur.left, depth +1);
}
- 路径总和 113.路径总和ii
这两道题的思路是一样的,和昨天的二叉树的所有路径也是一样的,正好我是一天做的,所以感觉简单
//路径总和
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) return false;
return hasTargetSum(root, 0, targetSum);
}
public boolean hasTargetSum(TreeNode cur, int beforeSum, int targetSum) {
if (cur == null) return false;
beforeSum+=cur.val;
if (cur.left == null && cur.right == null && targetSum == beforeSum) {
return true;
}
return hasTargetSum(cur.left, beforeSum, targetSum) || hasTargetSum(cur.right, beforeSum, targetSum);
}
//路径总和ii
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
if(root == null) return new ArrayList();
List<List<Integer>> result = new ArrayList<>();
hasTargetSum(root, 0, targetSum, result, new ArrayList<>());
return result;
}
public void hasTargetSum(TreeNode cur, int beforeSum, int targetSum, List<List<Integer>> result, List<Integer> beforeValues) {
beforeSum+=cur.val;
beforeValues.add(cur.val);
if (cur.left == null && cur.right == null && targetSum == beforeSum) {
result.add(new ArrayList<>(beforeValues));
}
if (cur.left != null) {
hasTargetSum(cur.left, beforeSum, targetSum, result, beforeValues);
beforeValues.remove(beforeValues.size() - 1);
}
if (cur.right != null) {
hasTargetSum(cur.right, beforeSum, targetSum, result, beforeValues);
beforeValues.remove(beforeValues.size() - 1);
}
}
从中序与后序遍历序列构造二叉树(节点中没有重复值)
这道题如果理清思路的话还是非常好写,同理,从前序与中序遍历序列构造二叉树也差不多
注意:前序和后序不能唯一的确定一颗二叉树
思路:首先由后序遍历数组确定中间的值(即root),然后用root值可以对中序遍历数组进行切分,找到切分的位置,即左右元素个数就确定了,此时可以对后序遍历的数组进行切分,然后对切分后的进行递归,即可确认每一个子树
//TODO 这里可以优化的是,在递归时不需要每次都搞一个新数组,而是用原数组+index的方式查找元素,
//这样写效率比较高,但是更容易出错,由于时间原因和身体原因,我就没有写了
public TreeNode buildTree(int[] inorder, int[] postorder) {
if (postorder == null || postorder.length == 0) return null;
if (postorder.length == 1) return new TreeNode(postorder[0]);
int middle = postorder[postorder.length - 1];
TreeNode root = new TreeNode(middle);
int middleIndex = 0;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == middle){
middleIndex = i;
}
}
int[] leftInorder = new int[middleIndex];
int[] rightInorder = new int[inorder.length - middleIndex - 1];
int[] leftPostorder = new int[middleIndex];
int[] rightPostorder = new int[inorder.length - middleIndex - 1];
for (int i = 0; i < inorder.length; i++) {
if (i < middleIndex) {
leftInorder[i] = inorder[i];
} else if (i > middleIndex) {
rightInorder[i - middleIndex - 1] = inorder[i];
}
}
for (int i = 0; i < postorder.length - 1; i++) {
if (i < middleIndex) {
leftPostorder[i] = postorder[i];
} else {
rightPostorder[i - middleIndex] = postorder[i];
}
}
root.left = buildTree(leftInorder, leftPostorder);
root.right = buildTree(rightInorder, rightPostorder);
return root;
}
两日总结
1.很多题目虽然是简单,看过解法之后也确实是简单,但是由于没有做过,对于很多技巧不知道,比如保存前值啦,设置全局变量啦,导致根本想不到,所以我基本上都是看了题解之后才能做出来,那么这一遍只能算是学习基础,了解一遍各种解法,后续再刷的时候才有机会自己想到;
2.今日发烧了,本来就不聪明的大脑现在更是雪上加霜;
3.现在的A股吧,买又怕回调,不买的话又怕踏空,真是左右为难,又没时间一直盯盘(盯了也不敢操作),希望早日回本。