力扣100题解及笔记 二叉树

 目录

1.力扣100题解及笔记 哈希-CSDN博客

2.力扣100题解及笔记 堆-CSDN博客

3.力扣100题解及笔记 栈-CSDN博客

4.力扣100题解及笔记 双指针-CSDN博客

5.力扣100题解及笔记 链表-CSDN博客

6.力扣100题解及笔记 二叉树-CSDN博客

7.力扣100题解及笔记 二分查找-CSDN博客

8.力扣100及题解 滑动窗口&子串-CSDN博客

9.力扣100题解及笔记 回溯-CSDN博客

10.力扣100题解及笔记 dp&多维dp-CSDN博客

11.力扣100题解及笔记 贪心-CSDN博客

12.力扣100题解及笔记 数组-CSDN博客

13.力扣100题解及笔记 技巧-CSDN博客

14.力扣100题解及笔记 矩阵-CSDN博客

15.力扣100题解及笔记 图论-CSDN博客

理论基础

二叉树

种类

普通二叉树:每个节点最多有两个子节点(左子节点和右子节点)。

满二叉树:所有叶子节点都在同一层,每个非叶子节点都有两个子节点。

完全二叉树:除最后一层外,其他每层都是满的,最后一层的节点从左到右排列。

二叉搜索树:对于每个节点,左子树的所有节点值小于该节点,右子树的所有节点值大于该节点。

遍历

前序:根 -> 左子树 -> 右子树

中序:左子树 -> 根 -> 右子树

后序:左子树 -> 右子树 -> 根

层序:按层逐层遍历,从上到下,从左到右。

定义

递归定义:树是一个根节点及其左、右子树。

非递归定义:树由节点和边组成,每个节点有左右子节点,边连接父子节点

遍历法

DFS

DFS递归
1.确定递归函数的参数和返回值
2.确定终止条件(防止栈溢出)
3.确定单层递归的逻辑

BFS

BFS使用队列
把每个还没有搜索到的点依次放入队列
然后再弹出队列的头部元素当做当前遍历点

1.不需要确定当前遍历到了哪一层
while queue 不空:
    cur = queue.pop()
    for 节点 in cur的所有相邻节点:
        if 该节点有效且未访问过:
            queue.push(该节点)

2.需要确定当前遍历到了哪一层
增加level:遍历到二叉树中的哪一层
增加size:队列中的元素数/当前层的所有元素

level = 0
while queue 不空:
    size = queue.size()
    while (size --) {
    //循环直到当前层的所有节点都处理完毕
        cur = queue.pop()
        for 节点 in cur的所有相邻节点:
            if 该节点有效且未被访问过:
                queue.push(该节点)
    }
    level ++;

BST

每个节点的左子树所有节点值都小于该节点,右子树所有节点值都大于该节点
且左、右子树也分别为二叉搜索树

特点:
中序遍历下,输出的二叉搜索树节点的数值是有序序列

常见题目

基础

  • 是否对称

    • 递归:后序,比较根节点的左子树和右子树是否互为镜像。
    • 迭代:使用队列或栈同时存储两个节点,逐步比较。
  • 求最大深度

    • 递归:后序遍历,通过递归计算左右子树的最大高度。
    • 迭代:层序遍历,记录遍历的层数。
  • 求最小深度

    • 递归:后序遍历,计算左右子树的最小高度(注意区分叶子节点)。
    • 迭代:层序遍历,第一个遇到叶子节点时返回。
  • 求节点数量

    • 递归:后序遍历,通过递归函数的返回值计算节点数量。
    • 迭代:层序遍历,逐个统计节点数量。
  • 是否平衡

    • 递归:后序遍历,递归过程中计算左右子树高度差是否满足平衡条件。
    • 迭代:不推荐,效率较低。
  • 找所有路径

    • 递归:前序遍历,结合回溯记录从根到叶子的所有路径。
    • 迭代:使用栈模拟递归,同时存储路径信息。
  • 求左叶子之和

    • 递归:后序遍历,三层约束条件判断是否是左叶子。
    • 迭代:模拟后序遍历,判断左叶子节点。
  • 求左下角的值

    • 递归:优先搜索左孩子,找到深度最大的叶子节点。
    • 迭代:层序遍历,最后一行最左边的节点即为结果。
  • 求路径总和

    • 递归:遍历整棵树,检查路径总和是否等于目标值。
    • 迭代:栈中保存节点指针及对应路径的累加和。 

