LintCode第95题-验证二叉查找树

题目:

描述

给定一个二叉树,判断它是否是合法的二叉查找树(BST)

一棵BST定义为:

  • 节点的左子树中的值要严格小于该节点的值。
  • 节点的右子树中的值要严格大于该节点的值。
  • 左右子树也必须是二叉查找树。
  • 一个节点的树也是二叉查找树。
  • 样例 1:

    输入:

     
    tree = {-1}

    输出:

     
    true

    解释:

     
    二叉树如下(仅有一个节点):
            -1
    这是二叉查找树。

    样例 2:

    输入:

     
    tree = {2,1,4,#,#,3,5}

    输出:

     
    true

    解释:

     
            二叉树如下:
              2
             / \
            1   4
               / \
              3   5
    这是二叉查找树。

思路:

该题的易错点在于 直接判断左子树的节点值小于根节点值 右子树的节点值大于根节点的值

类似于这种代码 

if (root.left != null && root.val <= root.left.val) flag = false;
if (root.right != null && root.val >= root.right.val) flag = false;
这只能判断 当前节点是否满足和直接子节点的关系

但BST(二叉排序树)是整棵左子树的所有值都要 < 当前节点值,整棵右子树所有值都要 > 当前节点值

    5
   / \
  3   8
     /
    4
比如这样

节点 8 的左孩子是 4,但 4 < 5,这个不合法 这是由于忽略了整个树的结构导致的

而通过中序遍历判断序列是否递增可以很好的满足该二叉树是否为二叉排序树 即将树的问题转换为了类似链表的问题 

代码如下:

/**

 * Definition of TreeNode:

 * public class TreeNode {

 *     public int val;

 *     public TreeNode left, right;

 *     public TreeNode(int val) {

 *         this.val = val;

 *         this.left = this.right = null;

 *     }

 * }

 */

public class Solution {

    /**

     * @param root: The root of binary tree.

     * @return: True if the binary tree is BST, or false

     */

    public boolean flag=true;

    TreeNode prevTreeNode = null; // 上一个访问的节点

    public boolean isValidBST(TreeNode root) {

        // write your code here

       // 中序遍历

        inOrder(root);

        return flag;

    }

    public void inOrder(TreeNode treeNode)

    {

        if (treeNode == null || !flag)

        {

            return;

        }

        inOrder(treeNode.left);

        // 访问当前节点:中序递增性判断

        if (prevTreeNode != null && treeNode.val <= prevTreeNode.val) {

            flag = false;

            return;

        }

        prevTreeNode=treeNode;

        inOrder(treeNode.right);

    }

}

由此会引发出两个问题:

第一个问题:遍历的时候是否可以用先序遍历或者是后序遍历 理论上是可以的 但实际上很麻烦 

后序遍历和先序遍历都需要设置上下限的辅助来限制左右子树

而中序遍历的优势在于

1.只需要比较当前值是否大于上一个访问过的值 prev.val

2.不用显式维护上下限

第二个问题:为什么中序遍历不需要判断左右两边的大小 只需要当前节点与上一个节点作比较呢

原因是二叉搜索树的中序遍历,访问节点的值是严格递增的

每次当前节点 root 的值,都应该大于 上一个访问过的节点 prev 的值

当然也可以先序遍历或者后序遍历 

以先序遍历为准:

思路是:先序遍历+限定上下边界判断

处理节点前要判断当前节点是否在限定区间范围内 如果是的话继续遍历 否则不符合要求

如下图所示:

        10
       /  \
      5    15
          /  \
         12   20

节点 允许值范围
10 (-∞, ∞)
5 (-∞, 10) ← 左子树:最大值限制为父节点
15 (10, ∞) ← 右子树:最小值限制为父节点
12 (10, 15) ← 在父节点的区间基础上收紧
20 (15, ∞)

根节点把自己的值,作为“边界”传给左右子树

左子树的上界是根节点值;

右子树的下界是根节点值;

每一层都只判断当前节点是否在 [min, max] 范围内

 代码如下:

/**
 * Definition of TreeNode:
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left, right;
 *     public TreeNode(int val) {
 *         this.val = val;
 *         this.left = this.right = null;
 *     }
 * }
 */

public class Solution {
    /**
     * @param root: The root of binary tree.
     * @return: True if the binary tree is BST, or false
     */
    public boolean flag = true;

    public boolean isValidBST(TreeNode root) {
        checkTreeNodeRange(root, null, null);
        return flag;
    }

    // 使用先序遍历 + 区间限制判断
    public void checkTreeNodeRange(TreeNode treeNode, Integer min, Integer max) {
        if (treeNode == null || !flag) return;

        // 判断当前节点是否在合法区间内
        if ((min != null && treeNode.val <= min) || (max != null && treeNode.val >= max)) {
            flag = false;
            return;
        }

        // 递归左子树:最大值为当前节点值
        checkTreeNodeRange(treeNode.left, min, treeNode.val);
        // 递归右子树:最小值为当前节点值
        checkTreeNodeRange(treeNode.right, treeNode.val, max);
    }
}
 

 

你可能感兴趣的:(数据结构,算法,二叉排序树,递归)