110.平衡二叉树
题目链接:力扣
思路
这一道题目算是求数的最大高度的升级版,求树的最大高度采用的是后序遍历
先左记录、再右记录、再中处理
那么判断一棵树是不是平衡二叉树我们不仅需要记录它的最大高度,还要判断子树是不是一棵平衡二叉树
如果不是平衡二叉树,它的最大高度就没有记录的必要了,但是需要对此做出标记
平衡二叉树
递归法
求树的高度是使用后序遍历
这道题目不太好写的部分是递归函数“中”的部分不好写
因为以前在对节点遍历之后,都是处理一件事就可以了,但是这次不仅要记录这个节点的最大高度返回给父节点,还要判断自己是不是一个平衡二叉树
所以在处理节点的过程中,如果这个节点树是二平衡二叉树,我们就记录这棵树的最大高度,返回给上一层,如果不是平衡二叉树,那就给最大高度赋值-1,返回给上一层
class Solution {
public boolean isBalanced(TreeNode root) {
return getHight(root) == -1 ? false : true;
}
public int getHight(TreeNode root) {
if (root == null) {
return 0;
}
// 左
// 记录树的最大高度
int leftHight = getHight(root.left);
// 判断树是否是平衡二叉树
if (leftHight == -1) {
return -1;
}
// 右
// 记录数的最大高度
int rightHight = getHight(root.right);
if (rightHight == -1) {
return -1;
}
// 中
int result;
// 判断该树是否是平衡二叉树
if (Math.abs(rightHight - leftHight) > 1) {
result = -1;
} else {
result = 1 + Math.max(rightHight,leftHight);
}
return result;
}
}
迭代法
这道题目使用迭代法太复杂了,二刷再看
257.二叉树的所有路径
题目链接:力扣
思路
题目的要求是记录二叉树中的每一条路径,这种情况下我们就要使用前序遍历对二叉树进行处理了,中 左 右 的顺序,这样才方便父节点指向孩子节点,找到对应的路径
但是每记录一条路径之后,就遍历到了最下面的叶子节点,怎么才能开始从头走第二条道路呢,这就涉及到了回溯的思想,简单来说,就是回退
再许多情况下,回溯算法相当于穷举搜索的巧妙实现
类似的题目:112 113
二叉树的所有路径
递归法
第一步:递归函数的函数参数以及返回值
我们需要传入根节点,要查看的二叉树;
需要记录每一条路径的path
需要存放的结果集result
第二步:确认递归的终止条件
这道题的终止条件是找到叶子节点才算终止
第三步:单层结构的处理
回溯与递归是一一对应的,有一个递归就要有一个回溯,所以回溯要和递归永远在一起,写在一个花括号之内
暂时对回溯算法有了初步了解,回溯算法比较难理解的就是递归中的每次回溯,其实调用的递归函数还有自己的回溯,进去是啥,出了递归只是比进去的多了一个节点,所以除掉最后一个就可以了。剩下的在递归里面已经去除干净了
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
// 用于保存最后的所有路径
List<String> result = new ArrayList<>();
if (root == null) {
return result;
}
// 用于保存每条路径上的节点
List<Integer> nodes = new ArrayList<>();
// 调用收集函数
traversal(root,nodes,result);
return result;
}
// 递归函数
public void traversal(TreeNode root,List<Integer> nodes,List<String> result) {
// 中
// 如果这个节点是叶子节点,要确保这个节点可以添加到路径中,这是根据终止条件来的
nodes.add(root.val);
// 终止条件
if (root.left == null && root.right == null) {
// 说明你这条路径走到头了 nodes也将这条路径上的所有元素都添加上去了
// 这条路径已经成了,那就将这条路径转换成符合要求的格式
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nodes.size () - 1; i++) { // 长度-1是因为最后一个节点不添加->
sb.append(nodes.get(i) + "->");
}
sb.append(nodes.get(nodes.size () - 1));
// 将这条路径转换成字符串之后,再添加到结果集中
result.add(sb.toString());
return;
}
if (root.left != null) {
traversal(root.left,nodes,result); // 这条语句执行完成之后,说这条路径从这个左节点开始已经记录完成了
nodes.remove(nodes.size() - 1); // 回溯
}
if (root.right != null) {
traversal(root.right,nodes,result);
nodes.remove(nodes.size() - 1); // 回溯
}
}
}
迭代法
暂时搁置这种方法的学习,后面再学
404.左叶子之和
题目链接:力扣
思路
这道题是你找到左叶子,左叶子无法在自身节点上面判断自己是不是左叶子,必须借助节点的父节点来判断其左孩子是不是左叶子
左叶子是什么:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点
这道题的关键就是要知道怎么筛选出左叶子节点,并对其进行处理
if (node.left != null && node.left.left == null && node.left.right == null) {
这个才是左叶子
}
左叶子之和
递归法
因为我们是要统计根节点的左右子树的左叶子数之和,所以采用后序遍历,收集左右的左叶子数之和,然后再对两边的和进行处理
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 0;
}
// 左
int leftNum = sumOfLeftLeaves(root.left);
// 右
int rightNum = sumOfLeftLeaves(root.right);
// 中处理
int midvalue = 0;
if (root.left != null && root.left.left == null && root.left.right == null) {
midvalue = root.left.val;
}
int sum = midvalue +leftNum + rightNum;
return sum;
}
}
迭代法
迭代法是比较好理解的,但需要注意的是,就是正常的层序遍历,只是在层序遍历的时候,对符合左叶子的节点进行收割
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
// 采用层序遍历
Deque<TreeNode> deque = new LinkedList<>();
// 先将根节点入队
deque.offer(root);
int sum = 0;
while (!deque.isEmpty()) {
int len = deque.size();
for (int i = 0; i < len; i++) {
TreeNode node = deque.poll();
// 添加左子节点
if (node.left != null) {
deque.offer(node.left);
}
// 添加右子节点
if (node.right != null) {
deque.offer(node.right);
}
// 整个程序中,只有出现左叶子的时候,对左叶子的结果进行收割
if (node.left != null && node.left.left == null && node.left.right == null) {
sum = sum + node.left.val;
}
}
}
return sum;
}
}