Day111 | 灵神 | 二叉树 | 验证二叉搜索树
98.验证二叉搜索树
98. 验证二叉搜索树 - 力扣(LeetCode)
方法一:前序遍历

递归函数传入合法的左右边界,只有当前结点是合法的边界,才是二叉搜索树,否则就返回false
对于左子树就传入当前结点的左边界,和当前结点的值作为右边界,从而验证左子树是一颗二叉搜索树
对于右子树就传入当前结点的右边界,和当前结点的值作为左边界,从而验证右子树是一颗二叉搜索树
只有当前结点符合条件并且左右子树也都是二叉搜索树才会返回true
灵神前序遍历代码:
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public: bool isValidBST(TreeNode* root, long long left = LLONG_MIN, long long right = LLONG_MAX) { if (root == nullptr) { return true; } long long x = root->val; return left < x && x < right && isValidBST(root->left, left, x) && isValidBST(root->right, x, right); } };
|
复杂度分析
- 时间复杂度:O(n),其中 n 为二叉搜索树的节点个数。
- 空间复杂度:O(n)。最坏情况下,二叉搜索树退化成一条链(注意题目没有保证它是平衡树),因此递归需要 O(n) 的栈空间。
方法二:中序遍历
二叉树搜索树的中序遍历是一个有序序列,记录前一个值,如果当前结点的值比前一个值小就是false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution { public: TreeNode *pre=nullptr; bool tra(TreeNode *t) { if(t==nullptr) return true; bool l=tra(t->left); if(pre!=nullptr) if(t->val<=pre->val) return false; pre=t; bool r=tra(t->right); return l&&r; } bool isValidBST(TreeNode* root) { return tra(root); } };
|
灵神中序遍历代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Solution { long long pre = LLONG_MIN; public: bool isValidBST(TreeNode* root) { if (root == nullptr) { return true; } if (!isValidBST(root->left) || root->val <= pre) { return false; } pre = root->val; return isValidBST(root->right); } };
|
复杂度分析
- 时间复杂度:O(n),其中 n 为二叉搜索树的节点个数。
- 空间复杂度:O(n)。最坏情况下,二叉搜索树退化成一条链(注意题目没有保证它是平衡树),因此递归需要 O(n) 的栈空间。
方法三:后序遍历
灵神后序遍历代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Solution { pair<long long, long long> dfs(TreeNode* node) { if (node == nullptr) { return {LLONG_MAX, LLONG_MIN}; } auto[l_min, l_max] = dfs(node->left); auto[r_min, r_max] = dfs(node->right); long long x = node->val; if (x <= l_max || x >= r_min) { return {LLONG_MIN, LLONG_MAX}; } return {min(l_min, x), max(r_max, x)}; }
public: bool isValidBST(TreeNode* root) { return dfs(root).second != LLONG_MAX; } };
|
复杂度分析
- 时间复杂度:O(n),其中 n 为二叉搜索树的节点个数。
- 空间复杂度:O(n)。最坏情况下,二叉搜索树退化成一条链(注意题目没有保证它是平衡树),因此递归需要 O(n) 的栈空间。
灵神点评
- 前序遍历在某些数据下不需要递归到叶子节点就能返回(比如根节点左儿子的值大于根节点的值,左儿子就不会继续往下递归了),而中序遍历和后序遍历至少要递归到一个叶子节点。从这个角度上来说,前序遍历是最快的。
- 中序遍历很好地利用了二叉搜索树的性质,使用到的变量最少。
- 后序遍历的思想是最通用的,即自底向上计算子问题的过程。想要学好动态规划的话,请务必掌握自底向上的思想。