目录
方法一 递归 - 中序遍历(记录中序遍历升序数组,再获取最小差值)
方法二 递归 - 中序遍历(中序遍历节点的同时,获取最小差值)
方法一 递归(任意方式遍历,记录map,遍历map获取众数)
方法三 统一迭代法(中序遍历,处理节点时记录当前最高频次的val)
【530.二叉搜索树的最小绝对差】简单题
二叉搜索树的性质:中序遍历的数组是升序数组。
思路:通过中序遍历可以确保当前遍历的节点的val大于上一个节点的val,再计算两节点的差值,更新最小差值即可。
相似题目:98.验证二叉搜索树
方法一 递归 - 中序遍历(记录中序遍历升序数组,再获取最小差值)
class Solution {
List<Integer> nodes = new ArrayList<>();
public int getMinimumDifference(TreeNode root) {
// 二叉搜索树的中序遍历是一个有序的数组,只需要统计相邻节点之间的差值,获取最小的差值即可
inorder(root);
// 获取升序数组中相邻节点的最小差值
int minDelta = nodes.get(1) - nodes.get(0);
for (int i = 1; i < nodes.size() - 1; i++){
int delta = nodes.get(i+1) - nodes.get(i);
if (delta < minDelta) minDelta = delta;
}
return minDelta;
}
// 递归,中序遍历,记录所有节点的值
public void inorder(TreeNode root){
if (root == null) return;
inorder(root.left);
nodes.add(root.val);
inorder(root.right);
}
}
方法二 递归 - 中序遍历(中序遍历节点的同时,获取最小差值)
class Solution {
TreeNode pre = null;
int minDelta = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
if (root == null) return 0;
getMinimumDifference(root.left); // 遍历左子树,更新最小差值
// 计算当前节点与上一个遍历节点的差值,比较和更新最小差值
if (pre != null) {
if (root.val - pre.val < minDelta) minDelta = root.val - pre.val;
}
pre = root; // 更新上一个节点
getMinimumDifference(root.right); // 遍历右子树,更新最小差值
return minDelta; // 返回当前节点的最小差值
}
}
方法三 统一迭代 - 中序遍历(处理节点时,更新最小差值)
统一迭代 - 中序遍历(左中右)的入栈顺序:
cur.right -> cur -> null -> cur.left
class Solution {
public int getMinimumDifference(TreeNode root) {
TreeNode pre = null;
int minDelta = Integer.MAX_VALUE;
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root;
stack.push(cur);
while (!stack.isEmpty()){
cur = stack.pop();
if (cur != null){
if (cur.right != null) stack.push(cur.right); // 右
stack.push(cur); // 中
stack.push(null); // null
if (cur.left != null) stack.push(cur.left); // 左
}
else{
cur = stack.pop();
if (pre != null) minDelta = Math.min(cur.val - pre.val, minDelta); // 更新最小差值
pre = cur; // 更新上一个遍历的节点
}
}
return minDelta;
}
}
【501.二叉搜索树中的众数】简单题
方法一 递归(任意方式遍历,记录map,遍历map获取众数)
思路:
1、递归遍历二叉树,获取map,key:root.val,value:cnt
2、遍历map,获取最高频次
3、遍历map,获取出现最高频次的节点val
4、将List转为Array并返回
class Solution {
Map<Integer, Integer> map = new HashMap<>();
public int[] findMode(TreeNode root) {
// 递归获取map,key:root.val,value:cnt
getNodes(root);
// 获取最高频次
int max = 0;
for (int val: map.keySet()){
max = Math.max(max, map.get(val));
}
// 获取出现最高频次的节点val
List<Integer> resList = new ArrayList<>();
for (int val: map.keySet()){
if (map.get(val) == max){
resList.add(val);
}
}
// 将List转为Array
int i = 0;
int[] res = new int[resList.size()];
for (int val:resList){
res[i++] = val;
}
return res;
}
// 递归获取map,key:root.val,value:cnt
public void getNodes(TreeNode root){
if (root == null) return;
getNodes(root.left);
map.put(root.val, map.getOrDefault(root.val, 0) + 1);
getNodes(root.right);
}
}
方法二 递归 - 中序遍历(中序遍历的同时,获取众数)
思路:利用递归进行中序遍历,相等val的节点会连续出现。令pre指向上一个遍历的节点,当节点val发生变化时(pre.val != root.val),统计上一个节点的val出现的次数,判断这个val是否为众数。
注意:最后统计的节点val并没有判断是否为众数,因此需要在递归结束后加一次判断。
class Solution {
TreeNode pre = null; // 记录上一个节点
int maxCnt = 0; // 记录最高频次
int cnt = 0; // 记录某个节点的val出现的次数
List<Integer> res = new ArrayList<>();
public int[] findMode(TreeNode root) {
getMode(root);
// 最后遍历到的值出现的次数没有和maxCnt比较,要单独处理
if (cnt >= maxCnt) {
if (cnt > maxCnt) res.clear();
res.add(pre.val);
}
int[] resArray = new int[res.size()];
int i = 0;
for (int x : res){
resArray[i++] = x;
}
return resArray;
}
public void getMode(TreeNode root){
if (root == null) return;
// 遍历左子树,统计节点val出现的次数
getMode(root.left);
// 当前节点的val不等于上一个节点的val时,比较上一个节点对应的cnt和maxCnt
if (pre != null && pre.val != root.val) {
if (cnt >= maxCnt) {
if (cnt > maxCnt) {
res.clear();
maxCnt = cnt;
}
res.add(pre.val); // 注意:加入结果集合的是上一个节点的val
}
cnt = 1;
}
else{
cnt++;
}
pre = root;
// 遍历右子树,统计节点val出现的次数
getMode(root.right);
}
}
方法三 统一迭代法(中序遍历,处理节点时记录当前最高频次的val)
思路:和方法二处理当前节点的思路基本一致,统一迭代中序遍历的入栈顺序为【cur.right -> cur -> null -> cur.left】
class Solution {
public int[] findMode(TreeNode root) {
TreeNode pre = null;
int cnt = 0;
int maxCnt = 0;
List<Integer> resList = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root;
stack.push(cur);
// 统一迭代遍历
while(!stack.isEmpty()){
cur = stack.pop();
if (cur != null){
if (cur.right != null) stack.push(cur.right);
stack.push(cur);
stack.push(null);
if (cur.left != null) stack.push(cur.left);
}
else{
cur = stack.pop();d
// 处理节点,记录结果
if (pre != null && pre.val != cur.val) {
if (cnt >= maxCnt){
if (cnt > maxCnt) {
resList.clear();
maxCnt = cnt;
}
resList.add(pre.val);
}
cnt = 1;
}
else {
cnt++;
}
pre = cur;
}
}
// 处理最后一个节点的val
if (cnt >= maxCnt){
if (cnt > maxCnt) {
resList.clear();
maxCnt = cnt;
}
resList.add(pre.val);
}
// List -> Array
int[] res = new int[resList.size()];
for(int i = 0; i < resList.size(); i++){
res[i] = resList.get(i);
}
return res;
}
}
【236. 二叉树的最近公共祖先】中等题
思路:递归 - 后序遍历1、确定参数和返回值:
传入当前节点和要寻找祖先的两个节点p和q,返回寻找的结果。
2、确定终止条件:
如果当前节点为null,或当前节点就是要寻找的节点,返回当前节点
3、确定单层递归逻辑(后序遍历):
- 获取左右子树的寻找结果
- 确定当前节点的返回结果
- 左右都不为空,则当前节点为最近公共祖先
- 如果左右都为空,则当前节点也为空
- 如果只有右不为空,则返回右
- 如果只有左不为空,则返回左
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 确定终止条件(如果当前节点为null,或当前节点就是要寻找的节点,返回当前节点)
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 null; // 如果左右都为空,则当前节点也为空
else if (right != null) return right; // 如果只有右不为空,则返回右
else return left; // 如果只有左不为空,则返回左
}
}