给定一个二叉树,判断其是否是一个有效的二叉搜索树。
一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入: 2 / \ 1 3 输出: true
示例 2:
输入: 5 / \ 1 4 / \ 3 6 输出: false 解释: 输入为: [5,1,4,null,null,3,6]。 根节点的值为 5 ,但是其右子节点值为 4 。
思路:
这道题用递归的方法,比较当前节点是否比左节点的值大,是否比右节点的值小,如果是就继续递归左右子树。写完自信满满,而然发现不通过,看了test case发现有如下的情况:
5 / \ 1 8 / \ 3 9
上述方法无法判断跨越了超过1层的情况。所以想其他方法。
那么能不能dfs来保存左右子树的最大最小值呢?即每次保存左子树的最大值,右子树的最小值。判断当前节点的值是否大于左子树最大,小于右子树最小,然后通过公式:
left_max=max(left_max,root->val)
right_min=min(right_min,root->val)
来更新然后回传给父节点。但是这样也是不行的,连最简单的test都通不过!!!!如下:
2 / \ 1 3
当判断节点1时,left_max和right_min都被更新成了1,当用中序遍历遍历到2的右子树3节点时(顺序1-2-3),通过公式更新right_min时依然会更新为1而不是3,因为右子树在更新时会带着左子树的信息,无法独立更新,如果在递归右子树前把left_max和right_min赋值为初始化值,就没有了判断超过两层的能力。递归函数本身无法区分左右子树,对函数签名而言,只要满足要求就进入主函数。
所以参考了网上的两个方法:中序遍历来判断和另一种递归方法(逐步缩小范围)。
1:中序遍历
由于左<根<右,不存在相等的情况,所以保存上次遍历的值,看这次的值是否大于上次即可。
所以不会出现这种情况:
2 2 / \ 2 2
这两种情况如果题目(左<=根<右),那么是无法区别的,因为中序遍历出来是相同的。
代码:
void isValidBSTCore(TreeNode* root, TreeNode* &tmp,bool &flag, TreeNode* true_root) { if (!flag || !root) { return; } isValidBSTCore(root->left, tmp,flag,true_root); if (tmp) { if (root->val <= tmp->val) { flag = false; return; } } tmp = root; isValidBSTCore(root->right, tmp, flag, true_root); } bool isValidBST(TreeNode* root) { if (!root) { return true; } TreeNode* tmp = nullptr; bool flag = true; isValidBSTCore(root, tmp, flag, root); return flag; }
2:递归逐步缩小范围,从父向子判断,首先初始化mm=long_min,mx=long_max,如果根节点值为mid,那么左子树的所有节点的范围为[mm,mid),右子树的所有节点范围为(mid,mx],以此类推。因为一开始的边界条件会判断当前节点是否在范围内,所以范围绝对是越来越小的,这样就很好解决了跨级判断的问题。还是如下图的例子,看图每个节点的范围就很清楚:
通过一层层缩小范围,可以找到3不满足bst条件。
代码:
bool isValidBSTCore(TreeNode* root, long mm, long mx) { if (!root) { return true; } if (root->val<=mm || root->val>=mx) { return false; } return isValidBSTCore(root->left, mm, root->val) && isValidBSTCore(root->right, root->val, mx); } bool isValidBST(TreeNode* root) { if (!root) { return true; } return isValidBSTCore(root, LONG_MIN, LONG_MAX); }