我们发现很多题使用前序、后序或者层次遍历都可以解决,但几乎没有中序遍历的。这是因为中序与前后序相比有不一样的特征,例如中序可以和搜索树结合在一起,但是前后序则不行。
二叉搜索树是一个很简单的概念,但是想说清楚却不太容易。简单来说就是如果一棵二叉树是搜索树,则按照中序遍历其序列正好是一个递增序列。比较规范的定义是:
若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
它的左、右子树也分别为二叉排序树。下面这两棵树一个中序序列是{3,6,9,10,14,16,19},一个是{3,6,9,10},因此都是搜索树:
搜索树的题目虽然也是用递归,但是与前后序有很大区别,主要是因为搜索树是有序的,就可以根据条件决定某些递归不必执行,这也称为“剪枝”。
1. 二叉搜索树中搜索特定值
LeetCode 700.给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。例如:
你应该返回如下子树:
本题看起来很复杂,但是实现非常简单,递归:
如果根节点为空 root == null 或者根节点的值等于搜索值 val == root.val,返回根节点。
如果 val < root.val,进入根节点的左子树查找 searchBST(root.left, val)。
如果 val > root.val,进入根节点的右子树查找 searchBST(root.right, val)。
这里提供两种遍历方法:
/**
* 递归方式实现
*
* @param root
* @param val
* @return
*/
public static TreeNode searchBST(TreeNode root, int val) {
if (root == null || val == root.val) return root;
return val < root.val ? searchBST(root.left,val) : searchBST(root.right,val);
}
如果采用迭代方式,也不复杂:
如果根节点不空 root != null 且根节点不是目的节点 val != root.val。
如果 val < root.val,进入根节点的左子树查找 root = root.left。
如果 val > root.val,进入根节点的右子树查找 root = root.right。
/**
* 迭代实现
*
* @param root
* @param val
* @return
*/
// public static TreeNode searchBST2(TreeNode root, int val) {
// while (root != null && val != root.val)
// root = val < root.val ? root.left : root.right;
// return root;
// }
public static TreeNode searchBST2(TreeNode root, int val) {
while (root != null && root.val != val) {
root = val < root.val ? root.left : root.right;
}
return root;
}
2. 验证二叉搜索树
LeetCode98.给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例:
示例1:
输入:root = [2,1,3]
输出:true
示例2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
我们根据题目的性质,我们可以进一步知道二叉搜索树 [中序遍历] 得到的值构成的序列一定是升序的,所以在中序遍历的时候,我们实时检查当前节点的值是否大于前一个节点的值即可。
/**
* 递归实现
*/
static long pre = Long.MIN_VALUE;
public static boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
// 如果左子树下某个元素不满足要求,则退出
if (!isValidBST(root.left)) {
return false;
}
// 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回 false;否则继续遍历。
if (root.val <= pre) {
return false;
}
pre = root.val;
// 访问右子树
return isValidBST(root.right);
}
另外,除了使用递归的方法,我们还可以使用迭代法进行求解,思路与上面类似:
public static boolean isValidBST2(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
if (root == null) {
return true;
}
double pre = Integer.MIN_VALUE;
while (!stack.isEmpty() || root != null){
while (root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
if (root.val <= pre){
return false;
}
pre = root.val;
root = root.right;
}
return true;
}