代码随想录算法训练营十六天|二叉树part06
LeetCode 530 二叉搜索树的最小绝对差
题目链接:530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
示例 1:
输入:root = [4,2,6,1,3]
输出:1
示例 2:
输入:root = [1,0,48,null,null,12,49]
输出:1
首先将二叉搜索树中序遍历转化为有序数组,其实在遍历的过程中就可以直接计算了。
需要用一个pre节点来记录当前节点的前一个节点。
递归法:
class Solution {int res=Integer.MAX_VALUE;TreeNode pre=null;public int getMinimumDifference(TreeNode root) {//递归if(root==null)return 0;getMinimumDifference(root.left);if(pre!=null)res=Math.min(res,root.val-pre.val);pre=root;getMinimumDifference(root.right);return res;}
}
迭代法:
class Solution {public int getMinimumDifference(TreeNode root) {//迭代int res=Integer.MAX_VALUE;TreeNode pre=null;Stack<TreeNode> st=new Stack<>();st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node!=null){if(node.right!=null)st.push(node.right);st.push(node);st.push(null);if(node.left!=null)st.push(node.left);}else{TreeNode tmp=st.pop();if(pre!=null)res=Math.min(res,tmp.val-pre.val);pre=tmp;}}return res;}
}
LeetCode 501 二叉搜索树中的众数
题目链接:501. 二叉搜索树中的众数 - 力扣(LeetCode)
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2]
输出:[2]
示例 2:输入:root = [0]
输出:[0]
最直观的方法就是把整个树遍历了,用map统计频率,把频率排序,最后取前面高频的元素的集合。
class Solution {public int[] findMode(TreeNode root) {Map<Integer,Integer> map=new HashMap<>();Stack<TreeNode> st=new Stack<>();if(root!=null)st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node!=null){if(node.right!=null)st.push(node.right);st.push(node);st.push(null);if(node.left!=null)st.push(node.left);}else{node=st.pop();if(!map.containsKey(node.val))map.put(node.val,0);else map.put(node.val,map.get(node.val)+1);}}int maxCount=0;List<Integer> list=new ArrayList<>();for(Integer obj:map.keySet()){if(map.get(obj)>maxCount)maxCount=map.get(obj);}for(Integer obj:map.keySet()){if(map.get(obj)==maxCount)list.add(obj);}int res[]=new int[list.size()];for(int i=0;i<list.size();i++){res[i]=list.get(i);}return res;}
}
但既然是二叉搜索树,它中序遍历就是有序的,遍历有序数组的元素出现频率,从头遍历,那么一定是两个相邻元素作比较,然后把出现频率最高的元素输出就行了。
需要定义一个pre节点,是当前节点的前一个节点,每次将二者作比较。
if(pre==null)count=1;
else if(pre!=null&&tmp.val==pre.val)count++;
else count=1;
pre=tmp;
因为可能有多个众数,所以应该要先遍历一遍数组,找出最大频率,然后再遍历一遍数组把出现频率为该最大频率的元素放入结果中。这样的方式需要遍历两次数组。
那如何只遍历一遍呢?
如果当前元素出现频率等于最大频率,就把这个元素加入到结果集中;如果当前元素出现频率大于最大频率,先更新最大频率,然后清空结果集,因为结果集之前的元素都失效了,然后再把当前元素加入结果集中。
完整代码如下:
class Solution {public int[] findMode(TreeNode root) {int count=0;int maxCount=0;Stack<TreeNode> st=new Stack<>();List<Integer> list=new ArrayList<>();TreeNode pre=null;if(root!=null)st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node!=null){if(node.right!=null)st.push(node.right);st.push(node);st.push(null);if(node.left!=null)st.push(node.left);}else{TreeNode tmp=st.pop();if(pre==null)count=1;else if(pre!=null&&tmp.val==pre.val)count++;else count=1;pre=tmp;if(count==maxCount)list.add(tmp.val);else if(count>maxCount){maxCount=count;list.clear();list.add(tmp.val);}}}int[] res=new int[list.size()];for(int i=0;i<list.size();i++){res[i]=list.get(i);}return res;}
}
递归写法:
class Solution {List<Integer> list=new ArrayList<>();int count=0;int maxCount=0;TreeNode pre=null;public int[] findMode(TreeNode root) {//递归if(root==null)return null;findMode(root.left);if(pre==null)count=1;else if(pre!=null&&pre.val==root.val)count++;else count=1;pre=root;if(count==maxCount)list.add(root.val);else if(count>maxCount){maxCount=count;list.clear();list.add(root.val);}findMode(root.right);int[] res=new int[list.size()];for(int i=0;i<list.size();i++){res[i]=list.get(i);}return res;}
}
LeetCode 236 二叉树的最近公共祖先
题目链接:236. 二叉树的最近公共祖先 - 力扣(LeetCode)
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
示例 3:输入:root = [1,2], p = 1, q = 2
输出:1
条件:
- 树中节点数目在范围
[2, 105]
内。-109 <= Node.val <= 109
- 所有
Node.val
互不相同
。p != q
p
和q
均存在于给定的二叉树中。
思路:首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。
那么如何自底向上查找?
回溯法,后序遍历可以根据左右子树的返回值来处理中节点的逻辑。
如何判断一个节点是节点p和节点q的公共祖先?
如果找出一个节点,发现左子树出现节点p,右子树出现节点q,或者左子树出现节点q,右子树出现p,那么该节点就是节点p和q的最近公共祖先。
所以递归逻辑就是:如果递归遍历遇到q,就返回q;遇到p,就返回p,那么如果左右子树的返回值不为空,说明此时的中节点,一定是p和q的最近公共祖先。
因为题目中说了二叉树节点是不重复的,所以不用担心会不会左子树和右子树都返回q或p这样的问题。
实现代码如下:
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {//递归if(root==null||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;else if(left==null&&right!=null)return right;else if(left!=null&&right==null)return left;else return null;}
}
如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解。
如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之亦然。
为什么left为空,right不为空,目标节点就通过right返回?
我们来看下面的图:
节点为10的左子树返回null,右子树返回7,那么此时节点10就要把右子树的返回值返回上去。
完整流程图如下: