给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左
子树
只包含 小于 当前节点的数。 - 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3] 输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
- 树中节点数目范围在
[1, 10^4]
内 -2^31 <= Node.val <= 2^31 - 1
解法一:递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left),
* right(right) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
return istrue(root, LONG_MIN, LONG_MAX); // 使用 LONG_MIN 和 LONG_MAX
}
bool istrue(TreeNode* root, long min, long max) {
if (root == nullptr)
return true;
// 使用严格比较
if (root->val <= min || root->val >= max)
return false;
return istrue(root->left, min, root->val) &&
istrue(root->right, root->val, max);
}
};
函数isValidBST
接受一个二叉树的根节点作为参数,并调用辅助函数istrue
来进行验证。辅助函数istrue
接受三个参数:当前节点root
,当前节点的允许的最小值min
和最大值max
。
首先,如果当前节点为空,说明当前子树是有效的二叉搜索树,返回true
。然后,判断当前节点的值是否在允许的范围内,如果不在范围内,说明当前子树不是有效的二叉搜索树,返回false
。
接下来,递归调用istrue
函数来判断左子树和右子树是否也是有效的二叉搜索树。对于左子树,允许的最小值是min
,最大值是当前节点的值;对于右子树,允许的最小值是当前节点的值,最大值是max
。
最后,将左子树和右子树的结果进行与操作,如果有任何一个子树不是有效的二叉搜索树,整个树就不是有效的二叉搜索树。
因为每个节点都需要比较一次,所以时间复杂度是O(n),其中n是二叉树的节点数。空间复杂度是O(n),因为在递归的过程中需要使用系统栈来保存递归调用的上下文。
解法二:中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left),
* right(right) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
stack<TreeNode*> stack;
long long inorder = (long long)INT_MIN - 1;
while (root != nullptr || !stack.empty()) {
while (root) {
stack.push(root);
root = root->left;
}
root = stack.top();
stack.pop();
if (root->val <= inorder)
return false;
inorder = root->val;
root = root->right;
}
return true;
}
};
这次使用了迭代的方式,使用了一个栈来辅助遍历二叉树。同时,定义了一个变量inorder
来记录中序遍历的前一个节点的值,初始值为INT_MIN-1,使用long long类型是为了避免整数溢出。
首先,将根节点入栈。然后,一直向左遍历,将遇到的所有节点都入栈,直到没有左子节点。此时,从栈中弹出一个节点,此节点为当前子树的最左下角的节点。
判断当前节点的值是否小于等于inorder
,如果是,说明当前子树不是有效的二叉搜索树,返回false
。更新inorder
为当前节点的值。
将当前节点的右子节点设为当前节点,进行下一次循环。如果当前节点为空且栈为空,说明遍历完成,整个树是有效的二叉搜索树,返回true
。
这种方法使用了中序遍历的方式,通过比较前一个节点和当前节点的值的大小关系来判断是否为有效的二叉搜索树。
由于每个节点都需要遍历一次,时间复杂度是O(n),其中n是二叉树的节点数。栈的空间复杂度为O(h),其中h是二叉树的高度,最坏情况下为O(n)。