视频链接:
怎么找二叉树的左下角? 递归中又带回溯了,怎么办?| LeetCode:513.找二叉树左下角的值_哔哩哔哩_bilibili
拿不准的遍历顺序,搞不清的回溯过程,我太难了! | LeetCode:112. 路径总和_哔哩哔哩_bilibili
坑很多!来看看你掉过几次坑 | LeetCode:106.从中序与后序遍历序列构造二叉树_哔哩哔哩_bilibili
思路:题目的目标是找最底层 最左边的节点,因此在遍历策略上,深度优先与广度优先都可以实现本题的求解,广度优先遍历很好理解,只要输出列表最后一项元素内最开始值的即可;而对于深度优先遍历,需要额外明确的一点就是,对于deep层,其最底层、最左边的节点一定是在遍历到达一个新的层次时当前指针所指向的节点,其原因在于深度优先是路径优先的遍历方式,因此如果存在可访问到deep层的路径,则在当前路径之前已经出现了访问deep的记录,但是现在并没有deep层的信息出现,直到当前路径的出现,所以最小的节点就是每一次到层新的层次了所访问的第一个节点。
仅仅只是因为访问到了新的节点并且出现了新的层次,则当前的节点可能就是最底层、最左边的节点,只要最后再比较一下该元素是否是所记录的元素中的最后一个,即可知道自己是否为左下角的值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int findBottomLeftValue(TreeNode root) {
// 使用层次遍历是解题的关键
// 但是我们观察递归的做法,发现递归的做法中,在进入一个新的层次开始创建列表的时候,一定是最最左边的孩子先进来的,原因是我们使用的是先序遍历,先序遍历与中序遍历都是深度优先的遍历策略
// 但是有一点就是必须要把所有的元素都访问结束才可以得到最下层的最左边元素,因为我们在直接对树进行遍历时不知道树的高度,当然也可以先求树的高度,然后再去直接找对应层次的第一个做节点
List list = new ArrayList<>();
BFS(root, list, 1);
return list.get(list.size()-1);
}
public void BFS(TreeNode t, List list, int deep){
// 层次遍历实现解题
// 使用的是先序遍历的思想
// 停止条件
if(t== null)
return;
// 判断树是否已经遍历到当前节点所处于的层次,如果deep从0开始,那么记得加一再做if判断
if(list.size() < deep){
// 全局列表中加入当前列表,表示全局已经准备好访问当前层次了
// 而且仅仅加入每个层次所访问到的第一个节点
list.add(t.val);
}
// 将指定层次的元素放到指定的数组之中去
BFS(t.left, list, deep+1);
BFS(t.right, list, deep+1);
}
}
思路:类似于 257. 二叉树的所有路径,是一样的道理,这里只不过是对于路径上面的每个元素不经是存储,还需要相加。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
// 很简单深度优先,先序遍历
return DFS(root, 0, targetSum);
}
public boolean DFS(TreeNode t, int sum, int targetSum){
// 停止条件
if(t == null)
return false;
// 先序遍历,所以确保了访问的顺序一定是沿着路径进行的
// 其次利用递归,在每一次进入一个新节点后,加入这个值来判断是否与targetsum相等
if(t.left == null && t.right == null && t.val + sum == targetSum)
return true;
return DFS(t.left, sum+t.val, targetSum) || DFS(t.right, sum+t.val, targetSum);
}
}
思路:首先自己按照中序与后序遍历之后的输出中所存在的结果,递归实现了二叉树的构成,但时间开销较大,因此思考在105.从前序和中序序列构造二叉树使用其他方法优化时间效率。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
// 时间复杂度O(n^2),空间复杂度O(n)
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return inpost(inorder, postorder, inorder.length);
}
public TreeNode inpost(int[] inorder, int[] postorder, int len){
// 停止条件
if(len == 0)
return null;
int[] arr_in_l = new int[len];
int[] arr_in_r = new int[len];
int[] arr_post_l = new int[len];
int[] arr_post_r = new int[len];
int left = 0;
int right = 0;
// 构建子树根结点
TreeNode t = new TreeNode(postorder[len-1], null, null);
// 确定子根结点所在的索引
int mid = Integer.MAX_VALUE;
for(int i = 0; i < len; i++){
if(i < mid){
if (inorder[i] == t.val){
mid = i;
left = mid; // 记录左子树的长度
continue;
}
// 分割出左子树
arr_in_l[i] = inorder[i];
arr_post_l[i] = postorder[i];
}
else{
arr_in_r[i-mid-1] = inorder[i];
arr_post_r[i-mid-1] = postorder[i-1];
}
}
right = len - 1 - mid; // 记录右子树的长度
// 形成左右子树后继续递归
if(left == 1)
t.left = new TreeNode(arr_in_l[0], null, null);
else
t.left = inpost(arr_in_l, arr_post_l, left);
if(right == 1)
t.right = new TreeNode(arr_in_r[0], null, null);
else
t.right = inpost(arr_in_r, arr_post_r, right);
return t;
}
}