513.找树左下角的值
题目:力扣
这道题用层序遍历的模板就可以直接找到左下角的节点了。
public int findBottomLeftValue(TreeNode root) {
//层序遍历
List<List<Integer>> result = new ArrayList<>();
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
int size = queue.size();//计算单层node的个数
List<Integer> list = new ArrayList<>();//使用list储存单层node
for(int i=0; i<size; i++){
TreeNode node = queue.poll();
list.add(node.val);
//储存下一层node
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
result.add(list);//将单层node的list放入result
}
return result.get(result.size()-1).get(0);//取出最后一层的第一个节点
}
但是因为只需要最后一层的第一个节点,因此可以优化 ---> 不用把所有的元素都储存,只储存每一层第一个元素
public int findBottomLeftValue(TreeNode root) {
//层序遍历
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
int result = 0;//记录每一层的第一个节点值
while(!queue.isEmpty()){
int size = queue.size();//计算单层node的个数
List<Integer> list = new ArrayList<>();//使用list储存单层node
for(int i=0; i<size; i++){
TreeNode node = queue.poll();
list.add(node.val);
//储存下一层node
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
result = list.get(0);//更新result
}
return result;
}
这道题如果采用递归的算法来做的话,思路上会比较复杂:
首先需要了解,寻找树左下角的值,并不等于一定是左叶子,假如说一个二叉树的最大深度位置只有右节点,那么这个值也是树左下角的值。
因此在递归的时候可以理解成:寻找最大深度位置的第一个节点。
这里采用的是前序遍历的方式 - 中间节点处并没有任何处理。
递归思路:
- 确定输入和输出:输入(root);输出(无) --- 这里在寻找最大深度的时候需要用到回溯的方法,因此需要两个全局变量<1. 最大深度;2.最大深度的第一个节点>
- 确定终止条件: 当遇到叶子节点的时候,判断此时的深度是否大于最大深度,如果大于则需要更新最大深度和第一个节点值
- 单层遍历逻辑:先遍历左节点、再遍历右节点。保证最大深度的第一个节点值在左侧。
代码实现:
<这里需要注意回溯的写法 - 以及回溯的隐藏写法>
隐藏回溯的写法:
class Solution {
public int findBottomLeftValue(TreeNode root) {
bfs(root, 0);
return result;
}
int result = 0;
int maxDepth = -1;
public void bfs(TreeNode root, int depth){
//终止条件
if(root.left == null && root.right == null){
if(depth > maxDepth){
maxDepth = depth;
result = root.val;
}
return;
}
//左
if(root.left != null){
bfs(root.left, depth+1);//隐藏回溯
}
//右
if(root.right != null){
bfs(root.right, depth+1);//隐藏回溯
}
return;
}
}
显示回溯的写法:<需要回溯的变量不能在函数中有变化>
class Solution {
public int findBottomLeftValue(TreeNode root) {
bfs(root, 0);
return result;
}
int result = 0;
int maxDepth = -1;
public void bfs(TreeNode root, int depth){
//终止条件
if(root.left == null && root.right == null){
if(depth > maxDepth){
maxDepth = depth;
result = root.val;
}
return;
}
//左
if(root.left != null){
depth++;
bfs(root.left, depth);
depth--;//回溯
}
//右
if(root.right != null){
depth++;
bfs(root.right, depth);
depth--;//回溯
}
return;
}
}
参考资料:代码随想录
112. 路径总和
题目:力扣
这道题应该是递归+回溯的做法。
可以采用前序遍历的方式 - 边遍历边累加值
递归思路:
- 输入和输出:输入( root,当前累加值);输出(无)---采用全局变量 - target,是否存在标志1/0
- 终止条件:当遍历到叶子节点的时候,加入叶子节点的值,判断当前累加值是否为target
- 单层循环逻辑:从左边子树遍历,再遍历右边子树。 -- 需要回溯
在实际实现的时候需要注意单层逻辑中 - 添加的节点值是什么(这里统一是添加要遍历的节点的值)【不能主方法添加的要遍历的节点的值,辅助方法的单层逻辑添加的仍然是上一层节点的值,要注意统一】
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null){
return false;
}
target = targetSum;
bfs(root, root.val);
return isValid == 1;
}
int target = 0;
int isValid = 0;
public void bfs(TreeNode root, int sum){
//终止条件
if(root.left == null && root.right == null){
if(target == sum){
isValid = 1;
}
return;
}
//左
if(root.left != null){
sum += root.left.val;
bfs(root.left, sum);
sum -= root.left.val;//回溯
}
if(root.right != null){
sum += root.right.val;
bfs(root.right, sum);
sum -= root.right.val;//回溯
}
return;
}
}
另一种实现方法是:1)把目标值减去节点的值,那么一直到叶子节点,如果目标值变为0,说明是存在这条路径的。这样在代码实现上会更加简洁。2)不用标志进行判断是否存在,直接把回溯函数改为boolean,存在的话就直接返回true。
这样实现的时候就不需要再设置两个全局变量来记录了target 和 存在标志。
需要注意的是:如果通过boolean的话进行递归,需要传递单层循环return的值。---> if(bfs(root.left, sum)) return true; 如果不传递的话,可能这个地方的boolean值仍然是false。
代码实现:
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null){
return false;
}
return bfs(root, targetSum - root.val);
}
public boolean bfs(TreeNode root, int sum){
//终止条件
if(root.left == null && root.right == null){
if(sum == 0){
return true;
}
return false;
}
//左
if(root.left != null){
sum -= root.left.val;
if(bfs(root.left, sum)) return true;
sum += root.left.val;//回溯
}
if(root.right != null){
sum -= root.right.val;
if(bfs(root.right, sum)) return true;
sum += root.right.val;//回溯
}
return false;
}
}
参考资料:代码随想录
113. 路径总和ii
题目:力扣
这道题和112的解题思路是一样的,直接用递归+回溯的方法做,并且记录符合的路径就可以。
区别:在终止条件中,若是符合,需要将list放到结果集中。
需要注意的是:放入的时候要放new ArrayList<>(list)(重新复制一份), 而不是list,引用指针是不行的。
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
target = targetSum;
result = new ArrayList<>();
if(root == null){
return result;
}
List<Integer> list = new ArrayList<>();
list.add(root.val);
bfs(root, root.val, list);
return result;
}
List<List<Integer>> result;
int target = 0;
public void bfs(TreeNode root, int sum, List<Integer> list){
//终止条件
if(root.left == null && root.right == null){
if(target == sum){
result.add(new ArrayList<>(list));
}
return;
}
//左
if(root.left != null){
sum += root.left.val;
list.add(root.left.val);
bfs(root.left, sum, list);
sum -= root.left.val;//回溯
list.remove(list.size()-1);
}
if(root.right != null){
sum += root.right.val;
list.add(root.right.val);
bfs(root.right, sum, list);
sum -= root.right.val;//回溯
list.remove(list.size()-1);
}
return;
}
}
106.从中序与后序遍历序列构造二叉树
题目:力扣
已知中序和后序,二叉树是很容易被画出来的。
在每一次的分割都都是单层的循环。
递归逻辑:
- 输入和输出:输入(后序list,中序list中的左侧index,中序list中的右侧index,后序list中的左index,后序list中的右index);输出 - 根节点;
- 终止条件:边界左侧大于等于边界右侧,说明是空节点,返回null;<注意左闭右开原则>
- 单层循环逻辑:通过后序list找到最后一位,确定此处的节点值index,生成一个新节点,将节点值放入新节点中;将中序list进行分割,递归二分 - index前的部分为左子树左右边界、index后的部分为右子树(左右边界);对后序list也进行分割,找到左子树位置的index(左右边界),找到右子树位置的index(左右边界)---> 根据中序和后序的左右边界找root的左节点;根据中序和后序的左右边界找root的右节点
这里需要注意的是1)边界的计算方式(左闭右开) - 2)这里采用的遍历方式是前序遍历(先处理中间节点,然后再往左侧和右侧遍历);
3)为了能够快速找到中序list中节点的index,在主方法中先用全局变量map来储存了中序遍历中元素的index。
代码实现:
class Solution {
Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(inorder == null || postorder == null){
return null;
}
//采用map储存中序节点的index,方便快速找到位置
for(int i=0; i< inorder.length; i++){
map.put(inorder[i], i);
}
return buildTreeHelper(postorder, 0, inorder.length, 0, postorder.length);
}
public TreeNode buildTreeHelper(int[] postorder, int inorder_left, int inorder_right, int postorder_left, int postorder_right) {
//终止条件
if(inorder_left >= inorder_right || postorder_left >= postorder_right){
return null;
}
int value = postorder[postorder_right-1];
TreeNode root = new TreeNode(value);
int index = map.get(value);//中序遍历中节点的index位置
//左侧
//inorder_left
//index
//postorder_left
//postorder_left+index-inorder_left
//右侧
//index+1
//inorder_right
//postorder_left+index-inorder_left
//postorder_right-1
root.left = buildTreeHelper(postorder, inorder_left, index, postorder_left, postorder_left+index-inorder_left);
root.right = buildTreeHelper(postorder, index+1, inorder_right, postorder_left+index-inorder_left, postorder_right-1);
return root;
}
}
参考资料:代码随想录
105.从前序与中序遍历序列构造二叉树
题目:力扣
这道题的解题思路是和106一样的,边界值的求法稍有不同。
同样是采用了前序遍历的方式,左闭右开求左右边界。
递归逻辑:
- 输入和输出:输入(前序list,后序list,中序list中的左侧index,中序list中的右侧index,后序list中的左index,后序list中的右index);输出 - 根节点;
- 终止条件:边界左侧大于等于边界右侧,说明是空节点,返回null;<注意左闭右开原则>
- 单层循环逻辑:通过前list找到第一位,确定此处的节点值index,生成一个新节点,将节点值放入新节点中;将中序list进行分割,递归二分 - index前的部分为左子树左右边界、index后的部分为右子树(左右边界);对前list也进行分割,找到左子树位置的index(左右边界),找到右子树位置的index(左右边界)---> 根据中序和前序的左右边界找root的左节点;根据中序和前序的左右边界找root的右节点
同样为了能够快速找到中序list中的index,采用map将中序遍历的index进行储存。
代码实现:
class Solution {
Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder == null || inorder == null){
return null;
}
for(int i=0; i<inorder.length; i++){
map.put(inorder[i], i);
}
return buildTreeHelper(preorder, 0, preorder.length, 0, inorder.length);
}
public TreeNode buildTreeHelper(int[] preorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
//终止条件
if(preorder_left >= preorder_right || inorder_left >= inorder_right){
return null;
}
TreeNode root = new TreeNode(preorder[preorder_left]);
int index = map.get(preorder[preorder_left]);
//左边
//preorder_left+1
//preorder_left+1+index-inorder_left
//inorder_left
//index
//右边
//preorder+1+index-inorder_left
//preorder_right
//index+1
//inorder_right
root.left = buildTreeHelper(preorder, preorder_left+1, preorder_left+1+index-inorder_left, inorder_left, index);
root.right = buildTreeHelper(preorder, preorder_left+1+index-inorder_left, preorder_right, index+1, inorder_right);
return root;
}
}