题目描述
给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。有效 二叉搜索树定义如下:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3] 输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。
解题思路
一、递归
考虑到有效二叉搜索树要保证左右子树也是二叉搜索树,所以可以用递归函数判断。其中要确保左孩子节点值小于父母节点值,右孩子节点的值大于父母节点的值,所以需要每次传入较大和较小的值。注意初始时,两个参数分别为最大最小值,当递归左子树时,当前节点的值作为较大值upper传入参数,当递归右子树时当前节点的值作为较小值lower传递参数。
代码实现
/**
* 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) {
//调用函数方便实现判断,其中LONG_MIN,LONG_MAX为long类型的最小最大关键字
return helper(root,LONG_MIN,LONG_MAX);
}
bool helper(TreeNode* root,long long lower,long long upper){
if(!root){
return true;
}
//当前节点的值比较小值小或者比较大值大则说明不是二叉搜索树
//第一次递归,lower为long类型的最小值,upper为long类型的最大值
//之后二者就为当前值的较大值和较小值
if(root->val <= lower || root->val >= upper){
return false;
}
//向左递归,则当前值为左子树的较大值;向右递归,则当前值为右子树的较小值
return helper(root->left,lower,root->val) && helper(root->right,root->val,upper);
}
};
二、中序遍历
利用对二叉搜索树的中序遍历结果为升序排列的特点,可以一边遍历一边与上一个节点的值进行比较,一旦出现当前节点值不大于上一节点值,则直接返回false,若全部遍历结束则返回true。
代码实现
/**
* 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) {
if(!root){
return true;
}
stack<TreeNode*> st;//存储节点
long long last_val = (long long )INT_MIN-1;//long long类型最小值
while(!st.empty()||root!=nullptr){
//一直往左深入遍历
while(root){
st.push(root);
root=root->left;
}
//左子树入栈完毕开始判断
root=st.top();
st.pop();
//出现不符合条件的节点则直接返回
if(root->val <= last_val){
return false;
}
//存储当前值,作为下一个节点的前一个节点值
last_val=root->val;
//遍历右子树
root = root->right;
}
return true;
}
};
总结
- 前序遍历 在某些数据下不需要递归到边界(base case)就能返回,而中序遍历和后序遍历至少要递归到一个边界,从这个角度上来说,前序遍历是最快的。
- 中序遍历 很好地利用了二叉搜索树的性质,使用到的变量最少。
- 后序遍历 的思想是最通用的,即自底向上计算子问题的过程。与动态规划思想类似。