二叉查找树(5) - 判断一棵二叉树是否为BST
2015-06-16 00:25
369 查看
在本系列的第一篇文章中,已经介绍过了二叉查找树的一些性质:
节点的左子树中任意节点值小于根节点
节点的右子树中任意节点值大于根节点
左右子树都必须是二叉查找树,不允许存在重复节点。
基于上面的这些性质,自然的就得到了这种判断方式:树中的每个节点都有一个特定的值。
假设树的节点定义为:
3
/ \
2 5
/ \
1 4
//判断是否为BST
bool isBST(Node* node)
{
return(isBSTUtil(node, INT_MIN, INT_MAX));
}
//如果是一颗二叉查找树,且值范围在[min,max],则返回true
bool isBSTUtil(Node* node, int min, int max)
代码实现:
tree1 is BST
tree2 is not BST
时间复杂度: O(n)
辅助空间 : 如果不考虑函数调用栈的大小,则为O(1), 否则为O(n)
3) 检测temp数组中元素是否为升序排列。如果是,则这棵树为BST.
时间复杂度: O(n)
方法4还可以进一步的优化,我们可以避免使用这个额外的数组。在中序遍历时,可以保存前驱节点,如果当前节点小于前驱节点,则这棵树不是BST.
更多参考:
http://en.wikipedia.org/wiki/Binary_search_tree http://cslibrary.stanford.edu/110/BinaryTrees.html
节点的左子树中任意节点值小于根节点
节点的右子树中任意节点值大于根节点
左右子树都必须是二叉查找树,不允许存在重复节点。
基于上面的这些性质,自然的就得到了这种判断方式:树中的每个节点都有一个特定的值。
假设树的节点定义为:
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.
//判断是否为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; }
更多参考:
http://en.wikipedia.org/wiki/Binary_search_tree http://cslibrary.stanford.edu/110/BinaryTrees.html
相关文章推荐
- Adding an instance to a MEF container
- 题库类产品如何计算题目的难度值
- log4net内部调试开启
- varnish详解3
- 设计模式学习--抽象工厂模式
- Servlet的生命周期
- MySQL存储过程详解 mysql 存储过程 (
- Android Studio导入Project的方法
- c#模拟js escape方法(转)
- 新手玩Docker
- android sdk manager 代理设置(送给牛逼的)
- Mysql修改大小写敏感
- Java Logging: Basic Usage
- leetcode——Container With Most Water
- myeclipse svn插件安装
- keyboard rock
- Global build settings
- ACM 今年暑假不AC
- 正则表达式(2) EditPlus正则表达式替换字符串详解
- 二叉查找树(4) - 中序查找一个给定值的前驱以及后继