Check Whether a tree is a BST tree

判断一棵二叉树是否为二叉搜索树(BST)是招聘面试的时候经常会遇到的一个问题。 这需要我们对BST的定义吃透。

首先明确BST的如下几个特点:

(1)对于每一个节点, 这个节点的左边子树的所有节点都小于这个节点

(2)对于每个节点, 这个节点的右边子树的所有节点均大于这个节点

以上的定义是递归的。

编写程序的时候, 很容易犯如下的错误:

Method1(simple but wrong):For each node, check if left node of it is smaller than the node and right node of it is greater than the node.

bool isBST(Node* root) 
{ 
  if (root == NULL) 
    return true; 
     
  /* false if left is > than node */
  if (root->left != NULL && root->left->data > root->data) 
    return false; 
     
  /* false if right is < than node */
  if (root->right != NULL && root->right->data < root->data) 
    return 0; 
   
  /* false if, recursively, the left or right is not a BST */
  if (!isBST(root->left) || !isBST(root->right)) 
    return false; 
     
  /* passing all that, it's a BST */
  return true; 
}

上述代码是错误的。 因为这个判断函数对于如下的一棵非BST树也会误判为是一棵BST(因为对于节点data 为3,4 在3的左子树, 但是却大于3, 所以不是BST ):


 Method2(correct but inefficient ):

/* we can also find the maximum of the left subtree, if it is less than or equal to the root, then the entire left subtree is less or equal to the root*/
bool IsSubtreeLesser(Node* root, int value) {
   if(root == NULL) return true; //base case
   if(root -> data <= value // data in the root is less than or equal to the given value
      && IsSubtreeLesser(root -> left, value) //recursive check if left and right subtree of the current root have less value or not
      && IsSubtreeLesser(root -> right, value))
         return true;// if all the 3 conditions are true, return true, otherwise false.
   else
      return false;
}

// can also find the minmum of the right subtree, if >, okay
bool IsSubtreeGreater(Node* root, int value) {
   if(root == NULL) return true; //base case
   if(root -> data > value
      && IsSubtreeGreater(root -> left, value)
      && IsSubtreeGreater(root -> right, value))
         return true;
   else
      return false;
}

bool IsBinarySearchTree(Node* root) {
   //return true if BST, otherwise, return false
   if(root == NULL) return true; //base case
   if(IsSubtreeLesser(root -> left, root -> data) //root -> data is data in the root
     && IsSubtreeGreater(root ->right, root -> data)
     && IsBinarySearchTree(root -> left) // recursive calls to check whether the left subtree is a BST
     && IsBinarySearchTree(root -> right)) // recursive calls to check whether the right subtree is a BST
        return true;// if all  these 4 conditions are true, then return true
   else
      return false;
}

下面我们分析上述程序是如何运行的:

例如对于下图的一棵树:

我们假设上述树的各个节点在内存中的地址标注如下:

为了判断上述树是否为BST, 我们需要调用IsBinarySearchTree(root)函数(下面图片中函数名用简写)

此时在函数IsBinarySearchTree内部, 运行到第二个if, 调用了IsSubtreeLesser(root->left, root -> data)函数, 用于判断节点的左子树(此时的根结点为root-> left)是否小于这个节点的数据:

 

所以调用ISl函数:

由于4 < 7, 所以第一个condition 为True。 然后判断第二个条件ISL:

接下来,继续递归调用,  同样1 < 7, 又第二个条件, 递归调用ISL。

此时返回True。

到达上级调用(frame):

然后, 在这一级, 判断第三个条件, 依然调用ISL函数:

此时, 由于root = NULL, 所以返回True, 由于此时三个条件都返回True, 继续回到上上一级:

所以程序到达这一级:

等等。。。。。。。。。。, 依照这样可以分析。

 

但是不难发现, 上述的算法的时间复杂度很大的。 因为树的每一个节点都会参与比较若干次。

Method2: 对于每一个左子树, 找这个节点左子树的最大的值, 只要这个做大的值小于这个节点的值, 就可以。 然后在找右子树的最小值 , 只要这个最小值小于这个节点值, 我们就说左子树的所有节点都大于这个节点。 当两个条件都满足的时候, 依次递归下去, 最终我们说这个树是个BST。

Mehod3: 下面说一个比较高效的办法:

我们的做法是定义一个permissible range node, 而且节点中的数据必须在这个range 范围内, 当所有的节点数据都满足的时候, 我们才敢说Tree是BST。

