原题Medium):

  给定一个二叉树,判断其是否是一个有效的二叉搜索树。

  假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

     

   这题我的第一感觉就是深度搜索,比较根结点与左子结点和右子结点的值,根据比较的结果在决定是返回false还是继续递归。就按着这个思路写了代码,运行才发现题目的意思与我的感觉不同。看第二个示例我们就可以发现怎样才算一个有效的二叉搜索树(注意,这是一个反例):

  就是说结点的左子树的所有结点都要小于当前结点,结点的右子树的所有结点都要大于当前结点。

思路一:递归

  这就有点复杂了,于当前结点而言,它的存在是否合理,要看它的父节点,以及父节点再往上的结点,举个例子:

   于当前结点而言(值为val),决定其值是否合理的,第一个要看其父节点,这里有两种情况,首先看x这个结点(数字仅为标记结点,并非其当前值),当前结点是其x这个结点的右子结点,那么val必须大于x->val,这是它的下界(不能低于它),那么val的上界呢,就是说它有没有一个不能高于的值,有,因为x这个点有父节点z,且x为该父节点的左子结点,说明x结点为根结点的子树的所有结点的值必须小于z这个结点的值,所以val的上界就是z->val,你可能说这不一定啊,如果z还有父节点zz且z是它的左子结点呢?对,没错,那这样的话val的上界就是结点zz值。如果当前结点是父节点的右子结点,需要保留其上界(即当前结点的上界来自父节点的上界),并设置其下界为父节点的值

  另外一种情况就是当前结点为y这个结点的左子结点,为了更好的说明情况,我们假设val合理,并且当前结点来到了j这个结点上,要想验证j->val是否合理,首先其值不能大于val,即j->val < val,这就是  j->val的的上界,那下界呢?如果val有一个父节点x,因为x的右子结点所形成的子树上的所有结点的值都会大于x->val,那么要想j->val合理,j->val > x->val就必须成立,那么x->val就是它下界。如果当前结点是父节点的左子结点,需要保留其下界(即当前结点的下界来自父节点的下界),并设置其上界为父节点的值。

  根结点是一个没有上下界的点,那么要从根结点开始,就要设置其上下界为类型最小值和类型最大值。

  我们可以通过递归遍历各结点,并在即将进入某个结点时,设置好其上下界(根据上述来决定其上下界的值)。

 1 /**
 2  * Definition for a binary tree node.
 3  * struct TreeNode {
 4  *     int val;
 5  *     TreeNode *left;
 6  *     TreeNode *right;
 7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 8  * };
 9  */
10
11 bool recursion(TreeNode* root, long lower, long upper)
12 {
13     if (root == NULL) return true;
14
15     int val = root->val;
16     //如果下界存在,且当前值比下界还小,不是二叉搜索树
17     if (lower != LONG_MIN && val <= lower) return false;
18     //如果上界存在,且当前值比上界还大,不是二叉搜索树
19     if (upper != LONG_MAX && val >= upper) return false;
20
21     //即将进入的点是当前结点的右子结点,需要保留其上界(即右子结点的上界来自当前结点的上界),并设置其下界为父结点的值
22     if (!recursion(root->right, val, upper)) return false;
23     //即将进入的点是当前结点的左子结点,需要保留其下界(即左子结点的下界来自当前结点的下界),并设置其上界为父结点的值
24     if (!recursion(root->left, lower, val)) return false;
25
26     return true;
27 }
28
29 bool isValidBST(TreeNode* root) {
30     //根结点是一个没有上下界的点,那么要从根结点开始,就要设置其上下界为类型最小值和类型最大值
31     return recursion(root, LONG_MIN, LONG_MAX);
32 }

  至于为什么上下界是long类型,是因为该题中有几个超出整型范围的cases,不得不改为long型。

思路二:中序遍历

  这个思路就显浅易懂了,我们应该都知道,一个二叉排序树的中序遍历能够获得其依次递增的数组,应为左子树 -> 结点 -> 右子树 意味着对于二叉搜索树而言,每个元素都应该比下一个元素小。所以,只要我们中序遍历该树,并对得到的数组逐一比较大小,看是否是依次递增就好了。用的是递归。

 1     void mid(TreeNode* root, vector<long>& arr)
 2     {
 3         if(root->left == NULL)
 4         {
 5             arr.push_back(root->val);
 6             if(root->right!=NULL)
 7                 mid(root->right, arr);
 8             return;
 9         }
10         else mid(root->left, arr);
11         arr.push_back(root->val);
12         if(root->right!=NULL)
13             mid(root->right, arr);
14         else return;
15     }
16
17     bool isValidBST(TreeNode* root) {
18         if(root == NULL) return true;
19         vector<long> arr;
20         mid(root, arr);
21         for(int i = 1;i <arr.size(); i++)
22         {
23             if(arr[i-1]<arr[i]) continue;
24             return false;
25         }
26         return true;
27     }
02-11 23:19