1、题目描述
要求:给一个二叉树,判断是否是一个有效的二叉搜索树。
有效的定义:①左子树都小于根节点 ②右子树都大于根节点 ③左右子树也都是二叉搜索树
2、分析
2.0、常见的误区
误区:代码中只是单纯的 判断 root节点的左孩子(存在的话)小于root、root节点的右孩子(存在的话)大于root;这样是不行的。例如 [5,4,6,null,null,3,7]。
因为,我们要确保右子树的所有节点都大于根节点、左子树的左右节点都小于根节点;上述写法只是确保右子树的根节点确实符合顺序,但是右子树中完全可能有其左子树的节点小于root。
2.1、BST方法论
对于二叉搜索树类的题目最重要的特性就是他的中序遍历序列是单调递增的。
同理,判断二叉树是不是二叉搜索树也通过这种方式。
2.2、思路一 通过中序序列判断是否是二叉搜索树
思路:先获得中序序列,然后判断中序序列是不是单调递增的。
class Solution {
public:
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
vector<int> nums;
inorder(root, nums);
//接下来判断是不是单调递增的
for(int i = 0; i < nums.size()-1; i++){
if(nums[i] >= nums[i+1]){
return false;
}
}
return true;
}
void inorder(TreeNode* root, vector<int>& nums){
if(root == NULL) return;
inorder(root->left, nums);
nums.push_back(root->val);
inorder(root->right, nums);
}
};
2.3、优化 不用借助额外数组遍历过程即完成比较
思路:在获得中序遍历序列的时候就完成比较。
注意:用例里面出现了int的最小值 [-2147483648];就要求max_val必须定义成 long long的最小值,否则第一次判断就不符合预期。
class Solution {
public:
long long max_val = LONG_MIN; //存在用例[-2147483648],所以定义成LONG_MIN
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
bool lvalid = isValidBST(root->left);
if(!lvalid) return false;
cout << root->val << ",";
if(root->val <= max_val){
return false;
}else{
max_val = root->val;
}
bool rvalid = isValidBST(root->right);
if(!rvalid) return false;
return true;
}
};
2.4、双指针比较
思路:更极端一些如果节点的val就被设成 LONG_MIN 了,我们要怎么办呢?
更好的方式是双指针。即记录前一个遍历到的节点,拿当前遍历到的节点同记录的前一个节点进行比较(记得更新即可)。
class Solution {
public:
TreeNode* pre = NULL;
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
bool lvalid = isValidBST(root->left);
if(!lvalid) return false;
cout << root->val << ",";
if(pre != NULL && root->val <= pre->val){
return false;
}else{
pre = root;
}
bool rvalid = isValidBST(root->right);
if(!rvalid) return false;
return true;
}
};
3、实现代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <math.h>
using namespace std;
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){}
};
/*
98.验证二叉搜索树
要求:给一个二叉树,判断是否是一个有效的二叉搜索树。
有效的定义:①左子树都小于根节点 ②右子树都大于根节点 ③左右子树也都是二叉搜索树
误区:单纯的判断 root节点的左孩子(存在的话)小于root,root节点的右孩子(存在的话)大于root 是不行的;例如 [5,4,6,null,null,3,7]这种情况。
因为,我们要确保右子树的所有节点都大于根节点、左子树的左右节点都小于根节点;上述写法只是确保右子树的根节点确实符合顺序,但是右子树中完全可能有其左子树的节点小于root。
思路一:先得到中序遍历序列,然后判断中序遍历序列是不是单调递增的。
思路二:数组其实不是必须的,我们在获取中序序列的时候其实已经获取遍历这个序列了,直接在遍历的过程中判断即可。
方法论:
对于二叉搜索树类的题目最重要的特性就是他的中序遍历序列是单调递增的。
同理,判断二叉树是不是二叉搜索树也通过这种方式。
*/
/*
class Solution {
public:
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
vector<int> nums;
inorder(root, nums);
//接下来判断是不是单调递增的
for(int i = 0; i < nums.size()-1; i++){
if(nums[i] >= nums[i+1]){
return false;
}
}
return true;
}
void inorder(TreeNode* root, vector<int>& nums){
if(root == NULL) return;
inorder(root->left, nums);
nums.push_back(root->val);
inorder(root->right, nums);
}
};
*/
//不借助数组存结果。遍历的时候就完成比较。
/*
class Solution {
public:
long long max_val = LONG_MIN;
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
bool lvalid = isValidBST(root->left);
if(!lvalid) return false;
cout << root->val << ",";
if(root->val <= max_val){
return false;
}else{
max_val = root->val;
}
bool rvalid = isValidBST(root->right);
if(!rvalid) return false;
return true;
}
};
*/
class Solution {
public:
TreeNode* pre = NULL;
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
bool lvalid = isValidBST(root->left);
if(!lvalid) return false;
cout << root->val << ",";
if(pre != NULL && root->val <= pre->val){
return false;
}else{
pre = root;
}
bool rvalid = isValidBST(root->right);
if(!rvalid) return false;
return true;
}
};
int main()
{
Solution s1;
TreeNode node4(1);
TreeNode node5(3);
TreeNode node3(5);
TreeNode* pnode2 = new TreeNode(2, &node4, &node5);
TreeNode root(4, pnode2, &node3);
bool valid = s1.isValidBST(&root);
cout << "valid:" << valid << endl;
}