LeetCode:98. 验证二叉搜索树

234 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

 98. 验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:

输入:
2
/ \      1   3
输出: true
示例 2:

输入:
5
/ \    1   4
/ \      3   6
输出: false
解释:    输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。

链接:leetcode-cn.com/problems/va…

方法1 递归:

思路:

引入上下边界(参考LeetCode大神题解

对于树的每个节点 val ,设其上下边界 low , high。(用 long 防止 INT_MAX 溢出 )
判断根结点时,须满足 low < val < high ,否则返回 false
判断左节点时,仅 上界 变化 ( 新上界为 high 与 val 较小值。又因 val 必小于 high,故新上界为 val )
判断右节点时,仅 下界 变化 ( 同理,新下界为 val )

时间复杂度: O(n) ****在递归调用的时候二叉树的每个节点最多被访问一次,因此时间复杂度为 O(n)

空间复杂度: O(n)   其中 n 为二叉树的节点个数。递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,即二叉树的高度。最坏情况下二叉树为一条链,树的高度为 n ,递归最深达到 n 层,故最坏情况下空间复杂度为 O(n) 

// 递归:
bool isValidBST(TreeNode* root) {
    return recurse(root, LONG_MIN, LONG_MAX);
}

bool recurse(TreeNode* root, long long low, long long high) { // low和hight:上届和下界
    // 递归终止条件
    if (root == NULL)                             // 空树也是特殊的二叉搜索树
        return true;
    if (root->val <= low || root->val >= high)      // 如果当前节点值不在上下界内,false
        return false;
    // 下探到下一层
    return recurse(root->left, low, root->val) && recurse(root->right, root->val, high);

    // error:不能拆开写,左子树和右子树应当同时判断,而不是先后关系:
    // return recurse(root->left, low, root->val);   // 左子树:上界为当前节点值(当前节点的左子树都小于当前节点值),下界不动
    // return recurse(root->right, root->val, high); // 右子树:下界为当前节点值(当前节点的右子树都大于当前节点值),上届不动
}

方法2 中序遍历:

思路: 二叉搜索树的中序遍历为升序排列,故比较遍历到的当前节点与前一个节点的值是否满足:Val前 <  val当前

时间复杂度: O(n) ****其中 n 为二叉树的节点个数。二叉树的每个节点最多被访问一次

空间复杂度: O(n)   其中 n 为二叉树的节点个数。栈最多存储 n 个节点,因此需要额外的 O(n) 的空间

// 1.根据中序遍历:前一个节点值应小于后一个节点值
bool isValidBST(TreeNode* root) { 
    stack<TreeNode*> st;
    // INT_MIN是先转换成long long类型然后再减去1的,也就是比所有的测试用例的值都要小了(测试用例的最小值是INT_MIN)
    // 中序遍历的结果应该是递增的,所以这样没错,左边一直小于右边就是true,包括最左边的数,它的左边肯定是最小值
    // 保留节点的上界与下界(因为当前节点值应大于左子树值,而不仅是左节点;当前节点值应大于右子树值,而不仅是右节点)
    long long leftChildVal = (long long)INT_MIN - 1;    // 左孩子节点

    while (root != NULL || !st.empty())
    {
        while (root != NULL)
        {
            st.push(root);
            root = root->left;
        }
        if (!st.empty())
        {
            root = st.top();
            if (root->val <= leftChildVal) // // 若当前根节点值大于其右孩子,不满足二叉搜索树中序遍历值递增性质
                return false;
            leftChildVal = root->val;
            st.pop();
            root = root->right;
        }
    }
    return true;
}