修改与构造

  • 翻转二叉树

    • 递归:前序遍历,交换左右子节点。
    • 迭代:使用栈模拟前序遍历交换子节点。
  • 构造二叉树

    • 递归:前序遍历,找分割点构建左右子树。
    • 迭代:复杂,不推荐。
  • 构造最大的二叉树

    • 递归:前序遍历,使用数组最大值分割左右子树。
    • 迭代:较复杂,不推荐。
  • 合并两个二叉树

    • 递归:前序遍历,同时操作两个树的节点。
    • 迭代:使用队列进行合并,类似层序遍历。

属性

  • 搜索节点

    • 递归:根据节点值有方向地递归查找。
    • 迭代:同样根据有序性,逐步向下查找。
  • 是否为二叉搜索树

    • 递归:中序遍历,检查节点值是否递增。
    • 迭代:模拟中序遍历,同样判断序列。
  • 求最小绝对差

    • 递归:中序遍历,双指针比较节点间差值。
    • 迭代:模拟中序遍历,逐步比较。
  • 求众数

    • 递归:中序遍历,统计出现频率,找到众数。
    • 迭代:模拟中序遍历,统计节点值出现次数。
  • 转换为累加树

    • 递归:中序遍历,通过累加计算。
    • 迭代:同样模拟中序遍历,进行累加。

公共祖先问题

  • 普通二叉树的公共祖先

    • 递归:后序遍历,回溯找到左、右子树出现目标节点的地方。
    • 迭代:不推荐模拟回溯,较复杂。
  • 二叉搜索树的公共祖先

    • 递归:通过节点值找到目标区间内的祖先节点。
    • 迭代:按顺序遍历二叉搜索树。

BST修改与构造

  • 插入节点

    • 递归:顺序无所谓,递归找到合适位置插入节点。
    • 迭代:按顺序遍历,记录插入父节点。
  • 删除节点

    • 递归:前序遍历,处理删除非叶子节点的复杂情况。
    • 迭代:较复杂,按顺序遍历处理。
  • 修剪二叉搜索树

    • 递归:前序遍历,根据返回值修剪节点。
    • 迭代:按顺序遍历修剪,较复杂。
  • 构造二叉搜索树

    • 递归:前序遍历,使用数组中间节点构建树。
    • 迭代:通过队列模拟构造,较复杂。

94.中序

94. 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
 

提示:

树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100

延伸:前中后遍历 递归法

// 前序遍历LC144_二叉树的前序遍历
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {
        if (root == null) return;
        result.add(root.val);
        preorder(root.left, result);
        preorder(root.right, result);
    }
}
// 中序遍历LC94_二叉树的中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode root, List<Integer> list) {
        if (root == null) return;
        inorder(root.left, list);
        list.add(root.val);         
        inorder(root.right, list);
    }
}
// 后序遍历LC145_二叉树的后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorder(root, res);
        return res;
    }

    void postorder(TreeNode root, List<Integer> list) {
        if (root == null) return;
        postorder(root.left, list);
        postorder(root.right, list);
        list.add(root.val);         
    }
}

104.最大深度

104. 二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。


提示:

树中节点的数量在 [0, 104] 区间内。
-100 <= Node.val <= 100

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数

前序求深度,后序求高度(为什么想一下遍历顺序就明白了)

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;
        //先求左子树的深度,再求右子树的深度,最后取左右深度最大的数值再+1
    }
}

延伸:最小深度

111. 二叉树的最小深度

最小深度是从根节点到最近叶子节点的最短路径上的节点数量,注意是叶子节点

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null)   return 0;
        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);
        if (root.left == null) return rightDepth + 1;
        if (root.right == null)  return leftDepth + 1;
        //子树空,自己画张图就理解了,要叶子结点
        return Math.min(leftDepth, rightDepth) + 1;
        //左右子树都不为空,则返回左右子树深度的最小值再加上当前节点的深度
    }
}

226.翻转

226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
 

提示:

树中节点数目范围在 [0, 100] 内
-100 <= Node.val <= 100

