leetcode 链接:合法二叉搜索树
《程序员面试代码指南》by 左程云 中相关题目:★★☆☆找到二叉树中的最大搜索二叉树(树形dp套路)
题目
实现一个函数,检查一棵二叉树是否为二叉搜索树。
示例:
输入: [2,1,3]
2
/ \
1 3
输出: true
输入: [5,1,4,null,null,3,6]
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
注意如下特殊情形:
输入: [1,1]
1
/
1
输出: false
输入: [2147483647]
2147483647
输出: true
输入: [-2147483648]
-2147483648
输出: true
解答 & 代码
解法一:
想法:如果以
root
节点为根节点的树是二叉搜索树,那么要求它的左子树是二叉搜索树、右子树是二叉搜索树,且左子树的最大值小于root
的值,右子树的最小值大于root
的值
设置一个 ReturnType
结构作为返回的结构,得到以 root
节点为根节点的树的一些信息,里面包含以 root
节点为根节点的树中的最小节点值 minVal
、最大节点值 maxVal
以及该树是否为二叉搜索树 isBST
设置 ReturnType posOrderProcess(TreeNode* root)
函数,进行递归 后序遍历,以得到以 root
节点为根节点的树中的最小节点值 minVal
、最大节点值 maxVal
以及该树是否为二叉搜索树 isBST
:
- 递归结束条件:root 为 NULL,则返回
ReturnType(minVal=(long long)INT_MAX + 1, maxVal=(long long)INT_MIN - 1, isBST=true)
- 递归得到
root
左子树的信息 - 递归得到
root
右子树的信息 - 计算以
root
节点为根节点的树中的最小的节点值:比较左子树的的最小值、右子树的最小值和root
的值 - 计算以
root
节点为根节点的树中的最大的节点值:比较左子树的的最大值、右子树的最大值和root
的值 - 如果左子树的最大值小于
root
的值,且右子树的最大值大于root
的值,且左子树是二叉搜索树,且右子树是二叉搜索树,那么以root
节点为根节点的树是二叉搜索树,返回其信息 - 否则,以
root
节点为根节点的树不是二叉搜索树,返回其信息
注意:本题有几个需要额外关注的输入样例
输入: [2147483647],输出 true
:INT_MAX = 2147483647
,因此在递归结束条件中,不能将返回值的minVal
设为INT_MAX
,否则回到上层递归比较,发现右子树的最小值(INT_MAX
)等于root
的值(2147483647),于是错误判定这棵树不是二叉搜索树- 并且不能直接将判定条件改成小于等于:“如果左子树的最大值小于等于
root
的值,且右子树的最大值大于等于root
的值,且左子树是二叉搜索树,且右子树是二叉搜索树,那么以root
节点为根节点的树是二叉搜索树”,因为还有输入为 [1, 1] 的情况,答案判定为false
,也就是说该题还要求左子树的值严格小于root
的值,且严格小于右子树的值,而不能相等
- 并且不能直接将判定条件改成小于等于:“如果左子树的最大值小于等于
输入: [-2147483648],输出 true
:INT_MIN = -2147483648
,和上面类似,在递归结束条件中,不能将返回值的maxVal
设为INT_MIN
,否则回到上层递归比较,发现左子树的最大值(INT_MIN
)等于root
的值(-2147483648),于是错误判定这棵树不是二叉搜索树输入: [1,1],输出 false
:也就是要求左子树的值严格小于root
的值,且严格小于右子树的值,而不能相等,才算二叉搜索树
解决方法:将 int
改为 long long
(不能是 long
,long
的取值范围和 int
相同);且在递归结束条件中,将返回值的 minVal
设为 INT_MAX + 1
,将返回值的 maxVal
设为 INT_MIN - 1
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
struct ReturnType {
long long minVal;
long long maxVal;
bool isBST;
ReturnType(long minV, long maxV, bool BST): minVal(minV), maxVal(maxV), isBST(BST) {}
};
class Solution {
private:
ReturnType posOrderProcess(TreeNode* root) {
if(root == NULL)
return ReturnType((long long)INT_MAX + 1, (long long)INT_MIN - 1, true);
ReturnType lData = posOrderProcess(root->left);
ReturnType rData = posOrderProcess(root->right);
long long minVal = (lData.minVal < rData.minVal) ? lData.minVal : rData.minVal;
minVal = (minVal < root->val) ? minVal : root->val;
long long maxVal = (lData.maxVal > rData.maxVal) ? lData.maxVal : rData.maxVal;
maxVal = (maxVal > root->val) ? maxVal : root->val;
if(lData.isBST && rData.isBST && lData.maxVal < root->val && rData.minVal > root->val)
return ReturnType(minVal, maxVal, true);
else
return ReturnType(minVal, maxVal, false);
}
public:
bool isValidBST(TreeNode* root) {
return posOrderProcess(root).isBST;
}
};
执行结果:
执行结果:通过
执行用时:12 ms, 在所有 C++ 提交中击败了 85.77% 的用户
内存消耗:21.2 MB, 在所有 C++ 提交中击败了 13.79% 的用户
解法二:
以 root
节点为根的树是二叉搜索树,只有几种情形:
root == NULL
root
节点不为 NULL,左子树若不为空则左子树的值都小于root
的值;右子树若不为空则右子树的值都大于root
的值
思想:递归遍历每个节点的同时,缩小每个节点的值应满足的上下界,如果是二叉搜索树,就必须每个值都在其上下界内
因此,设置一个辅助的递归函数 bool isBST(TreeNode* root, long long min, long long max)
来判断以 root
节点为根的树是否为二叉搜索树
- 如果
root == NULL
,则是二叉搜索树,直接返回 true - 否则,需要先判断
root
的值是否在区间(min, max)
内,注意是开区间,min
、max
分别是root
节点值(or 以root
节点为根的树)的上下界- 如果不在区间内,则不是二叉搜索树,返回 false
- 如果在区间内,还需要继续缩小区间范围,递归判定其左子树的值是否在新的区间
(min, root->val)
内,以及其右子树的值是否在新的区间(root->val, max)
内,如果满足条件,则是二叉搜索树,返回 true;否则不是二叉搜索树,返回 false
执行结果: ``` 执行结果:通过/** * 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 { private: bool isBST(TreeNode* root, long long min, long long max) { return (root == NULL) || ((root->val > min) && (root->val < max) && isBST(root->left, min, root->val) && isBST(root->right, root->val, max)); } public: bool isValidBST(TreeNode* root) { return isBST(root, LONG_MIN, LONG_MAX); } };
执行用时:16 ms, 在所有 C++ 提交中击败了 61.73% 的用户 内存消耗:21 MB, 在所有 C++ 提交中击败了 58.78% 的用户 ```