257. 二叉树的所有路径
力扣题目链接
给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
解决思路
- 使用回溯算法。
- 由于在回溯的层面上用的是同一个字符串,本来是需要做回溯的,现在是重新定义了一个字符串sb
- 当节点为空,过滤掉。如果节点的左右节点都是空的,添加到结果集中。
Java实现
class Solution_LC257 {
List<String> res = new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
backtrack(root, "");
return res;
}
private void backtrack(TreeNode root, String path) {
if (root == null) {
return;
}
StringBuilder sb = new StringBuilder(path);
sb.append(root.val);
if (root.left == null && root.right == null) {
res.add(sb.toString());
return;
}
sb.append("->");
backtrack(root.left, sb.toString());
backtrack(root.right, sb.toString());
}
}
404.左叶子之和
力扣题目链接
给定二叉树的根节点 root
,返回所有左叶子之和。
输入: root = [3,9,20,null,null,15,7]
输出: 24
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
解决思路
- 增加了判断是否是叶子节点的方法。
- 如果是叶子节点,就是结果集累加的时候。
Java实现
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) {
return 0;
}
int ans = 0;
if (root.left != null) {
if (isLeaf(root.left)) {
ans += root.left.val;
} else {
ans += sumOfLeftLeaves(root.left);
}
}
if (root.right != null) {
ans += sumOfLeftLeaves(root.right);
}
return ans;
}
private boolean isLeaf(TreeNode node) {
return node.left == null && node.right == null;
}
}
513.找树左下角的值
力扣题目链接
给定一个二叉树的 根节点 root
,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
输入: root = [2,1,3]
输出: 1
解决思路
- 层序遍历,很简单
- 递归遍历。需要记录递归的深度。当找到更深的一层,更新左节点的值。由于左节点会优先更新,所以curVal会代表当前层最左边节点的数据。
Java实现
层序遍历
class Solution_LC513 {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList();
List<Integer> list = new ArrayList();
queue.add(root);
int res = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (i == 0) {
res = node.val;
}
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}
return res;
}
}
递归法
class Solution {
int curVal = 0;
int curHeight = 0;
public int findBottomLeftValue(TreeNode root) {
int curHeight = 0;
dfs(root, 0);
return curVal;
}
public void dfs(TreeNode root, int height) {
if (root == null) {
return;
}
height++;
dfs(root.left, height);
dfs(root.right, height);
if (height > curHeight) {
curHeight = height;
curVal = root.val;
}
}
}
112. 路径总和
力扣题目链接
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
叶子节点 是指没有子节点的节点。
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
解决思路
- 不断更新目标合,直到找到叶子节点的值等于目标和
Java实现
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) {
return false;
}
if (root.left == null && root.right == null && root.val == targetSum) {
return true;
}
int newSum = targetSum - root.val;
return hasPathSum(root.left, newSum) || hasPathSum(root.right, newSum);
}
}
113. 路径总和ii
力扣题目链接
给你二叉树的根节点 root
和一个整数目标和 targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
解决思路
- 回溯算法
- 在回溯下一级添加当前元素的值,退出后要把最后的元素给删除。
Java实现
class Solution_LC113 {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
if (root == null) return res; // 非空判断
backtrack(root, targetSum);
return res;
}
private void backtrack(TreeNode root, int targetSum) {
path.add(root.val);
if (root.left == null && root.right == null && root.val == targetSum) {
res.add(new ArrayList<>(path));
}
int newSum = targetSum - root.val;
if (root.left != null) {
backtrack(root.left, newSum);
path.removeLast();
}
if (root.right != null) {
backtrack(root.right, newSum);
path.removeLast();
}
}
}
105. 从前序与中序遍历序列构造二叉树
leetcode题目链接
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
实现思路
第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取前序数组第一个元素作为节点元素。
第三步:找到前序数组第一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:切割后序数组,切成前序左数组和前序右数组(中序数组大小一定是和前序数组的大小相同的)
第六步:递归处理左区间和右区间
Java实现
class Solution_LC105 {
Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return buildTree(preorder, 0, preorder.length, inorder, 0, inorder.length);
}
private TreeNode buildTree(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight) {
//临界条件
if (preLeft >= preRight || inLeft >= inRight) {
return null;
}
//前序节点的首节点是根节点,根左右
TreeNode treeNode = new TreeNode(preorder[preLeft]);
//获取其在中序遍历序列中的位置
Integer rootIndex = map.get(preorder[preLeft]);
//中序序列切割左子树序列和右子树序列,左中右。
int leftLen = rootIndex - inLeft;
treeNode.left = buildTree(preorder, preLeft + 1, preLeft + 1 + leftLen, inorder, inLeft, rootIndex);
treeNode.right = buildTree(preorder, preLeft + 1 + leftLen, preRight, inorder, rootIndex + 1, inRight);
return treeNode;
}
}
106. 从中序与后序遍历序列构造二叉树
leetcode题目链接
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
解决思路
- 同上。
Java实现
class Solution_LC106 {
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 rootIndex = map.get(postorder[postEnd - 1]); // 找到后序遍历的最后一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[rootIndex]); // 构造结点
int lenOfLeft = rootIndex - inBegin; // 保存中序左子树个数,用来确定后序数列的个数
root.left = findNode(inorder, inBegin, rootIndex,
postorder, postBegin, postBegin + lenOfLeft);
root.right = findNode(inorder, rootIndex + 1, inEnd,
postorder, postBegin + lenOfLeft, postEnd - 1);
return root;
}
}
654.最大二叉树
力扣题目地址
给定一个不重复的整数数组 nums
。 最大二叉树 可以用下面的算法从 nums
递归地构建:
- 创建一个根节点,其值为
nums
中的最大值。 - 递归地在最大值 左边 的 子数组前缀上 构建左子树。
- 递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums
构建的 最大二叉树 。
输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:
- [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
- [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
- 空数组,无子节点。
- [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
- 空数组,无子节点。
- 只有一个元素,所以子节点是一个值为 1 的节点。
- [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
- 只有一个元素,所以子节点是一个值为 0 的节点。
- 空数组,无子节点。
解决思路
- 终止条件,当nums的节点数为0,返回空节点;当nums节点为数1,返回单节点。
- 获取指定区间的最大值,继续分割数组。
- 数组区间是[left,right),不包含right的元素。
Java实现
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return buildTree(nums, 0, nums.length);
}
private TreeNode buildTree(int[] nums, int left, int right) {
if (right - left < 1) {
return null;
}
if (right - left == 1) {
return new TreeNode(nums[left]);
}
int max = nums[left];
int maxIndex = left;
for (int i = left + 1; i < right; i++) {
if (nums[i] > max) {
maxIndex = i;
max = nums[i];
}
}
TreeNode node = new TreeNode(max);
node.left = buildTree(nums, left, maxIndex);
node.right = buildTree(nums, maxIndex + 1, right);
return node;
}
}