leetcode 104. 二叉树的最大深度
这道题代码很简单,但是思考方式跟以前不一样,不过还是同一个划分的方法,将树分为左右子树来调用
3 3 3 3
/ \ / \ / \
9 20 9 20
对于node(3),最大深度自然是左右子结点+1,左右子节点有的可能为空,只要有一个,
树的最大高度就是1+1=2。然后再增加几个结点:
很显然相对于node(20),最大深度自然是左右子结点+1,左右子节点有的可能为空,只要有一个,树的最大高度就是1+1=2,用代码表示就是:int depth = 1 + max(leftDepth, rightDepth);
而对于3,则是左右子树深度最大的那个然后再+1,具体谁更大,则不必关心。所以对于node(3)的判断逻辑就是:
int leftDepth = getDepth(node.left); // 左
int rightDepth = getDepth(node.right); // 右
int depth = 1 + max(leftDepth, rightDepth); // 中
那什么时候结束呢,这里仍然是root == null返回0就行了。至于入参,自然是要处理的子树的根节点root,而返回值则是当前root所在子树的最大高度。所以合在一起就是:
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
leetcode 110. 平衡二叉树
这道题跟上一题的差别就是多一个判断,如何判断return回去的高度做比较,如果为0,OK,为-1,高度不对
class Solution {
public boolean isBalanced(TreeNode root) {
return height(root) >= 0;
}
public int height(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = height(root.left);
int rightHeight = height(root.right);
if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
return -1;
} else {
return Math.max(leftHeight, rightHeight) + 1;
}
}
}
判断左右子树的高度差是否大于1,如果大于1则返回-1表示不平衡,否则返回左右子树中较大的高度加1。
最终,如果根节点的高度为-1,则整个二叉树不平衡;否则,整个二叉树平衡。
优化:
不过这段代码看起来并不好理解,可以把if拿出来,不过这段代码稍微好理解一点,但是没有上面那一段好
class Solution {
public boolean isBalanced(TreeNode root) {
return height(root) != -1;
}
public int height(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = height(root.left);
if (leftHeight == -1) {
return -1;
}
int rightHeight = height(root.right);
if (rightHeight == -1) {
return -1;
}
if (Math.abs(leftHeight - rightHeight) > 1) {
return -1;
}
return Math.max(leftHeight, rightHeight) + 1;
}
}
然后我使用gpt帮忙优化一下,但是时间会比较长,应该是数据达到一定的量的时候,会更好,下面直接贴出代码,主要是使用一个缓存Map来保存已经计算过的节点高度,避免重复计算
class Solution {
private Map<TreeNode, Integer> heightCache = new HashMap<>();
public boolean isBalanced(TreeNode root) {
return height(root) >= 0;
}
public int height(TreeNode root) {
if (root == null) {
return 0;
}
// 检查缓存中是否已经计算过该节点的高度
if (heightCache.containsKey(root)) {
return heightCache.get(root);
}
int leftHeight = height(root.left);
int rightHeight = height(root.right);
// 提前终止判断
if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
return -1;
} else {
int maxHeight = Math.max(leftHeight, rightHeight) + 1;
// 将计算结果加入缓存
heightCache.put(root, maxHeight);
return maxHeight;
}
}
}
不过在leetcode执行会发现最开始那个更快,因为第二段代码可能由于缓存操作而导致运行时间稍长,因为缓存操作需要额外的内存和时间开销。只有在数据量比较大时,重复计算的次数才会明显增加,进而体现出第二段代码的优势。
所以在一棵二叉树中节点数量大约在10^4级别时,第二段代码相对第一段代码的性能优势可能开始显现。这个是一个大概的估计,实际效果还与具体的二叉树结构以及其他因素有关。
leetcode 111. 二叉树的最小深度
这里的关键问题是题目中说的:最小深度是从根节点到最近叶子节点的最短路径上的节点数量,也就是最小深度的一层必须要有叶子结点,因此不能直接用。
这里的核心问题仍然是分析终止条件:
● 如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
● 反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。
最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
class Solution {
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
int min_depth = Integer.MAX_VALUE;
if(root.left != null){
min_depth = Math.min(minDepth(root.left), min_depth);
}
if(root.right != null){
min_depth = Math.min(minDepth(root.right), min_depth);
}
return min_depth + 1;
}
}
leetcode 559. N 叉树的最大深度
这个题的实现和上一个题的最大区别就是处理子树时加了个处理List的for循环
class Solution {
public int maxDepth(Node root) {
if(root == null){
return 0;
}
if(root.children.isEmpty()){
return 1;
}
List<Integer> heigths = new LinkedList<>();
for(Node item : root.children){
heigths.add(maxDepth(item));
}
return Collections.max(heigths)+1;
}
}
这样就OK了,不过可以进一步优化代码,也是使用一个辅助函数,在递归过程中将已经计算过的节点深度保存起来
class Solution {
private Map<Node, Integer> depthCache = new HashMap<>(); // 创建一个缓存,用于保存已经计算过的节点深度
public int maxDepth(Node root) {
if (root == null) {
return 0; // 如果根节点为空,说明是空树,深度为0
}
if (depthCache.containsKey(root)) {
return depthCache.get(root); // 如果缓存中已经存在当前节点的深度,则直接返回缓存中的结果
}
int maxChildDepth = 0; // 初始化最大子节点深度为0
for (Node child : root.children) { // 遍历所有子节点
int childDepth = maxDepth(child); // 递归计算每个子节点的深度
maxChildDepth = Math.max(maxChildDepth, childDepth); // 取所有子节点深度的最大值
}
int depth = maxChildDepth + 1; // 当前节点的深度等于最大子节点的深度加1
depthCache.put(root, depth); // 将当前节点的深度加入缓存
return depth; // 返回当前节点的深度
}
}
这段代码的实现思路如下: