代码随想录刷题day18丨530.二叉搜索树的最小绝对差,501.二叉搜索树中的众数 ,236. 二叉树的最近公共祖先
1.题目
1.1二叉搜索树的最小绝对差
-
视频讲解:二叉搜索树中,需要掌握如何双指针遍历!| LeetCode:530.二叉搜索树的最小绝对差_哔哩哔哩_bilibili
-
文档讲解:https://programmercarl.com/0530.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E5%B0%8F%E7%BB%9D%E5%AF%B9%E5%B7%AE.html
-
解题思路:递归(中序遍历)+ 双指针思想
- 需要用一个pre节点记录一下cur节点的前一个节点。
-
代码:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { int result = Integer.MAX_VALUE; TreeNode pre = null; public int getMinimumDifference(TreeNode root) { traversal(root); return result; } public void traversal(TreeNode cur){ if(cur == null){ return; } traversal(cur.left); if(pre != null){ result = Math.min(result,cur.val - pre.val); } pre = cur; traversal(cur.right); } }
-
总结:
- 遇到在二叉搜索树上求什么最值,求差值之类的,都要思考一下二叉搜索树可是有序的,要利用好这一特点。
1.2二叉搜索树中的众数
-
视频讲解:不仅双指针,还有代码技巧可以惊艳到你! | LeetCode:501.二叉搜索树中的众数_哔哩哔哩_bilibili
-
文档讲解:https://programmercarl.com/0501.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E4%BC%97%E6%95%B0.html
-
解题思路:递归(中序遍历)+ 双指针
- 弄一个指针指向前一个节点,这样每次cur(当前节点)才能和pre(前一个节点)作比较。而且初始化的时候pre = NULL,这样当pre为NULL时候,我们就知道这是比较的第一个元素。
- 频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集
-
代码:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { TreeNode pre = null; int count = 0; int maxCount = 0; List<Integer> result = new ArrayList<>(); public int[] findMode(TreeNode root) { traversal(root); int[] res = new int[result.size()]; for(int i = 0;i < result.size();i++){ res[i] = result.get(i); } return res; } public void traversal(TreeNode cur){ if(cur == null){ return; } traversal(cur.left); if(pre == null){ count = 1; }else if(pre.val == cur.val){ count++; }else{ count = 1; } pre = cur; if(count == maxCount){ result.add(cur.val); } if(count > maxCount){ maxCount = count; result.clear(); result.add(cur.val); } traversal(cur.right); } }
-
总结:
- 如果不是二叉搜索树,最直观的方法一定是把这个树都遍历了,用map统计频率,把频率排个序,最后取前面高频的元素的集合。
1.3二叉树的最近公共祖先
-
文档讲解:https://programmercarl.com/0236.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88.html
-
解题思路:递归(后序遍历)
-
遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。
那么二叉树如何可以自底向上查找呢?
回溯啊,二叉树回溯的过程就是从低到上。
后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
-
接下来就看如何判断一个节点是节点q和节点p的公共祖先呢?
- 首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。 即情况一:
-
- 判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。
- **很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。** 情况二
- 其实情况一 和 情况二 代码实现过程都是一样的,也可以说,实现情况一的逻辑,顺便包含了情况二。
因为遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本身就是 公共祖先的情况。
-
代码:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if(root == null){ return null; } if(root == p || root == q){ return root; } TreeNode left = lowestCommonAncestor(root.left,p,q); TreeNode right = lowestCommonAncestor(root.right,p,q); if(left != null && right != null){ return root; } if(left != null && right == null){ return left; } if(left == null && right != null){ return right; } return null; } }
-
总结:
- 注意题目强调:二叉树节点数值是不重复的,而且一定存在 q 和 p。