附上原题:
一棵二叉树如果属于二叉搜索树,必须满足三个条件:
- 左子树的所有节点都小于根节点
- 右子树的所有节点都大于根节点
- 左子树跟右子树同时也是二叉搜索树
我们看到第三条定义本身也是递归的。那根据该定义,如何来检验一棵二叉树到底是不是二叉搜索树?
首先,如果左子树跟右子树其中有一棵不是二叉搜索树的情况下,则该二叉树必定不属于二叉搜索树。那如何判定左子树跟右子树是不是二叉搜索树呢?好像我们又回到了原来的问题。看一种比较特殊的情况,假如说二叉树只有一个节点,即没有左子树也没有右子树,那么它必定属于二叉搜索树。如果左子树不存在,右子树存在的情况下呢?此时左子树就不需要再考虑是不是二叉搜索树了。反之右子树不存在的情况下也一样。
因此,对于这个问题,我们可以先考虑一棵二叉树的叶子节点。比如说有这样一棵二叉树:
左子树跟右子树都属于叶子节点,必定是二叉搜索树。在符合第三条定义的情况下,再来看前面两条。根节点大于左子树的所有节点,难道要用根节点与左子树的每个节点去比较?我们换个角度想,是不是只要根节点大于左子树最大的节点就可以了?因为在确定左子树属于二叉搜索树的情况下,只要沿着左子树的根节点出发一直向右即可找到最大的节点。同样,对于第二个定义,根节点只需小于右子树最小的节点即可。
我们运用了一种“分而治之”的思想,先考虑最基本的情况,即叶子节点,再得到左子树和右子树同时为二叉搜索树的前提下,用当前树的根节点去比较左子树的最大值和右子树的最小值。然后算法不断得往上回溯,最终得出整棵树是不是二叉搜索树。
根据以上分析,我们设计出如下算法:
- 如果当前节点即不存在左子树也不存在右子树,直接返回true。
- 如果当前节点存在左子树,则验证左子树是否为二叉搜索树,若是,并且根节点大于左子树的最大值,则左子树验证通过。若左子树不存在,同样验证通过。
- 如果当前节点存在右子树,则验证右子树是否为二叉搜索树,若是,并且根节点小于右子树的最小值,右子树验证通过。若右子树不存在,同样验证通过。
- 若左子树跟右子树同时验证通过,返回true;否则,返回false。
看代码:
class Solution {
public:
bool isValidBST(TreeNode* root) {
bool isValidLeft = true;
bool isValidRight = true;
if (root) {
int val = root->val;
if (root->left) {
//验证左子树是否为二叉搜索树,并且根节点大于左子树的最小值
isValidLeft = isValidBST(root->left) && (val > fetchMaxNodeVal(root->left));
}
if (root->right) {
isValidRight = isValidBST(root->right) && (val < fetchMinNodeVal(root->right));
}
if (!root->left && !root->right) {
return true;
}
}
return isValidLeft && isValidRight;
}
private:
//获取二叉树的最大值
int fetchMaxNodeVal(TreeNode *node) {
while (node->right) {
node = node->right;
}
return node->val;
}
int fetchMinNodeVal(TreeNode *node) {
while (node->left) {
node = node->left;
}
return node->val;
}
};