513. 找树左下角的值
题目链接
题目描述:
给定一个二叉树,在树的最后一行找到最左边的值。
示例 1:
示例2:
难点:
递归法
思路:
这题要找
- 最底层
- 最左边
很容易就想到层序遍历
递归法的话,有点复杂
要向左找到最大深度,那么“左”比“右”先遍历,前中后序遍历都可以
时间复杂度:O()
空间复杂度:O()
//使用层序遍历,最下面一层的第一个就是要找的
class Solution {
public int findBottomLeftValue(TreeNode root) {
Deque<TreeNode> que = new ArrayDeque<>();
int res = 0;
que.addLast(root);
while(!que.isEmpty()) {
int len = que.size();
for (int i = 0; i < len; i++) {
TreeNode cur = que.removeFirst();
if (i == 0) res = cur.val; //记录当前层最左侧元素
if (cur.left != null) que.addLast(cur.left);
if (cur.right != null) que.addLast(cur.right);
}
}
return res;
}
}
//递归法
class Solution {
int maxDepth = 0;
int result = Integer.MAX_VALUE;
public int findBottomLeftValue(TreeNode root) {
result = root.val;
traversal(root, 0);
return result;
}
private void traversal(TreeNode root, int depth) {
if (depth > maxDepth) { //遇到更大的深度就更新(因为“左”比“右”先遍历,所以深度下探最先访问左边的结点)
maxDepth = depth;
result = root.val;
}
if (root.left != null) {
traversal(root.left, depth+1);
}
if (root.right != null) {
traversal(root.right, depth+1);
}
}
}
时长:
20min
收获:
递归法的分析练习
还可以练习,优先掌握递归法:
110. 平衡二叉树
257. 二叉树的所有路径
112. 路径总和
题目链接
题目描述:
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例: 给定如下二叉树,以及目标和 sum = 22,
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
提示:
树中节点的数目在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
难点:
注意题目中结点的值是可以为负数的~~~
思路:
看到这题马上想到应该用递归会更好写一些
就用前序遍历吧~
要判断和targetSum
的关系,递归函数中就需要有当前总和curSum
在单层递归中,要判断是否找到了叶子结点
如果找到叶子,判断总和是否相同
时间复杂度:O()
空间复杂度:O()
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) return false;
int curSum = 0;
return traversal(root, curSum, targetSum);
}
private boolean traversal(TreeNode root, int curSum, int targetSum) {
if (root == null) return false;
curSum += root.val;
if (root.left == null && root.right == null) { //遍历到叶子节点
if (curSum == targetSum) {
return true;
}else {
return false;
}
}
//左右子树只要有满足条件的路径就返回
return traversal(root.left, curSum, targetSum) || traversal(root.right, curSum, targetSum);
}
}
参考代码随想录还有更简洁的方法,其实这个减法思想在两数之和就接触到过,注意消化:
class solution {
public boolean haspathsum(treenode root, int targetsum) {
if (root == null) {
return false;
}
targetsum -= root.val;
// 叶子结点
if (root.left == null && root.right == null) {
return targetsum == 0;
}
if (root.left != null) {
boolean left = haspathsum(root.left, targetsum);
if (left) { // 已经找到
return true;
}
}
if (root.right != null) {
boolean right = haspathsum(root.right, targetsum);
if (right) { // 已经找到
return true;
}
}
return false;
}
}
// 简洁方法
class solution {
public boolean haspathsum(treenode root, int targetsum) {
if (root == null) return false; // 为空退出
// 叶子节点判断是否符合
if (root.left == null && root.right == null) return root.val == targetsum;
// 求两侧分支的路径和
return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
}
}
时长:
12min
收获:
求目标总和的减法思维
可以练习:
113. 路径总和 II
构造二叉树
106. 从中序与后序遍历序列构造二叉树
105. 从前序与中序遍历序列构造二叉树
难点:
找到切分点,递归地去构造,注意区间划分要准确,统一开闭
思路:
//中序+后序 构造二叉树
class Solution {
Map<Integer, Integer> map; // 方便根据数值查找位置
public TreeNode buildTree(int[] inorder, int[] postorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(inorder, 0, inorder.length, postorder,0, postorder.length); // 前闭后开
}
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
// 参数里的范围都是前闭后开
if (inBegin >= inEnd || postBegin >= postEnd) { // 不满足左闭右开,说明没有元素,返回空树
return null;
}
int delimiterIndex = map.get(postorder[postEnd - 1]); // 找到后序遍历的最后一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[delimiterIndex]); // 构造结点
int lenOfLeft = delimiterIndex - inBegin; // 保存中序左子树个数,用来确定后序数列的个数
root.left = findNode(inorder, inBegin, delimiterIndex,
postorder, postBegin, postBegin + lenOfLeft);
root.right = findNode(inorder, delimiterIndex + 1, inEnd,
postorder, postBegin + lenOfLeft, postEnd - 1);
return root;
}
}
//前序+中序 构造二叉树
class Solution {
Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(preorder, 0, preorder.length, inorder, 0, inorder.length); // 前闭后开
}
public TreeNode findNode(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd) {
// 参数里的范围都是前闭后开
if (preBegin >= preEnd || inBegin >= inEnd) { // 不满足左闭右开,说明没有元素,返回空树
return null;
}
int delimiterIndex = map.get(preorder[preBegin]); // 找到前序遍历的第一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[delimiterIndex]); // 构造结点
int lenOfLeft = delimiterIndex - inBegin; // 保存中序左子树个数,用来确定前序数列的个数
root.left = findNode(preorder, preBegin + 1, preBegin + lenOfLeft + 1,
inorder, inBegin, delimiterIndex);
root.right = findNode(preorder, preBegin + lenOfLeft + 1, preEnd,
inorder, delimiterIndex + 1, inEnd);
return root;
}
}
时长:
20min
收获:
先构造map,每次查找直接可以获取到根节点的位置
比每次遍历整个数组找切分结点要方便~
前序和中序可以唯一确定一棵二叉树。
后序和中序可以唯一确定一棵二叉树。
那么前序和后序可不可以唯一确定一棵二叉树呢?
前序和后序不能唯一确定一棵二叉树!,因为没有中序遍历无法确定左右部分,也就是无法分割。