递归交换左右孩子的指针/递归地翻转左右子树

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        invertTree(root.left);
        invertTree(root.right);
        swap(root);
        //前序后序都可以
        return root;
    }
    private void swap(TreeNode root){
        TreeNode temp=root.left;
        root.left=root.right;
        root.right=temp;
        //交换指针
    }
}

101.对称

101. 对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

 

提示:

树中节点数目在范围 [1, 1000] 内
-100 <= Node.val <= 100
 

进阶:你可以运用递归和迭代两种方法解决这个问题吗?

比较子树内外侧是否相等

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left,root.right);
        //直接返回 compare 的布尔值结果
    }

    private boolean compare(TreeNode left,TreeNode right){
        if(left==null&&right!=null) return false;
        if(left!=null&&right==null) return false;
        if(left==null&&right==null) return true;
        if(left.val!=right.val) return false;
        //不要再打一个=了!
        boolean out=compare(left.left,right.right);
        boolean in=compare(left.right,right.left);
        return out&&in;
    }
}

543.直径

543. 二叉树的直径

给你一棵二叉树的根节点,返回该树的 直径 。

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。

两节点之间路径的 长度 由它们之间边数表示。


提示:

树中节点数目在范围 [1, 104] 内
-100 <= Node.val <= 100

直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root

最长直径就是左右子树的最大深度之和,递归遍历每个节点时更新全局最大值

class Solution {
    int ans = 0;//全局变量记录最大值
    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);
        return ans;
    }
    int dfs(TreeNode u) {
        if (u == null) return 0;
        int l = dfs(u.left), r = dfs(u.right);//左右子树的深度
        ans = Math.max(ans, l + r);//更新最大值
        return Math.max(l, r) + 1;//父节点需要返回值(子树的深度)计算以它为根的最大深度,并继续向上返回
    }
}

可以看作,最大深度+更新一个全局变量

102.层序

102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

 

提示:

树中节点数目在范围 [0, 2000] 内
-1000 <= Node.val <= 100

回顾一下BFS

把每个还没有搜索到的点依次放入队列
然后再弹出队列的头部元素当做当前遍历点
level:遍历到二叉树中的哪一层
size:队列中的元素数/当前层的所有元素

level = 0
while queue 不空:
    size = queue.size()
    while (size -- >0) {
    //循环直到当前层的所有节点都处理完毕
        cur = queue.pop()
        for 节点 in cur的所有相邻节点:
            if 该节点有效且未被访问过:
                queue.push(该节点)
    }
    level ++;

注意二维列表和当前层级的列表

class Solution {
    public List<List<Integer>> res = new ArrayList<List<Integer>>();
    //res 记录整个树的所有层次的节点值
    public List<List<Integer>> levelOrder(TreeNode root) {
    Fun(root);
    return res;
    }

    public void Fun(TreeNode node) {
        if (node == null) return;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(node);//将根节点入队
        while (!que.isEmpty()){
            List<Integer> itemList = new ArrayList<Integer>();
            //itemList临时列表,每次处理完一层节点后存储该层的节点值
            int len = que.size();
            while (len-- > 0) {
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);
                //记录单层节点
                if (tmpNode.left != null) que.offer(tmpNode.left);
                if (tmpNode.right != null) que.offer(tmpNode.right);
            }
            res.add(itemList);//当前层级的节点值列表添加到结果列表           
        }
    }
}

108.有序数组转换BST

108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 
平衡

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 严格递增 顺序排列

根据数组构造一棵二叉树就是寻找分割点

有序数组构造二叉搜索树,分割点就是数组中间位置的节点(奇偶不影响,答案不唯一)

以升序序列中的任一个元素作为根节点,以该元素左边的升序序列构建左子树,以该元素右边的升序序列构建右子树

public class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return buildBST(nums, 0, nums.length - 1);
    }
 
    private TreeNode buildBST(int[] nums, int start, int end) {
        if (start > end) return null; // 超过数组范围
        int mid = start + (end - start) / 2;
        TreeNode root = new TreeNode(nums[mid]);// 选择中间位置的元素作为根节点
        root.left = buildBST(nums, start, mid - 1);//左
        root.right = buildBST(nums, mid + 1, end);//右
        return root;
    }
}

相关延伸:

*数组换成链表