例如, 从根节点开始判断: 我们定义根节点的允许范围在(-∞,+∞)。 假如我们发现最终根节点的数据是7, 对于根节点的左孩子,  允许的范围是(-∞, 7), 右孩子的permissible range为: (7, +∞)。 依次进行下去:

例如对于下面的树, 要是一个BST, 则各个节点的permissible range的标记如下:

C++ 程序如下:

bool IsBstUtil(Node* root, int minValue, int maxValue) {
   if(root == NULL) return true;
   if(root -> data > minValue
      && root -> data < maxValue
      && IsBstUtil(root -> left, minValue, root -> data)
      && IsBstUtil(root -> right, root -> data, maxValue))
      return true;
   else
      return false;
}

bool IsBinarySearchTree2(Node* root) {
   return IsBstUtil(root, INT_MIN, INT_MAX);
   }
// another solution to check is to do in order traversal
// of the Tree, if the tree is a BST, you will get your data in sorted order, 知道排列顺序后, 每一次只需要保存遍历的前一个的值,
// 与当前值比较, 一旦出现不满足的情况, 则不是BST

上述算法的时间复杂度为O(n)
Method4:  给定一棵树, 我们也可以通过inorder Traversal 的办法遍历玩这课树, 将每个树的数据存储在一个list中, 如过我们得到的的数据是排好序的数据, 则说明这课树时BST. 或者直接比较, 记住比较的大小顺序, 只需几下前一个和当前的值, 然后比较即可, 一旦不满足, 说明就不是BST。

整个程序如下:

#include <iostream>
using namespace std;

#define INT_MAX 999999
#define INT_MIN -999999

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


bool IsSubtreeLesser(Node* root, int value) {
   if(root == NULL) return true; //base case
   if(root -> data <= value 
      && IsSubtreeLesser(root -> left, value) 
         return true;
      return false;
}


bool IsSubtreeGreater(Node* root, int value) {
   if(root == NULL) return true; //base case
   if(root -> data > value
      && IsSubtreeGreater(root -> left, value)
      && IsSubtreeGreater(root -> right, value))
         return true;
   else
      return false;
}

bool IsBinarySearchTree(Node* root) {
   //return true if BST, otherwise, return false
   if(root == NULL) return true; //base case
   if(IsSubtreeLesser(root -> left, root -> data) 
     && IsSubtreeGreater(root ->right, root -> data)
     && IsBinarySearchTree(root -> left) 
     && IsBinarySearchTree(root -> right)) 
        return true;
   else
      return false;
}

//more efficient way to check whether a binary
// tree is a binary search tree

bool IsBstUtil(Node* root, int minValue, int maxValue) {
   if(root == NULL) return true;
   if(root -> data > minValue
      && root -> data < maxValue
      && IsBstUtil(root -> left, minValue, root -> data)
      && IsBstUtil(root -> right, root -> data, maxValue))
      return true;
   else
      return false;
}

bool IsBinarySearchTree2(Node* root) {
   return IsBstUtil(root, INT_MIN, INT_MAX);
   }



//Function to visit nodes in Inorder
void Inorder(Node *root) {
	if(root == NULL) return;

	Inorder(root->left);       //Visit left subtree
	cout << root->data << " ";  //Print data
	Inorder(root->right);      // Visit right subtree
}


Node* Insert(Node *root,char data) {
	if(root == NULL) {
		root = new Node();
		root->data = data;
		root->left = root->right = NULL;
	}
	else if(data <= root->data)
		root->left = Insert(root->left,data);
	else
		root->right = Insert(root->right,data);
	return root;
}

int main() {
	/*Code To Test the logic
	  Creating an example tree
                               5
			   / \
			  3   10
			 / \   \
			1   4   11
    */
	Node* root = NULL;
	root = Insert(root,5); root = Insert(root,10);
	root = Insert(root,3); root = Insert(root,4);
	root = Insert(root,1); root = Insert(root,11);
    if(IsBinarySearchTree(root))
       cout << "Using IsBinarySearchTree function: BST" << endl;
    else
       cout << "not BST" << endl;
    if (IsBinarySearchTree2(root))
       cout << "Using IsBinarySearchTree2 function: BST" << endl;
    else
       cout << "not BST " << endl;
	//Print Nodes in Inorder
	cout<<"Inorder: ";
	Inorder(root);
	cout<<"\n";
}



 

运行结果为:

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值