#include <climits>//头文件,这里要使用INT_MIN
class Solution {
public:
bool isValidBST(TreeNode* root) {
return dfs(root,INT_MIN, INT_MAX);
}
private:
bool dfs(TreeNode* root,int min,int max){
if(!root) return true;
if (root->val<=min||root->val>=max) {
return false;
}
return (dfs(root->left,min,root->val)&&
dfs(root->right,root->val,max));
}
};
//方法二:中序遍历验证
class Solution {
public:
bool isValidBST(TreeNode* root) {
TreeNode* prev = nullptr;
return inorder(root, prev);
}
private:
bool inorder(TreeNode* node, TreeNode*& prev) {
if (!node) return true;
// 检查左子树
if (!inorder(node->left, prev)) {
return false;
}
// 检查当前节点(中序遍历应该是递增的)
if (prev && node->val <= prev->val) {
return false;
}
prev = node;
// 检查右子树
return inorder(node->right, prev);
}
};
方法三:迭代中序遍历
class Solution {
public:
bool isValidBST(TreeNode* root) {
stack<TreeNode*> stk;
TreeNode* curr = root;
TreeNode* prev = nullptr;
while (curr || !stk.empty()) {
// 模拟递归:一直向左深入
while (curr) {
stk.push(curr); // 相当于递归调用前的"保存现场"
curr = curr->left; // 相当于递归调用 inorder(root->left)
}
// 模拟递归返回:回到父节点
curr = stk.top();
stk.pop(); // 相当于从调用栈弹出
// 访问当前节点(中序位置)
if (prev && curr->val <= prev->val) return false;
prev = curr;
// 转向右子树,模拟递归调用
curr = curr->right; // 相当于递归调用 inorder(root->right)
}
return true;
}
};
为什么还需要while(curr)循环?
虽然大部分时间curr的值来自栈,但我们仍然需要while(curr)循环,因为:
1.处理新的子树:每当转向一个非空的右子树时,我们需要从这个新子树的根节点开始,找到它的最左节点
2.统一处理逻辑:这样写代码更简洁,不需要区分"第一次进入"和"后续处理"的情况
3.确保完整性:保证每个新子树的遍历都从中序遍历的起点开始