力扣题目链接
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例3:
输入:root = []
输出:true
因为是要求高度,所以用后序遍历
递归三要素
public int getHeight(TreeNode root)
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
if (root == null) {
return 0;
}
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉平衡树了。
如果是-1,肯定是不平衡的了,就可以一直往上返回,不需再计算该树的其他子树的高度
// 计算左右子树的高度
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
// 如果左右子树返回的是-1的话就可以一层层返回上去
if (leftHeight == -1) {
return -1;
}
if (rightHeight == -1) {
return -1;
}
// 判断二者高度差
// 如果不平衡
if (Math.abs(leftHeight - rightHeight) > 1) {
return -1;
}
// 如果平衡返回高度
return Math.max(leftHeight, rightHeight) + 1;
完整代码:
// 递归--------------------------------------------------------------
public boolean isBalanced(TreeNode root) {
return getHeight(root) != -1;
}
public int getHeight(TreeNode root) {
if (root == null) {
return 0;
}
// 计算左右子树的高度
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
// 如果左右子树返回的是-1的话就可以一层层返回上去
if (leftHeight == -1) {
return -1;
}
if (rightHeight == -1) {
return -1;
}
// 判断二者高度差
// 如果不平衡
if (Math.abs(leftHeight - rightHeight) > 1) {
return -1;
}
// 如果平衡返回高度
return Math.max(leftHeight, rightHeight) + 1;
}
// 递归--------------------------------------------------------------
力扣题目链接
给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例1:
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
示例2:
输入:root = [1]
输出:["1"]
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
递归三要素:
public void traversal(TreeNode root, List<String> res, List<Integer> path)
->
存入res返回为什么没有判断root是否为空呢?因为下面的逻辑可以控制空节点不入循环。
if (root.left == null && root.right == null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.size() - 1; i++) { // 只到倒数第二个数,因为最后一个数不带箭头
// path的每个点存入StringBuilder,每个节点后面加箭头
sb.append(path.get(i)).append("->");
}
sb.append(path.get(path.size() - 1)); // 最后一个数
res.add(sb.toString());
return;
}
path.add(root.val);
然后是递归和回溯的过程,上面说过没有判断cur是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。所以递归前要加上判断语句,下面要递归的节点是否为空
而且递归完,要做回溯啊,因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。
if (root.left != null) {
traversal(root.left, res, path);
path.remove(path.size() - 1); // 回溯
}
if (root.right != null) {
traversal(root.right, res, path);
path.remove(path.size() - 1); // 回溯
}
注:每一次递归完,都要回溯一次
for (int i = 0; i < path.size() - 1; i++) { // 只到倒数第二个数,因为最后一个数不带箭头
// path的每个点存入StringBuilder,每个节点后面加箭头
sb.append(path.get(i)).append("->");
}
sb.append(path.get(path.size() - 1)); // 最后一个数
在将数存入path时,最后一个数字不用加箭头,因此对最后一个数单独加入,其他数和箭头一起加进path
完整代码
public List<String> binaryTreePaths(TreeNode root) {
// 存储最后结果
List<String> res = new ArrayList<>();
// 存储每条边
List<Integer> path = new ArrayList<>();
if (root == null) {
return res;
}
traversal(root, res, path);
return res;
}
public void traversal(TreeNode root, List<String> res, List<Integer> path) {
// 中,中为什么写在这里,因为最后一个节点也要加入到path中
// 每遍历一个节点都要加入path
path.add(root.val);
// 叶子节点的左右子树肯定是空,那么就将现在的路径放回res
if (root.left == null && root.right == null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.size() - 1; i++) { // 只到倒数第二个数,因为最后一个数不带箭头
// path的每个点存入StringBuilder,每个节点后面加箭头
sb.append(path.get(i)).append("->");
}
sb.append(path.get(path.size() - 1)); // 最后一个数
res.add(sb.toString());
return;
}
if (root.left != null) {
traversal(root.left, res, path);
path.remove(path.size() - 1); // 回溯
}
if (root.right != null) {
traversal(root.right, res, path);
path.remove(path.size() - 1); // 回溯
}
}
可以直接定义一个成员变量为object的栈Stack
存放节点和路径
stack.push(root.val + "")
可以直接将int类型转化为String
完整代码:
// 迭代--------------------------------------------------------------
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
if (root == null) {
return res;
}
Stack<Object> stack = new Stack<>();
// 节点和路径同时入栈
stack.push(root);
stack.push(root.val + "");
while (!stack.isEmpty()) {
// 节点和路径同时出栈
String path = (String) stack.pop();
TreeNode node = (TreeNode) stack.pop();
// 若找到叶子节点
if (node.left == null && node.right == null) {
res.add(path);
}
//右子节点不为空
if (node.right != null) {
stack.push(node.right);
stack.push(path + "->" + node.right.val);
}
//左子节点不为空
if (node.left != null) {
stack.push(node.left);
stack.push(path + "->" + node.left.val);
}
}
return res;
}
力扣题目链接
给定二叉树的根节点 root
,返回所有左叶子之和。
示例 1:
输入: root = [3,9,20,null,null,15,7]
输出: 24
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
示例 2:
输入: root = [1]
输出: 0
左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点
不能直接以叶子节点的左右为空为左节点,因为此时不能判断是左还是右孩子,而是要在他的父节点先定下他的左叶子,再进行判断
完整代码:
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) {
return 0;
}
// 左
int leftValue = sumOfLeftLeaves(root.left);
if (root.left != null && root.left.left == null && root.left.right == null) {
leftValue = root.left.val;
}
// 右
int rightValue = sumOfLeftLeaves(root.right);
// 中
return leftValue + rightValue;
}
前序遍历的迭代,只要把左叶子节点统计出来,就可以了
// 迭代--------------------------------------------------------------
public int sumOfLeftLeaves1(TreeNode root) {
if (root == null) {
return 0;
}
Deque<TreeNode> stack = new ArrayDeque<>();
stack.offer(root);
int sum = 0;
while (!stack.isEmpty()) {
TreeNode node = stack.poll();
// 判断是叶子节点
if (node.left != null && node.left.left == null && node.left.right ==null) {
sum = sum + node.left.val;
}
if (node.left != null) {
stack.offer(node.left);
}
if (node.right != null) {
stack.offer(node.right);
}
}
return sum;
}