700.二叉搜索树中的搜索
1.题目描述
- 给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
- 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
2.解题思路
二叉搜索树是一个有序树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树。
3.代码实现
1.确定递归函数的参数和返回值:
递归函数定义:传入根节点和目标值,返回目标值所在的节点,目标值不存在就返回null。
//700.二叉搜索树中的搜索
//递归函数定义:传入根节点和目标值,返回目标值所在的节点,目标值不存在就返回null
public TreeNode searchBST(TreeNode root, int val) {}
2.确定终止条件:
如果为空节点的话,就返回null,表示目标值不存在。
//递归终止条件
if (root == null) {
return root;
}
3.确定单层递归的逻辑:
利用二叉搜索树的特性:
- 目标值小于当前节点值,去当前节点的左子树寻找。
- 目标值大于当前节点值,去当前节点的右子树寻找。
- 目标值等于当前节点值,返回当前节点。
//单层递归逻辑
TreeNode res = null;
if (val < root.val) {
res = searchBST(root.left, val);//去当前节点的左子树寻找
} else if (val > root.val) {
res = searchBST(root.right, val);//去当前节点的右子树寻找
} else {
return root;//返回当前节点
}
return res;
完整代码如下:
class Solution {
//700.二叉搜索树中的搜索
//递归函数定义:传入根节点和目标值,返回目标值所在的节点,目标值不存在就返回null
public TreeNode searchBST(TreeNode root, int val) {
//递归终止条件
if (root == null) {
return root;
}
//单层递归逻辑
TreeNode res = null;
if (val < root.val) {
res = searchBST(root.left, val);
} else if (val > root.val) {
res = searchBST(root.right, val);
} else {
return root;
}
return res;
}
}
98.验证二叉搜索树
1.题目描述
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效二叉搜索树定义如下:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
2.解题思路
要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。
有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。
3.代码实现
3.1递归法
1.确定递归函数的参数和返回值:
递归函数定义:传入一个节点,采用中序遍历,把节点值放进list中。
//递归函数定义:传入一个节点,采用中序遍历,把节点值放进list中
public void travesal(TreeNode root) {}
2.确定终止条件:
如果为空节点的话,就return。
//递归终止条件
if (root == null) {
return;
}
3.确定单层递归的逻辑:
利用二叉搜索树的特性:
单层递归逻辑(一定采用中序遍历,左中右)。
把遍历到的所有节点值放进list 中。
将list转换为数组,检查是否有序即可。
//单层递归逻辑(一定采用中序遍历,左中右)
isValidBST(root.left);//左
//把遍历到的所有节点值放进list 中
inOrderList.add(root.val);
isValidBST(root.right);//右
完整代码如下:
class Solution {
//98.验证二叉搜索树
List<Integer> inOrderList = new ArrayList<>();
public boolean isValidBST(TreeNode root) {
travesal(root);
//将list转换为数组,检查是否有序即可
int[] inOrderArray = inOrderList.stream()
.mapToInt(Integer::intValue).toArray();
for (int i = 1; i < inOrderArray.length; i++) {
if (inOrderArray[i] <= inOrderArray[i - 1]) {
return false;
}
}
return true;
}
//递归函数定义:传入一个节点,采用中序遍历,把节点值放进list中
public void travesal(TreeNode root) {
//递归终止条件
if (root == null) {
return;
}
//单层递归逻辑(一定采用中序遍历,左中右)
isValidBST(root.left);//左
//把遍历到的所有节点值放进list 中
inOrderList.add(root.val);
isValidBST(root.right);//右
}
}
3.2递归法(双指针)
class Solution {
//98.验证二叉搜索树(双指针-递归)
TreeNode pre;
//递归函数定义:传入一个节点,检查以该节点为根节点的树是不是二叉搜索树
public boolean isValidBST(TreeNode root) {
//递归终止条件
if (root == null) {
return true;
}
//单层递归逻辑(一定采用中序遍历,左中右)
boolean leftBST = isValidBST(root.left); //左
//每次比较pre和root的值,只要不满足单调递增就返回false
if (pre != null && pre.val >= root.val) {
return false;
}
//更新pre,让pre跟在root的后面
pre = root; //中
boolean rightBST = isValidBST(root.right); //右
return leftBST && rightBST;
}
}
530.二叉搜索树的最小绝对差
1.题目描述
给你一个二叉搜索树的根节点
root
,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。
2.解题思路
- 题目中要求在二叉搜索树上任意两节点的差的绝对值的最小值。
- 注意是二叉搜索树,二叉搜索树可是有序的。
- 遇到在二叉搜索树上求什么最值啊,差值之类的,就把它想成在一个有序数组上求最值,求差值,这样就简单多了。
- 当然也可以采用双指针的写法,每次更新差值即可。
3.代码实现(递归-双指针)
1.确定递归函数的参数和返回值:
递归函数定义:传入一个节点,用于更新最小绝对差(res)。
//递归函数定义:传入一个节点,用于更新最小绝对差(res)
public void travesal2(TreeNode cur) {}
2.确定终止条件:
如果为空节点的话,就return。
//递归终止条件
if (root == null) {
return;
}
3.确定单层递归的逻辑:
利用二叉搜索树的特性:
单层递归逻辑(一定采用中序遍历,左中右)。
每次得到cur和preNode的差值,更新res。
更新preNode,让preNode跟在cur的后面。
//单层递归逻辑
travesal2(cur.left);
//每次得到cur和preNode的差值,更新res
if (preNode != null) {
res = Math.min(res, cur.val - preNode.val);
}
//更新preNode,让preNode跟在cur的后面
preNode = cur;
travesal2(cur.right);
完整代码如下:
class Solution {
//530.二叉搜索树的最小绝对差
int res = Integer.MAX_VALUE;
TreeNode preNode;
public int getMinimumDifference(TreeNode root) {
travesal2(root);
return res;
}
//递归函数定义:传入一个节点,用于更新最小绝对差(res)
public void travesal2(TreeNode cur) {
//递归终止条件
if (cur == null) {
return;
}
//单层递归逻辑
travesal2(cur.left);
//每次得到cur和preNode的差值,更新res
if (preNode != null) {
res = Math.min(res, cur.val - preNode.val);
}
//更新preNode,让preNode跟在cur的后面
preNode = cur;
travesal2(cur.right);
}
}
501. 二叉搜索树中的众数
1.题目描述
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有众数(即出现频率最高的元素)。如果树中有不止一个众数,可以按任意顺序返回。
假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
2.解题思路
这道题目呢,递归法从两个维度来讲。
首先如果不是二叉搜索树的话,应该怎么解题,是二叉搜索树,又应该如何解题,两种方式做一个比较,可以加深大家对二叉树的理解。
3.代码实现
3.1递归法(非二叉搜索树)
- 新建一个map,Key:节点值,value:用于存放每个节点出现的频率。
- 将map按频率由高到低进行排序,并转换为list。
- 得到list中第一个元素(排序后频率最高),添加到结果集中。
- 查看是否有和最大频率相同的元素,有的话就加到结果集中,如果不相等,因为排序了,后边的频次都比前边小,终止循环。
完整代码如下:
class Solution {
//501. 二叉搜索树中的众数-暴力解法
//Key:节点值,value:用于存放每个节点出现的频率
HashMap<Integer, Integer> map = new HashMap<>();
public int[] findMode(TreeNode root) {
//结果集
ArrayList<Integer> ans = new ArrayList<>();
if (root == null) {
return ans.stream().mapToInt(Integer::intValue).toArray();
}
//得到节点出现的频率map
travesal4(root);
//将map按频率由高到低进行排序,并转换为list
List<Map.Entry<Integer, Integer>> list = map.entrySet().stream()
.sorted((c1, c2) -> c2.getValue() - c1.getValue())
.collect(Collectors.toList());
//得到list中第一个元素(排序后频率最高),添加到结果集中
ans.add(list.get(0).getKey());
//查看是否有和最大频率相同的元素,有的话就加到结果集中
for (int i = 1; i < list.size(); i++) {
if (list.get(i).getValue() == list.get(0).getValue()) {
ans.add(list.get(i).getKey());
} else {
//如果不相等,因为排序了,后边的频次都比前边小,终止循环
break;
}
}
return ans.stream().mapToInt(Integer::intValue).toArray();
}
public void travesal4(TreeNode cur) {
//递归终止条件
if (cur == null) {
return;
}
//单层递归逻辑
map.put(cur.val, map.getOrDefault(cur.val, 0) + 1);
travesal4(cur.left); //左
travesal4(cur.right); //右
}
}
3.2递归法(二叉搜索树)
1.确定递归函数的参数和返回值:
递归函数定义:传入一个节点,用于更新众数MaxCount,并将众数放进list中。
//递归函数定义:传入一个节点,用于更新众数MaxCount,并将众数放进list中
public void travesal3(TreeNode cur) {}
2.确定终止条件:
如果为空节点的话,就return。
//递归终止条件
if (root == null) {
return;
}
3.确定单层递归的逻辑:
利用二叉搜索树的特性:
单层递归逻辑(一定采用中序遍历,左中右)。
根据preNode和cur的值,更新count。
为了只遍历一次,碰到一个count就添加到list中。
count(一个节点出现的个数)大于MaxCount(之前记录的节点最大出现次数),更新MaxCount值,并将list清空,把MaxCount对应的节点放进list。
更新preNode,让preNode跟在cur的后面。
//单层递归逻辑
travesal3(cur.left); //左
//根据preNode和cur的值,更新count
if (preNode == null) {
count = 1; //pre不存在,cur为第一个节点,count=1
} else if (preNode.val == cur.val) {
count++; //两者相等,count++
} else {
count = 1;//两者不相等,新节点出现次数为1
}
//为了只遍历一次,碰到一个count就添加到list中
if (count == MaxCount) {
list.add(cur.val);
//count(一个节点出现的个数)大于MaxCount(之前记录的节点最大出现次数)
} else if (count > MaxCount) {
MaxCount = count;//更新MaxCount值
list.clear();//并将list清空
list.add(cur.val);//把MaxCount对应的节点放进list
}
//更新preNode,让preNode跟在cur的后面
preNode = cur; //中
travesal3(cur.right); //右
完整代码如下:
class Solution {
//501. 二叉搜索树中的众数
TreeNode preNode;
int count = 0;
int MaxCount = 0;
List<Integer> list = new ArrayList<>();
public int[] findMode(TreeNode root) {
travesal3(root);
return list.stream().mapToInt(Integer::intValue).toArray();
}
//递归函数定义:传入一个节点,用于更新众数MaxCount,并将众数放进list中
public void travesal3(TreeNode cur) {
//递归终止条件
if (cur == null) {
return;
}
//单层递归逻辑
travesal3(cur.left); //左
//根据preNode和cur的值,更新count
if (preNode == null) {
count = 1; //pre不存在,cur为第一个节点,count=1
} else if (preNode.val == cur.val) {
count++; //两者相等,count++
} else {
count = 1;//两者不相等,新节点出现次数为1
}
//为了只遍历一次,碰到一个count就添加到list中
if (count == MaxCount) {
list.add(cur.val);
//count(一个节点出现的个数)大于MaxCount(之前记录的节点最大出现次数)
} else if (count > MaxCount) {
MaxCount = count;//更新MaxCount值
list.clear();//并将list清空
list.add(cur.val);//把MaxCount对应的节点放进list
}
//更新preNode,让preNode跟在cur的后面
preNode = cur; //中
travesal3(cur.right); //右
}
}