题目:
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
这是leetcode里面我遇到的一道比较有意思的题目,题目的意思非常清楚:输入一棵树,判断它是不是二叉搜索树。大家对二叉搜索树的概念应该非常熟悉,题目中也非常清楚地给出了判定二叉搜索树的几个条件(应该说是充分必要条件),看到这三个条件:节点的左子树只包含小于当前节点的数、节点的右子树只包含大于当前节点的数、所有左子树和右子树自身必须也是二叉搜索树,第一反应肯定是想到使用递归来做,也应该是一道比较简单的题目。
当我仔细去思考如何去构造递归过程的时候发现这道题其实并没有我想象得那么容易,当判断判断节点的左子树只包含小于当前节点的数与节点的右子树只包含大于当前节点的数这两个条件时,需要分别知道左子树的最大值和右子树的最小值,但是仅仅知道这两个值还是不够的,当前节点作为子树节点在递归回溯的过程中还需要知道该节点下面所有节点中的最大值和最小值,因此在递归的过程中还是需要知道该节点的左子树的最小值和右子树的最大值。
如果用l_min、l_max、r_min、r_max分别表示该节点的左子树中的最小值、左子树中的最大值、右子树中的最小值、右子树中的最大值,在递归返回的过程中,如果该节点的左子树和右子树满足条件进一步推断该节点也是一颗二叉搜索树,每个节点的左子树的最小值和右子树的最大值就会成为该节点为根的子树中的最小值和最大值,这一点时递归过程中的关键。
代码如下:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
int a,b;
return isvalid(root,a,b);
}
private:
bool isvalid(TreeNode* root,int &max,int &min)//max传出root下最大值、min传出root下最小值
{
if(root==NULL)
{
//初始化当前节点中的最大值和最小值
max=INT_MIN;
min=INT_MAX;
return true;
}
int left_max,left_min,right_max,right_min;//左子树的最大值、左子树的最小值、右子树的最大值、右子树的最小值
//递归判断左子树和右子树是否是二叉搜索树,并通过引用分别传出左子树和右子树的最大值和最小值
if(isvalid(root->left,left_max,left_min)&&isvalid(root->right,right_max,right_min))
{
//分为四种情况
//右子树为空、左子树不为空
if(root->left==NULL&&root->right!=NULL)
{
if(root->val<right_min)
{
min=root->val;//传出最小值和最大值
max=right_max;
return true;
}
else
return false;
}
//右子树位空、左子树不为空
if(root->right==NULL&&root->left!=NULL)
{
if(root->val>left_max)
{
min=left_min;
max=root->val;
return true;
}
else
return false;
}
//左子树、右子树都为空
if(root->left==NULL&&root->right==NULL)
{
min=root->val;
max=root->val;
return true;
}
//左子树、右子树都不为空
if(root->val>left_max&&root->val<right_min)
{
min=left_min;
max=right_max;
return true;
}
else
return false;
}
else
return false;
}
};