LeetCode #98 验证搜索二叉树

LeetCode #98 判断搜索二叉树

本文记录了完成LeetCode #98 判断搜索二叉树中的一些心得。

题目如下:

给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:

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

示例 1:

输入:
2
/
1 3
输出: true

示例 2:

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


第一次 失败的测试

因为我想用前序遍历试一下能不能写好这道题,于是打算在遍历中判断子树是否符合二叉搜索树:

第一次的代码

bool isValidBST(struct TreeNode* root) {
    if (root == NULL || (root->left == NULL && root->right == NULL))
        return true;
    if (root->left != NULL)
        if (root->left->val >= root->val)
            return false;
    if (root->right != NULL)
        if (root->right->val <= root->val)
          return false;
    return (isValidBST(root->right) && isValidBST(root->left));
}

不出意外的解答错误
因为我只分析了子树中的情况,没有考虑各元素与根节点的大小关系。


第二次 比较成功的测试:

因为我想用前序遍历试一下能不能写好这道题,于是参考了网上某位的解答思路:

  • 先遍历左子树,查找是否有元素大于根节点;
  • 再遍历右子树,同上;
  • 最后按照第一次的方式,检查各个子树中的关系。

这里我原本是只遍历了所有元素与最初的那个根节点的大小关系,没有遍历与其他根节点的关系。
之后我在CheckLeftCheckRight里面加上了递归遍历所有元素与根节点的大小关系,这样之后最后一条检查各个子树中的关系就显得有些多余了,因为我把那段注释掉之后代码仍然测试成功。

第二次的代码

bool CheckLeft(struct TreeNode* root, struct TreeNode* tmp);
bool CheckRight(struct TreeNode* root, struct TreeNode* tmp);
bool CheckTree(struct TreeNode* root);
bool isValidBST(struct TreeNode* root)
{
    if (root == NULL || (root->left == NULL && root->right == NULL))
        return true;
    bool tmpLeft = true, tmpRight = true;
    if (root->left != NULL)
    {
        if (!CheckLeft(root, root->left))
            return false;
    } 
    
    if (root->right != NULL)
    {
        if (!CheckRight(root, root->right))
            return false;
    }
        
    return true;
}

bool CheckLeft(struct TreeNode* root, struct TreeNode* tmp)
{
    bool tmpLeft = true, tmpRight = true;
    if (tmp->val >= root->val)
        return false;
    
    if (tmp->left != NULL)
        tmpLeft = (CheckLeft(root, tmp->left) && CheckLeft(tmp, tmp->left));
    else
        tmpLeft = true;
    
    if (tmp->right != NULL)
        tmpRight = (CheckLeft(root, tmp->right) && CheckRight(tmp, tmp->right));
    else
        tmpRight = true;
    
    return (tmpLeft && tmpRight);
}

bool CheckRight(struct TreeNode* root, struct TreeNode* tmp)
{
    bool tmpLeft = true, tmpRight = true;
    if (tmp->val <= root->val)
        return false;
    
    if (tmp->left != NULL)
        tmpLeft = (CheckRight(root, tmp->left) && CheckLeft(tmp, tmp->left));
    else
        tmpLeft = true;
    
    if (tmp->right != NULL)
        tmpRight = (CheckRight(root, tmp->right) && CheckRight(tmp, tmp->right));
    else
        tmpRight = true;
    
    return (tmpLeft && tmpRight);
}

这段代码总体上是成功的,但是执行时间过长。
于是我放弃了用前序遍历写这道题的尝试,转向中序遍历。


第三次 中序遍历的成功

了解到 “用中序遍历二叉搜索树输出的数列是递增数列” 后,我就开始尝试用中序遍历写出这道题。

这是第三次的代码

int counter;
void Order(struct TreeNode* root, int* arr);
bool isValidBST(struct TreeNode* root)
{
    if (root == NULL || (root->left == NULL && root->right == NULL))
        return true;
    int i, arr[10240] = {0};
    counter = 0;
    Order(root, arr);
    for (i = 1; i < counter; i++)
    {
        if (arr[i] <= arr[i-1])
            return false;
    }
    return true;
}

void Order(struct TreeNode* root, int* arr)
{
    if (root->left != NULL)
        Order(root->left, arr);
    
    arr[counter] = root->val;
    counter++;
    
    if (root->right != NULL)
        Order(root->right, arr);
    return;
}

由于思路比较成熟,所以这次是一次过。
由于需要一个变量实时记录录入的值得数量,但受到C语言的限制不能用int&这样传递实参的方法,所以我设置了一个全局变量counter
先用Order把二叉搜索树中序遍历输出到arr,再循环查看是否有逆序的情况,有则false
由于OJ过题的时间时长时短,而我的代码居然跑了20ms,于是我再跑了一次,跑到8ms


第四次 改进的测试

我是在第三次尝试过了之后来写的这篇文章,写完已经是凌晨三点多,于是去洗澡。洗澡的时候想着20ms的成绩不太对劲,思考怎么提升比较好,于是想到可以把遍历完输出再比较改成遍历时比较,就有了接下来的代码:

这是第四次的代码

int counter;
int Order(struct TreeNode* root, int* arr);
bool isValidBST(struct TreeNode* root)
{
    if (root == NULL || (root->left == NULL && root->right == NULL))
        return true;
    int i, arr[10240] = {0};
    counter = 0;
    if (Order(root, arr) == 1)
        return false;
    else
        return true;
}

int Order(struct TreeNode* root, int* arr)
{
    if (root->left != NULL)
        if (Order(root->left, arr) == 1)
            return 1;
    
    arr[counter] = root->val;
    if (counter > 0)
        if (arr[counter] <= arr[counter - 1])
            return 1;
    counter++;

    if (root->right != NULL)
        if (Order(root->right, arr) == 1)
            return 1;
    return 0;
}

流程是这样的:

1. 当输出第二个数时,开始与前一个进行大小比较,大则继续,小则返回1
2. 递归中的函数发现有1返回时,停止并退出递归,否则继续递归并返回0;
3. 函数isValidBST中发现调用的函数Order返回1时,返回false,否则返回true

这次测试通过的时间是4ms,是本题最快的时间之一。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值