LeetCode 513 找树左下角的值
思路
左下角的值:树最后一行最左边的值 涉及到最后一行,使用层序遍历会简单很多。
迭代法:层序遍历
全局变量mostLeft保存层序遍历中每一层最左边的值。因为最后遍历的一定是最后一层,所以此变量最后留下的一定是最后一层最左边的值
递归法
从深度优先搜索角度考虑,树左下角的值是前序遍历到的第一个深度最大的值。 因此在前序遍历过程中,用全局变量记录递归的深度,当每次首次到一个更大的深度,就记录下节点的值到全局变量mostLeft。这样最后留下的值一定是全局最大深度下第一个值,即左下角的值
考虑递归三要素:
- 递归函数的参数和返回值 全局变量:mostLeft记录最左值,depth记录深度,maxDepth记录曾经到达过的最大深度 参数:根节点 返回值:空
- 递归函数的结束条件 节点为空,返回
- 单层递归逻辑 depth+1 如果depth>maxDepth,把当前节点值记录在mostLeft中,并更新最大深度 分别对左右子节点递归调用 depth-1
解法
迭代法:层序遍历
class Solution {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
int mostLeft = 0;
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (i==0) {
mostLeft = node.val;
}
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
return mostLeft;
}
}
递归法
class Solution {
int depth;
int maxDepth;
int mostLeft;
public int findBottomLeftValue(TreeNode root) {
depth = 0;
maxDepth = 0;
mostLeft = 0;
traverse(root);
return mostLeft;
}
public void traverse(TreeNode root) {
if (root == null) {
return ;
}
depth++;
if (maxDepth < depth) {
maxDepth = depth;
mostLeft = root.val;
}
traverse(root.left);
traverse(root.right);
depth--;
}
}
LeetCode 112&113 路径总和
112思路
考虑递归三要素:
- 递归函数的参数和返回值 全局变量:当前路径总和sumPath,标志位flag 参数:根节点root 返回值:空
- 递归函数的结束条件 根节点为空,返回
- 单层递归逻辑 在路径中加入本节点的值 如果本节点为叶子节点,判断路径总和是否满足要求,是则把flag置为true 如果不是叶子节点,对左右子节点递归调用 在路径中减去本节点的值
112解法
个人认为,回溯相比于单纯的递归需要多考虑:当函数在返回时,变量的状态。通常的操作就是要把状态变量还原
class Solution {
boolean flag = false;
int pathSum = 0;
int targetSum;
public boolean hasPathSum(TreeNode root, int targetSum) {
this.targetSum = targetSum;
traverse(root);
return flag;
}
public void traverse(TreeNode root) {
if (root == null) {
return ;
}
pathSum += root.val;
if (root.left == null && root.right == null) {
if (pathSum == this.targetSum) {
flag = true;
}
}
else {
traverse(root.left);
traverse(root.right);
}
pathSum -= root.val;
}
}
113思路
考虑递归三要素:
- 递归函数的参数和返回值 全局变量:path当前路径,pathSum当前路径和,result结果集 参数:根节点 返回值:空
- 递归函数的结束条件 当根节点为空,返回
- 单层递归逻辑 更新path和pathSum 如果节点是叶子节点,判断路径和是否满足要求,是则把路径的副本加入结果集合 不是叶子节点则分别对左右子节点递归调用 还原path和pathSum
113解法
class Solution {
int targetSum;
int pathSum;
List<Integer> path;
List<List<Integer>> result;
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
pathSum = 0;
this.targetSum = targetSum;
result = new ArrayList<>();
path = new ArrayList<>();
traverse(root);
return result;
}
public void traverse(TreeNode root) {
if (root == null) {
return ;
}
path.add(root.val);
pathSum += root.val;
if (root.left == null && root.right == null) {
if (pathSum == this.targetSum) {
result.add(new ArrayList<>(path));
}
}
else {
traverse(root.left);
traverse(root.right);
}
path.remove(path.size()-1);
pathSum -= root.val;
}
}
LeetCode 106 从中序与后序遍历序列构造二叉树
思路
考虑树的中序遍历:左子树-根节点-右子树
考虑树的后序遍历:左子树-右子树-根节点
所以根据后序遍历的最后一个节点,可以在中序遍历中分割出左右子树。左子树在中序后序遍历中位置相同,所以在相同位置取子数组就可以得到左子树的中序和后序遍历,对应也得到右子树的。再次应用根节点在末尾原则,又得到了左右子树的根节点。如此递归下去,就可以构建出二叉树。
考虑递归三要素:
- 递归函数的参数和返回值 参数:中序数组inOrder,后序数组postOrder 返回值:构建出的树的根节点root
- 递归函数的结束条件 数组为空,返回null
- 单层递归逻辑 取后序数组的最后一个元素构建根节点 用根节点分割子数组 递归对左右子树调用,返回值赋给根节点的左右指针 返回根节点
解法
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
if (inorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(postorder[postorder.length-1]);
int rootIndex = 0;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == root.val) {
rootIndex = i;
break;
}
}
int[] inOrderLeft = Arrays.copyOfRange(inorder, 0, rootIndex);
int[] postorderLeft = Arrays.copyOfRange(postorder, 0, rootIndex);
int[] inOrderRight = Arrays.copyOfRange(inorder, rootIndex+1, inorder.length);
int[] postOrderRight = Arrays.copyOfRange(postorder, rootIndex, postorder.length-1);
root.left = buildTree(inOrderLeft, postorderLeft);
root.right = buildTree(inOrderRight, postOrderRight);
return root;
}
}
LeetCode 105 从前序与中序遍历序列构造二叉树
思路
考虑树的中序遍历:左子树-根节点-右子树
考虑树的前序遍历:根节点-左子树-右子树
所以根据前序遍历的第一个节点,可以在中序遍历中找到根节点索引并分割出左右子树。右子树在中序前序遍历中位置相同,所以在相同位置取子数组就可以得到右子树的中序和后序遍历,对应也得到左子树的。再次应用根节点在首部原则,又得到了左右子树的根节点。如此递归下去,就可以构建出二叉树。
考虑递归三要素:
- 递归函数的参数和返回值 参数:中序数组inOrder,前序数组preOrder 返回值:构建出的树的根节点root
- 递归函数的结束条件 数组为空,返回null
- 单层递归逻辑 取前序数组的第一个元素构建根节点 用根节点分割子数组 递归对左右子树调用,返回值赋给根节点的左右指针 返回根节点
解法
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preorder[0]);
int rootIndex = -1;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == root.val) {
rootIndex = i;
break;
}
}
int[] inorderLeft = Arrays.copyOfRange(inorder, 0, rootIndex);
int[] inorderRight = Arrays.copyOfRange(inorder, rootIndex+1, inorder.length);
int[] preorderRight = Arrays.copyOfRange(preorder, rootIndex+1, preorder.length);
int[] preorderLeft = Arrays.copyOfRange(preorder, 1, rootIndex+1);
root.left = buildTree(preorderLeft, inorderLeft);
root.right = buildTree(preorderRight, inorderRight);
return root;
}
}
今日收获总结
今日学习4小时,除了最后两题构造二叉树都独立做出来了。构造二叉树的思路还需要复习~