简介
题目 - 01: 验证二叉搜索树 (LeetCode 98)
题目 - 02: 二叉搜索树中的最小绝对值 (LeetCode 530)
题目 - 03: 二叉搜索树中的众数 (LeetCode 501)
题目 - 04: 二叉树的最近公共祖先 (LeetCode 236)
题目 - 05: 二叉搜索树的插入操作 (LeetCode 701 )
题目 01 - 验证二叉搜索树
原题链接
题目描述
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树 有效 二叉搜索树定义如下:
- 节点的左子树只包含
小于当前节点的数。- 节点的右子树只包含
大于当前节点的数。- 所有左子树和右子树自身必须也是二叉搜索树。
方法 01 - 递归 + 转化成数组
- 如果为一个二叉搜索树,则按照中序遍历 (左根右)得到的结果应该是递增的
- 可以先遍历并按照中序遍历记录所有遍历得到的节点值- 然后判断是否递增,如果不是则不是二叉搜索树
代码实现
class Solution {
private List<Integer> ans = new ArrayList<>();
public boolean isValidBST(TreeNode root) {
if (root == null) {
return false;
}
inOrder(root);
for (int i = 1; i < ans.size(); i++) {
if (ans.get(i - 1) >= ans.get(i)) {
return false;
}
}
return true;
}
public void inOrder(TreeNode node) {
if (node == null) {
return;
}
inOrder(node.left);
ans.add(node.val);
inOrder(node.right);
}
}
执行结果
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
方法 02 - 递归内判断
- 直接再递归内判断:
- 验证条件: 左子树中所有节点均要小于中间节点,右子树所有节点大于中间节点
- 按照中序遍历顺序,后一个遍历到的节点值一定要比前一个节点值大 prev
- 临时变量用于记录前一个结点
代码实现
class Solution {
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
// 中序遍历: 左
boolean isLeftValid = isValidBST(root.left);
// 若为二叉搜索树前一个节点的值需要小于后一个
if (pre != null && pre.val >= root.val) {
return false;
}
pre = root; // 更新前一个结点的记录
boolean isRightValid = isValidBST(root.right);
return isLeftValid && isRightValid;
}
}
执行结果
- 时间复杂度:
O(N) - 空间复杂度:
O(H)
题目 02 - 二叉搜索树的最小绝对值
原题链接
题目描述
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值。
差值是一个正数,其数值等于两值之差的绝对值。
方法 - 递归
- 借助两个变量分别记录,
最小差值: midDiff;前一个遍历到的节点: 最小差值。 - 由于是二叉搜索树,如果按照中序遍历的话。为递增序列,因此最小差值肯定发生在两个相邻节点之间
- 借助
abs计算最小差值。Math.min()更新最小差值 - 每轮遍历完当前节点后更新前驱节点
pre指向当前节点
代码实现
class Solution {
private TreeNode pre;
private int minDiff = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return root.val;
}
getMin(root);
return minDiff;
}
public void getMin(TreeNode node) {
if (node == null) {
return;
}
getMin(node.left); // 中序遍历; 左
if (pre != null) { // 中
minDiff = Math.min(minDiff, Math.abs(pre.val - node.val));
}
pre = node; //更新前驱
getMin(node.right); // 右
}
}
执行结果
- 时间复杂度:
O(N) - 空间复杂度:
O(H)
题目 03 - 二叉搜索树中的众数
原题链接
题目描述
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义: 结点左子树中所含节点的值 小于等于 当前节点的值 结点右子树中所含节点的值 大于等于 当前节点的值 左子树和右子树都是二叉搜索树
方法 - 递归
- 由于是二叉搜索树,如果按照中序遍历,则遍历的顺序得到的结果为从小到大
prev记录当前节点的前驱节点,count记录当前节点值的出现次数,maxCount记录最大出现次数。- 如果
prev为空或者当前节点值与前驱节点值不一致,则需要更新计数count - 将等于
count出现次数的所有节点值记录,假设它们都为 '众数'; - 若存在
count>maxCount,说明众数出现次数大于当前节点值出现次数,清空之前记录的所有 '众数', - 记录当前
curr.val为众数之一,同时更新maxCount只有大于等于该curr.val才可能是众数
代码实现
class Solution {
private List<Integer> ans = new ArrayList<>(); // 统计所有出现次数最多次元素
private int maxCount = 0; // 最大出现频率
private int count = 0; // 出现频率
private TreeNode prev = null; // 前驱节点
public int[] findMode(TreeNode root) {
if (root.left == null && root.right == null) {
return new int[] { root.val };
}
inOrder(root);
// 将结果封装到数组
int[] arr = new int[ans.size()];
for (int i = 0; i < ans.size(); i++) {
arr[i] = ans.get(i);
}
return arr;
}
public void inOrder(TreeNode curr) {
if (curr == null) {
return;
}
inOrder(curr.left);
int rootValue = curr.val;
// 计数,如果前驱为 null 或者遍历到新节点值
if (prev == null || rootValue != prev.val) {
count = 1;
} else {
count++;
}
// 更新结果以及maxCount
if (count > maxCount) {
ans.clear();
ans.add(rootValue);
maxCount = count;
} else if (count == maxCount) {
ans.add(rootValue);
}
prev = curr;
inOrder(curr.right);
}
}
执行结果
- 时间复杂度:
O(N) - 空间复杂度:
O(H)
题目 04 - 二叉树的最近公共祖先
原题链接
题目描述
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,
最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
方法 - 递归
若存在祖先节点:
- 情况一: 如果当前 p q 分别为一个 node 的左/右子节点则返回 node
- 情况二: 如果当前 p q 中的一个为另外一个的祖先节点,则返回 p / q
- 由于需要判断左右子树的父节点,那么只能通过后序遍历实现从底向上的遍历方式
- 在回溯的过程中,需要遍历整个二叉树
- 归返回条件,如果为空,或者当前节点 node 为 q 或者 p 也返回
- 借助返回值判断
- 如果返回值都为 NULL,叶子节点
- 如果返回值不为,即为 p 和 q; 此时祖先节点即为当前层的 root 节点;返回 root
- 如果返回值其中一个为 NULL,另外一个不为 NULL 的即为公共祖先结果,传递回上一层
代码实现
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == q || root == p || root == null) return root;
//遍历左子树
TreeNode left = lowestCommonAncestor(root.left, p, q);
//右子树
TreeNode right = lowestCommonAncestor(root.right, p, q);
//如果 left 和 right 都不为空,说明 root 就是公共节点
if(left != null && right != null) {
return root;
}
//若存在不为空,已找到最近公共祖先 / 当前p或者q为另外一个的祖先节点,返回
if(left == null && right != null) {
return right;
} if(right == null && left != null) {
return left;
}
//否则返回 null
return null;
}
}
执行结果
- 时间复杂度:
O(N) - 空间复杂度:
O(H)
题目 05 - 二叉搜索树中的插入操作
原题链接
题目描述
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
方法 - 递归
- 仅考虑挂载节点的情况,不去实现重构整个树结构
- 递归终止条件
node == null: 插入树节点new TreeNode(root.val); - 由于是二叉搜索树,如果
val > root.val当前节点值,查询其右子树; 反之左子树 - 传递参数:node 的左右子直接 + val
- 挂载新节点的方式: node.left = return 树节点
代码实现
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null) {
return new TreeNode(val);
}
//当前节点大于 val; 往左子树寻找;反之右子树
if(root.val > val) root.left = insertIntoBST(root.left, val); //如果节点为空,挂载节点
if(root.val < val) root.right = insertIntoBST(root.right, val);
return root;
}
}
执行结果
- 时间复杂度:
O(N) - 空间复杂度:
O(H)