BST----Binary Search Tree (二叉搜索树、二叉查找树、排序二叉树)
概念:1.空树; 2.左右子树均是BST;3.左子树所有节点的数据域均小于或等于根节点的数据域,右子树则大于
特点:中序遍历的结果是有序的
235. 二叉搜索树的最近公共祖先
思路一:从根出发,两次遍历,分别记录两节点各自的路径,它们的路径必有分叉点,而那个分叉点即为最近公共祖先。例如求0,5的最近公共祖,第一次dfs获得0的路径为[6,2,0],第二次获取5的路径为[6,2,4,5]。对比两路径,可得分叉点为2.
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> lp = new ArrayList<TreeNode>();
List<TreeNode> lq = new ArrayList<TreeNode>();
//两次遍历,得到各自路径
dfs(root,p,lp);
dfs(root,q,lq);
TreeNode ans = null;
//对比
for(int i=0;i<lp.size() && i<lq.size();i++){
//最后一个交叉点,即分叉点
if(lp.get(i).val == lq.get(i).val) ans = lp.get(i);
else break;
}
return ans;
}
void dfs(TreeNode root,TreeNode find,List<TreeNode> l){
if(root == null) return;
if(find.val == root.val) {
l.add(find);
return;
} else if(find.val < root.val) {
l.add(root);
dfs(root.left,find,l);
} else {
l.add(root);
dfs(root.right,find,l);
}
return;
}
改进:二次遍历也可改进为一次遍历,只是返回判断需要两个节点都找到了才返回
思路二:根据本题和二叉搜索树的特点,可得出左边的所有节点<根节点<右边的所有节点,说明最近公共祖先节点是介于两节点之间的。例如求0,5,其最近公共祖先为搜索树中第一个满足0<=find<=5的节点(等号是因为节点本身可做祖先),而且仅有一个(需要仔细品味)。
TreeNode ans = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
int max = Math.max(p.val,q.val);
int min = Math.min(p.val,q.val);
dfs(root,min,max);
return ans;
}
void dfs(TreeNode root, int min, int max){
if(root == null || ans != null) return;//到底或者找到了就结束。
else if(root.val<min) {
dfs(root.right,min,max);//小于往右
} else if(root.val>max){
dfs(root.left,min,max);//大于往左
} else {//等价root.val >= min.val && root.val <= max.val
ans = root;
return;
}
}
236. 二叉树的最近公共祖先
思路: 树递归有个特点就是无论你左边和右边的深度一不一样,当你左右两边递归结束,总会返回到同一层。例如求6和7,找到6就返回,找到7就返回,虽然两边深度不一样,但必定会在5这一层相遇,而该节点也就是答案。
注意:本题是二叉树,而非二叉搜索树,所以不具备BST的特性。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
//返回找到的节点
if(root.val == p.val) return p;
if(root.val == q.val) return q;
//往左右递归
TreeNode l = lowestCommonAncestor(root.left,p,q);
TreeNode r = lowestCommonAncestor(root.right,p,q);
//接收到左右两边返回节点都不为空,说明当前节点为答案
if(l != null && r !=null) {
return root;
}
//否则,继续将不为空的节点返回上一层
return l != null ? l : r;
}
669. 修剪二叉搜索树
思路:二叉搜索树–删除节点,开头说的BST概念中的第三个性质尤为重要
这种删除相对较简单,根据区间来决定—[low,high]
- 若当前节点值在该区间内,则进入下一层递归,去判断子节点即可。
- 若当前节点值小于low,则直接用右节点来代替该节点,也就是root = root.right,无需考虑左节点,因为当前节点一定比左节点大,也就是左节点肯定小于low,所以直接用右节点代替当前节点即可
- 若当前节点值大于high,则用左节点来代替该节点,也就是root = root.left,理由同上;
注意:上面两步不是一次if判断赋值来解决的,而是需要通过递归或循环,因为root = root.right,可能右边的值也小于low,所以需要不断递归或循环,直到找到位于该区间内的节点才算对。
注意在java中,没有所谓的C/C++的那种指针,所以递归的时候需要返回每个节点的引用
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root == null) return null;
if(root.val < low) root = trimBST(root.right,low,high);//递归
else if(root.val > high) root = trimBST(root.left,low,high);//递归
else {
root.left = trimBST(root.left,low,high);
root.right = trimBST(root.right,low,high);
}
return root;
}
}
改进:对于递归筛选节点,也可以通过穷举
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root == null) return null;
while(root != null){//穷举
if(root.val < low) root = root.right;
else if (root.val > high) root = root.left;
else break;
}
if(root != null) {
root.left = trimBST(root.left,low,high);
root.right = trimBST(root.right,low,high);
}
return root;
}
}
230. 二叉搜索树中第K小的元素
思路:开头说的BST的特点:中序遍历有序
class Solution {
int num=0;
int ans = -1;
//中序遍历+获取第k个即可
public int kthSmallest(TreeNode root, int k) {
dfs(root,k);
return ans;
}
public void dfs(TreeNode root, int k) {
if(root == null || ans != -1) return;
dfs(root.left,k);
num++;
if(k == num) {
ans = root.val;
}
dfs(root.right,k);
return;
}
}
538. 把二叉搜索树转换为累加树
思路:中序遍历的逆序
遍历顺序是8->7->6->5->4->3->2->1->0, 正好和中序遍历相反。所以遍历时只需要交换左右,然后使用一个变量用于累加每个节点值。
class Solution {
int temp=0;//累加变量
public TreeNode convertBST(TreeNode root) {
if(root == null) return null;
convertBST(root.right);
temp += root.val;
root.val = temp;
convertBST(root.left);
return root;
}
}
98. 验证二叉搜索树
https://leetcode.cn/problems/validate-binary-search-tree/
文章开头提到过二叉搜索树中序遍历的结果有序,那么验证是否是二叉搜索树,可根据该特性进行解题,先获取中序遍历的结果,然后两两比较,判断是否严格递增,是则true,反之false
class Solution {
private List<Integer> arr = null;
public boolean isValidBST(TreeNode root) {
//中序结果有序
// 1 5 3 4 6
arr = new ArrayList<>();
dfs(root);
int l = arr.size();
for(int i = 1; i<l ;i++) {
if(arr.get(i-1) >= arr.get(i)) return false;
}
return true;
}
private void dfs(TreeNode root) {
if(root == null) {
return;
}
dfs(root.left);
arr.add(root.val);
dfs(root.right);
}
}
优化:在遍历过程中进行比较,只要arr大小超过0就可以将当前元素和arr中的最后一个元素进行比较,如果小于说明有序,否则无序直接返回false
class Solution {
private List<Integer> arr = null;
private boolean ans = false; //保存结果
public boolean isValidBST(TreeNode root) {
arr = new ArrayList<>();
dfs(root);
return !ans;
}
private void dfs(TreeNode root) {
if(ans || root == null) {
return;
}
dfs(root.left);
int size = arr.size();
//中间进行比较,逆序则无需加入,直接返回
if(size > 0 && root.val <= arr.get(size - 1)) {
ans = true;
return;
}
arr.add(root.val);
dfs(root.right);
}
}