二叉查找树(5) - 判断一棵二叉树是否为BST

目录

1.左右孩子递归检查

2.左右子树递归

3.限定值范围递归

4.使用中序遍历


在本系列的第一篇文章中,已经介绍过了二叉查找树(BST)的一些性质:

  • 节点的左子树中任意节点值小于根节点

  • 节点的右子树中任意节点值大于根节点

  • 左右子树都必须是二叉查找树,不允许存在重复节点

基于上面的这些性质,自然的就得到了这种判断方式:树中的每个节点都有一个特定的值。

假设树的节点定义为:

struct Node {
    int key;
    Node* left;
    Node* right;
};

1.左右孩子递归检查

对于每个节点,检测它的左孩子节点是否小于它,且右孩子节点是否大于它。

bool isBST(Node* node) {
    if (node == NULL)
        return true;

    //如果左孩子大于根节点,则不是BST
    if (node->left != NULL && node->left->key > node->key)
        return false;

    //如果右孩子节点小于根节点,则不是BST
    if (node->right != NULL && node->right->key < node->key)
        return false;

    //递归的判断
    if (!isBST(node->left) || !isBST(node->right))
        return false;

    //检测完毕,所有条件通过,则是BST
    return true;
}

此方法虽然实现简单,但是,却是错误的。如下面例子所示,节点4处于根节点3的左子树中,但是函数检测到这棵树是BST。
        3
      /   \
    2     5
  /  \
1    4

2.左右子树递归

对于每个节点,检测左子树中的最大值是否比它小,而右子树的最小值比它大。

//如果是BST,则返回true
bool isBST(Node* node) {
    if (node == NULL)
        return true;

    //如果左子树最大值大于根节点,则返回false
    if (node->left != NULL && maxValue(node->left) > node->key)
        return false;

    //如果右子树最小值小于根节点,则返回false
    if (node->right != NULL && minValue(node->right) < node->key)
        return false;

    //递归判断
    if (!isBST(node->left) || !isBST(node->right))
        return(false);

    //所有检测都通过,是BST
    return true;
}

其中,maxValue以及minValue函数,分别返回一颗非空树中的最大值和最小值。

此方法虽然正确,但是效率比较低,会多次重复的遍历树中的众多节点。

3.限定值范围递归

方法2因为要重复的遍历树中的部分数据,效率比较低,更好的方案是每个节点只遍历一次。

方法3的巧妙之处在于限定了子树中节点值的范围,从而每个节点只需访问一次。节点值的初始范围可限定为INT_MIN以及INT_MAX。

//判断是否为BST
bool isBST(Node* node)
{
    return(isBSTUtil(node, INT_MIN, INT_MAX));
}
//如果是一颗二叉查找树,且值范围在[min,max],则返回true
bool isBSTUtil(Node* node, int min, int max)

代码实现如下。

#include <iostream>

struct Node {
    int key;
    Node* left;
    Node* right;
};

bool isBSTUtil(Node* node, int min, int max);

//判断是否为BST
bool isBST(Node* node) {
    return(isBSTUtil(node, INT_MIN, INT_MAX));
}

//如果是一颗二叉查找树,且值范围在[min, max],则返回true
bool isBSTUtil(Node* node, int min, int max) {
    //空树也是BST
    if (node == NULL)
        return true;

    //如果节点值违反了最大/最小约束条件,则不是BST
    if (node->key < min || node->key > max)
        return false;

    //递归检查子树
    return  isBSTUtil(node->left, min, node->key - 1) &&
        isBSTUtil(node->right, node->key + 1, max);
}

// 创建一个新的BST节点
Node* createNewNode(int item) {
    Node* temp = new Node;
    temp->key = item;
    temp->left = temp->right = NULL;
    return temp;
}

int main() {
    /* tree1的定义
            4
          /   \
         2     5
        / \
       1   3
    */
    Node* root = createNewNode(4);
    root->left = createNewNode(2);
    root->right = createNewNode(5);
    root->left->left = createNewNode(1);
    root->left->right = createNewNode(3);

    if (isBST(root))
        std::cout << "tree1 is BST\n";
    else
        std::cout << "tree1 is not BST\n";

    /* tree2的定义
           4
         /   \
        2     5
       /     /
      1     3
    */
    root = createNewNode(4);
    root->left = createNewNode(2);
    root->left->left = createNewNode(1);
    root->right = createNewNode(5);
    root->right->left = createNewNode(3);

    if (isBST(root))
        std::cout << "tree2 is BST\n";
    else
        std::cout << "tree2 is not BST\n";

    return 0;
}

运行结果:
tree1 is BST
tree2 is not BST

时间复杂度:O(n)
辅助空间:如果不考虑函数调用栈的大小,则为O(1), 否则为O(n)

4.使用中序遍历

1) 对树进行中序遍历,将结果保存在temp数组中。
3) 检测temp数组中元素是否为升序排列。如果是,则这棵树为BST。
时间复杂度:O(n)
方法4还可以进一步的优化,我们可以避免使用这个额外的数组。在中序遍历时,可以保存前驱节点,如果当前节点小于前驱节点,则这棵树不是BST。

#include <iostream>

struct Node {
    int key;
    Node* left;
    Node* right;
};

//判断是否为BST
bool isBST(Node* root) {
    static Node* prev = NULL;

    // 中序遍历这棵树,并保存前驱节点至prev中
    if (root) {
        if (!isBST(root->left))
            return false;

        // 检测节点值的合法性
        if (prev != NULL && root->key <= prev->key)
            return false;

        prev = root;

        //右子树
        return isBST(root->right);
    }

    return true;
}

// 创建一个新的BST节点
Node* createNewNode(int item) {
    Node* temp = new Node;
    temp->key = item;
    temp->left = temp->right = NULL;
    return temp;
}

int main() {
    /* tree1的定义
            4
          /   \
         2     5
        / \
       1   3
    */
    Node* root = createNewNode(4);
    root->left = createNewNode(2);
    root->right = createNewNode(5);
    root->left->left = createNewNode(1);
    root->left->right = createNewNode(3);

    if (isBST(root))
        std::cout << "tree1 is BST\n";
    else
        std::cout << "tree1 is not BST\n";

    /* tree2的定义
           4
         /   \
        2     5
       /     /
      1     3
    */
    root = createNewNode(4);
    root->left = createNewNode(2);
    root->left->left = createNewNode(1);
    root->right = createNewNode(5);
    root->right->left = createNewNode(3);

    if (isBST(root))
        std::cout << "tree2 is BST\n";
    else
        std::cout << "tree2 is not BST\n";

    return 0;
}

运行结果:
tree1 is BST
tree2 is not BST

更多参考:
http://en.wikipedia.org/wiki/Binary_search_tree
http://cslibrary.stanford.edu/110/BinaryTrees.html

  • 18
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值