LeetCode:513.找树左下角的值
解决方案:
1.思路
- 在遍历一个节点时,需要先把它的非空右子节点放入队列,
- 然后再把它的非空左子节点放入队列,这样才能保证从右到左遍历每一层的节点。
- 广度优先搜索所遍历的最后一个节点的值就是最底层最左边节点的值。
class Solution {
public int findBottomLeftValue(TreeNode root) {
int ret = 0;
//基于数组的双端队列
Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode p = queue.poll();
if (p.right != null) {
queue.offer(p.right);
}
if (p.left != null) {
queue.offer(p.left);
}
//如果当前队列不为空。当前根节点既没有左节点,也没有右节点,那么就把此该节点赋值给ret
ret = p.val;
}
return ret;
}
}
3.复杂度分析
LeetCode:112. 路径总和
解决方案:
1.思路:
递归三部曲:
- 函数返回类型和参数;
- 终止条件;
- 递归逻辑
递归逻辑
2.代码实现
public class Solution {
private boolean traversal(TreeNode cur, int count) {
if (cur.left == null && cur.right == null && count == 0) return true; // 遇到叶子节点,并且计数为0
if (cur.left == null && cur.right == null) return false; // 遇到叶子节点直接返回
if (cur.left != null) { // 左
count -= cur.left.val; // 递归,处理节点;
if (traversal(cur.left, count)) return true;
count += cur.left.val; // 回溯,撤销处理结果
}
if (cur.right != null) { // 右
count -= cur.right.val; // 递归,处理节点;
if (traversal(cur.right, count)) return true;
count += cur.right.val; // 回溯,撤销处理结果
}
return false;
}
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) return false;
return traversal(root, sum - root.val);
}
}
3.复杂度分析
LeetCode:113.路径总和ii
解决方案:
1.思路:
- 思路同112
- 但是注意递归函数不再有返回值,而是用一个 path数组接住;然后再用一个result数组接住所有path;
2.代码实现
public class Solution {
private List<List<Integer>> result = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
// 递归函数不需要返回值,因为我们要遍历整个树
private void traversal(TreeNode cur, int count) {
if (cur.left == null && cur.right == null && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
result.add(new ArrayList<>(path));
return;
}
if (cur.left == null && cur.right == null) return; // 遇到叶子节点而没有找到合适的边,直接返回
if (cur.left != null) { // 左 (空节点不遍历)
path.add(cur.left.val);
count -= cur.left.val;
traversal(cur.left, count); // 递归
count += cur.left.val; // 回溯
path.remove(path.size() - 1); // 回溯
}
if (cur.right != null) { // 右 (空节点不遍历)
path.add(cur.right.val);
count -= cur.right.val;
traversal(cur.right, count); // 递归
count += cur.right.val; // 回溯
path.remove(path.size() - 1); // 回溯
}
}
public List<List<Integer>> pathSum(TreeNode root, int sum) {
result.clear();
path.clear();
if (root == null) return result;
path.add(root.val); // 把根节点放进路径
traversal(root, sum - root.val);
return result;
}
}
3.复杂度分析
- 时间复杂度:O(N),其中N是树中节点的数量。这是因为每个节点在递归过程中会被访问一次。尽管存在递归调用,但每个节点只被访问并处理一次,因此总体时间复杂度线性依赖于树的大小,而不是递归深度。
- 空间复杂度:最坏情况下,当树完全不平衡,且每一条从根到叶子的路径都满足题目条件时,递归的深度达到最大,此时的空间复杂度由递归栈的深度决定,为O(N)。最好情况下(即树完全平衡),递归的最大深度为log(N),因此在这种情况下,空间复杂度为O(log(N))。但由于我们还需要存储路径(path),在最坏情况下(每条边都构成解),这也会占用O(N)的空间。因此,综合考虑,整体的空间复杂度也是O(N)。
LeetCode:106.从中序与后序遍历序列构造二叉树
解决方案:
1.思路:
- 利用遍历特性:中序遍历(左根右)确定节点在序列中的相对位置,后序遍历(左右根)的最后一个元素总是当前子树的根节点
- 递归思想:通过递归不断地将问题分解为更小的子问题,直到达到基础情况(空列表),然后逐层返回,逐步构建整棵树。
- 动态调整遍历列表:每次递归调用前,根据当前子树的信息调整中序和后序遍历列表,确保传入的列表仅对应当前子树的信息。
2.代码实现
import java.util.ArrayList;
import java.util.List;
public class Solution {
private TreeNode traversal(List<Integer> inorder, List<Integer> postorder) {
if (postorder.size() == 0) return null;
// 后序遍历数组最后一个元素,就是当前的中间节点
int rootValue = postorder.get(postorder.size() - 1);
TreeNode root = new TreeNode(rootValue);
// 叶子节点
if (postorder.size() == 1) return root;
// 找到中序遍历的切割点
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
if (inorder.get(delimiterIndex) == rootValue) break;
}
// 切割中序数组
List<Integer> leftInorder = new ArrayList<>(inorder.subList(0, delimiterIndex));
List<Integer> rightInorder = new ArrayList<>(inorder.subList(delimiterIndex + 1, inorder.size()));
// postorder 舍弃末尾元素
postorder.remove(postorder.size() - 1);
// 切割后序数组
List<Integer> leftPostorder = new ArrayList<>(postorder.subList(0, leftInorder.size()));
List<Integer> rightPostorder = new ArrayList<>(postorder.subList(leftInorder.size(), postorder.size()));
root.left = traversal(leftInorder, leftPostorder);
root.right = traversal(rightInorder, rightPostorder);
return root;
}
public TreeNode buildTree(List<Integer> inorder, List<Integer> postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return null;
return traversal(inorder, postorder);
}
}
3.复杂度分析
- 时间复杂度:尽管递归深度会影响栈的空间复杂度,但从时间复杂度的角度看,每个节点都会导致一次遍历操作和一次分割操作,总的时间复杂度与树中节点数量成正比,即O(n)。这里的n代表树中节点的总数。
- 空间复杂度:该算法的时间复杂度为O(n),空间复杂度在最坏情况下也是O(n),主要是由于递归调用栈的深度可能达到O(n)。在实际应用中,若二叉树较为平衡,空间复杂度可以视为O(log n)