一、题目
235. 二叉搜索树的最近公共祖先、501. 二叉搜索树中的众数、98. 验证二叉搜索树、230. 二叉搜索树中第K小的元素、538. 把二叉搜索树转换为累加树、1008. 前序遍历构造二叉搜索树、99. 恢复二叉搜索树、1305. 两棵二叉搜索树中的所有元素
二、解析
2.1 二叉搜索树
- 结点左子树中所含节点的值 小于(等于) 当前节点的值
- 结点右子树中所含节点的值 大于(等于) 当前节点的值
- 左子树和右子树都是二叉搜索树
- 有没有等于主要看具体题目
2.2 思路
二叉搜索树的题目一般都是用它的两个性质,第一个就是节点大小,第二个就是中序遍历是个有序队列
2.2.1 节点大小
对于两个点的最近公共祖先这个点的值肯定是大于左节点小于右节点。所以从根节点开始遍历就行,当前节点的值大于这两个值,将当前节点移动到它的左子节点,反之亦然向右子树移动直到这个节点的值符合条件
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode ancestor = root;
while (true) {
if (p.val < ancestor.val && q.val < ancestor.val) {
ancestor = ancestor.left;
} else if (p.val > ancestor.val && q.val > ancestor.val) {
ancestor = ancestor.right;
} else {
break;
}
}
return ancestor;
}
}
如果该二叉树的左子树不为空,则左子树上所有节点的值均小于它的根节点的值; 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值,所以每个节点都有一个范围,如果超出这个范围则不是二叉搜索树,每次递归这个范围的上限或者下限会变,递归调用左子树上限就是这个root.val;调用右子树下限是root.val;
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode node, long lower, long upper) {
if (node == null) {
return true;
}
if (node.val <= lower || node.val >= upper) {
return false;
}
return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);
}
}
前序遍历的每一个节点都是中间节点root,那就能通过遍历一次得出当前root的左右节点(第一个大于root.val的是右节点小于的是左节点),然后递归得到每个节点的左右子树
class Solution {
public TreeNode bstFromPreorder(int[] preorder) {
TreeNode root=new TreeNode(preorder[0]);
int l=0;
mid(root,preorder,l,preorder.length);
return root;
}
public void mid(TreeNode node,int[] preorder,int l,int r){
int pr=0;
if(node==null)
return;
for (int i = l; i < r; i++) {
if(node.val>preorder[i]){
l=i;
node.left=new TreeNode(preorder[i]);
break;
}
}
for (int i = l; i < r; i++) {
if(node.val<preorder[i]){
pr=r;
r=i;
node.right=new TreeNode(preorder[i]);
break;
}
}
mid(node.left,preorder,l,r);
mid(node.right,preorder,r,pr);
}
}
2.2.2中序遍历
501. 二叉搜索树中的众数、98. 验证二叉搜索树、230. 二叉搜索树中第K小的元素、99. 恢复二叉搜索树、1305. 两棵二叉搜索树中的所有元素
#230. 二叉搜索树中第K小的元素
class Solution {
int i=0;
int r;
public int kthSmallest(TreeNode root, int k) {
mid(root, k);
return r;
}
public void mid(TreeNode root,int k){
if(root==null)
return;
mid(root.left,k);
i++;
if(i==k)
r=root.val;
mid(root.right, k);
}
}
538. 把二叉搜索树转换为累加树(右中左遍历)
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
if (root != null) {
convertBST(root.right);
sum += root.val;
root.val = sum;
convertBST(root.left);
}
return root;
}
}
这一部分没什么好说的,中序遍历后得到一个有序队列,然后在这基础上进行各种操作
不过有一种moris中序遍历可以节省空间,就是相当于有个前缀指针
public class MorrisTraversal {
public void inorderTraversal(TreeNode root) {
TreeNode current = root;
TreeNode mostRight = null;
while (current != null) {
// If there is no left child, print the current node and move to the right child.
if (current.left == null) {
System.out.println(current.val);
current = current.right;
} else {
// Find the most right child of the current node's left subtree
mostRight = current.left;
while (mostRight.right != null && mostRight.right != current) {
mostRight = mostRight.right;
}
// If the right pointer of the most right child is null, link it to the current node,
// and go to the left child.
if (mostRight.right == null) {
mostRight.right = current;
current = current.left;
} else {
// If the right pointer of the most right child is not null, it means we have
// already visited the left subtree. Print the current node and move to the right child.
System.out.println(current.val);
mostRight.right = null; // Remove the link.
current = current.right;
}
}
}
}
}