109. 有序链表转换二叉搜索树

链表分割点,用快慢指针找 876. 链表的中间结点

*数组构造普通二叉树

654. 最大二叉树

*递归函数返回值

701. 二叉搜索树中的插入操作

450. 删除二叉搜索树中的节点

*循环不变量

35. 搜索插入位置 - 力扣(LeetCode)

59. 螺旋矩阵 II - 力扣(LeetCode)

98.验证BST

98. 验证二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 
平衡
 

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 严格递增 顺序排列

//中序遍历是有序序列,验证BST=判断序列是否递增
class Solution {
    long pre = Long.MIN_VALUE;//注意后台测试数据
    //保证第一次比较时任何正常的节点值都会大于它
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        if (!isValidBST(root.left)) return false;  // 左
        if (root.val <= pre) return false;// 中,序列递增?
        pre = root.val;//更新pre,为什么写找个叶子自己模拟一下
        return isValidBST(root.right); // 右
    }
}

230.BST中第K小

230. 二叉搜索树中第 K 小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。
提示:

树中的节点数为 n 。
1 <= k <= n <= 104
0 <= Node.val <= 104
 

进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?

转化为求中序遍历的第 k 个节点

public class Solution {
    int n=0,res=0; // 计数器
    public int kthSmallest(TreeNode root, int k) {
        inorder(root, k);
        return res;
    }
    private void inorder(TreeNode node, int k) {
        if (node == null) return; 
        inorder(node.left, k);
        n++;
        if (n== k) {
            res= node.val; //保存其值并结束遍历
            return;
        }
        inorder(node.right, k);
    }
}

199.右视图

199. 二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。


提示:

二叉树的节点个数的范围是 [0,100]
-100 <= Node.val <= 100 

BFS :记录下每层的最后一个元素

DFS: 根结点 -> 右子树 -> 左子树

//BFS
class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null)  return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- > 0) {
                TreeNode node = queue.poll();
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
                if (size == 1) res.add(node.val); 
            }
        }
        return res;
    }
}

//DFS
class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> rightSideView(TreeNode root) {
        dfs(root, 0); // 从根节点开始访问,根节点深度是0
        return res;
    }
    private void dfs(TreeNode root, int depth) {
        if (root == null)  return;
        if (depth == res.size()) res.add(root.val);//该深度下的最右边的节点
        depth++;//递归到下一层,深度增加
        dfs(root.right, depth);
        dfs(root.left, depth);
    }
}

144.展开为链表

114. 二叉树展开为链表

你二叉树的根结点 root ,请你将它展开为一个单链表:

展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

提示:

树中结点数在范围 [0, 2000] 内
-100 <= Node.val <= 100
 

进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗?

根据二叉树前序遍历的顺序,将节点依次串起来

最好理解的版本

class Solution {
    public void flatten(TreeNode root) {
        if (root == null) return;
        Stack<TreeNode> s= new Stack<>();
        s.push(root);
        TreeNode pre= null;
        while (!s.isEmpty()) {
            TreeNode cur= s.pop();
            if (cur.right != null)  s.push(cur.right);
            if (cur.left != null)  s.push(cur.left);
            cur.left = null;
            if (pre != null)  pre.right = cur;
            pre = cur;
        }
    }
}

还有两个不易理解的版本,自己画图去

展开链表的各个节点是通过right进行来连接,有些会丢失,所以对当前节点的右子树进行暂存

class Solution {
    TreeNode p;
    public void flatten(TreeNode root) {
        if(root == null)return;
        if(p == null) p= root; //首个节点直接记录
        else{
            p.left = null;
            p.right = root;
            p = p.right;
        }
        TreeNode r = root.right;    // 暂存节点的右子树,下一轮用
        flatten(root.left); 
        flatten(r);
    }
}

//如果存在左子树,则将左子树插入当前节点右边,否则遍历至右子树
class Solution {
    public void flatten(TreeNode root) {
        while(root != null){
            TreeNode p = root.left;
            if(p != null){
                while(p.right != null) p = p.right;
                p.right = root.right;
                root.right = root.left;
                root.left = null; 
            }
            root = root.right;
        }
    }
}

105.前中序构造

105. 从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。


输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]
 

提示:

1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列

数据结构经典题目

class Solution {
    Map<Integer, Integer> m;
    public TreeNode buildTree(int[] qian, int[] zhong) {
        m = new HashMap<>();
        for (int i = 0; i < zhong.length; i++)  m.put(zhong[i], i);
        return findNode(qian, 0, qian.length, zhong,  0, zhong.length);  // 前闭后开
    }

    public TreeNode findNode(int[] qian, int pa, int pb, int[] zhong, int ia, int ib) {
        if (pa >= pb || ia >= ib) return null;
        int rootIndex = m.get(qian[pa]);  // 找到前序遍历的第一个元素在中序遍历中的位置
        TreeNode root = new TreeNode(zhong[rootIndex]);  // 构造结点
        int lenOfLeft = rootIndex - ia;  // 保存中序左子树个数,用来确定前序数列的个数
        root.left = findNode(qian, pa + 1, pa + lenOfLeft + 1,zhong, ia, rootIndex);
        root.right = findNode(qian, pa + lenOfLeft + 1, pb,zhong, rootIndex + 1, ib);
        return root;
    }
}

延伸:中后序

106. 从中序与后序遍历序列构造二叉树

class Solution {
    Map<Integer, Integer> m;  // 方便根据数值查找位置
    public TreeNode buildTree(int[] i, int[] p) {
        m = new HashMap<>();
        for (int j = 0; j < i.length; j++) { // 用m保存中序序列的数值对应位置
            m.put(i[j], j);
        }

        return findNode(i,  0, i.length, p, 0, p.length);  // 前闭后开
    }

    public TreeNode findNode(int[] i, int ia, int ib, int[] p, int pa, int pb) {
        if (ia >= ib || pa >= pb) {  // 不满足左闭右开,说明没有元素,返回空树
            return null;
        }
        int idx = m.get(p[pb - 1]);  // 找到后序遍历的最后一个元素在中序遍历中的位置
        TreeNode root = new TreeNode(i[idx]);  // 构造结点
        int lenOfLeft = idx - ia;  // 保存中序左子树个数,用来确定后序数列的个数
        root.left = findNode(i, ia, idx, p, pa, pa + lenOfLeft);
        root.right = findNode(i, idx + 1, ib, p, pa + lenOfLeft, pb - 1);

        return root;
    }
}

437.路径总和

437. 路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。


提示:

二叉树的节点个数的范围是 [0,1000]
-109 <= Node.val <= 109 
-1000 <= targetSum <= 1000 

class Solution {
    public int pathSum(TreeNode root, long sum) { //遍历每个节点,并对每个节点调用 rootSum
        if (root == null)  return 0;
        int res = rootSum(root, sum);//当前节点
        res += pathSum(root.left, sum);
        res += pathSum(root.right, sum);
        return res;
    }
    public int rootSum(TreeNode root, long sum) { //从当前节点开始查找路径
        int res = 0;
        if (root == null) return 0;
        int val = root.val;
        if (val == sum)  res++;
        //路径和等于 (sum - 当前节点值) 的路径数量
        res += rootSum(root.left, sum - val);
        res += rootSum(root.right, sum - val);
        return res;
    }
}

延伸:和为K的子数组

560. 和为 K 的子数组

236.最近公共祖先

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”



提示:

树中节点数目在范围 [2, 105] 内。
-109 <= Node.val <= 109
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        //递归处理去找到 p 或 q
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        //没找到,说明两个都在另一边的左子树
        if(left == null) return right;
        if(right == null) return left;
        //分别在左右子树
        return root;
    }
}

124.最大路径和h

124. 二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。


提示:

树中节点数目范围是 [1, 3 * 104]
-1000 <= Node.val <= 1000

class Solution {
    private int res = Integer.MIN_VALUE;  // 记录当前全局最大路径和,初始化为最小值
    public int maxPathSum(TreeNode root) {
        dfs(root);
        return res;
    }
    private int dfs(TreeNode node) {
        if (node == null) return 0;
        int lVal = dfs(node.left);
        int rVal = dfs(node.right);
        res = Math.max(res, lVal + rVal + node.val);//当前节点可以贡献的最大路径和
        return Math.max(Math.max(lVal, rVal) + node.val, 0);  //左子树或右子树中的一个方向来延展路径,负数则放弃
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值