深度优先搜索DFS(三)

146 阅读3分钟

LeetCode 98. 验证二叉搜索树

题目1.png 题目2.png 题意:判断一颗二叉树是否为有效的二叉搜索树。判断条件为:1.节点的左子树只包含 小于 当前节点的数;2.节点的右子树只包含 大于 当前节点的数;3.所有左子树和右子树自身必须也是二叉搜索树。

思路1:比较暴力的方案,将树进行一次遍历,使用深搜来进行遍历,深搜会遍历单独一条条支流,我们可以单独存储当前节点的那条支流上的节点,然后将当前节点跟前面的节点进行比较,如果有不符合的就不是二叉搜索树,反之成立。

例子:判断这棵树是否为二叉搜索树 1-1.png 从根节点开始,使用一个容器存储支流上的节点,存储的信息有节点数值和区分跟当前节点判断大还是小;

1-2.png 上图流程是先遍历到跟节点,容器是空的;接下来是遍历左子节点,左子节点2跟父子节点比较,因为是左边,所以2<5是符合的,然后将5放入到容器中,因为支流在5的左边,所以附加值是左;再进行左子节点的遍历,先判断子节点跟父子节点的大小,1<2是符合的,值1再跟容器中的5做比较,因为是左边,所以1<5也是符合的,然后将2放入容器中,附加值是左。

1-3.png 节点1的左右子节点都是空,返回到节点2,容器弹出数值2的信息;遍历到节点2的右子节点,右子节点3>2和容器里的值比较3<5也是符合,将2加入容器,附加值是右;节点3的左右子节点是空,返回节点2,弹出容器数值2的信息。

1-4.png 节点2左右节点遍历完,返回到节点5,弹出容器5的信息;遍历节点5的右子节点,8>5符合,将5加入容器,附加值是右;遍历节点8的左子节点,7<8与7>5符合,将8加入容器,附加值左;

1-5.png 节点7左右子节点为空,返回到节点8,弹出容器8的信息;遍历节点8的右子节点,9>8与9>5符合,将8加入容器,附加值是右;

1-6.png 基本整棵树遍历完成,都是符合要求,所以是一棵二叉搜索树。

实现代码:

class Solution {
public:
    struct rootinfo{
        bool blr;
        int val;
    };
    
    void DFS(TreeNode* root, vector<rootinfo> rootvalue, bool& res) {
        if(root == nullptr || res == false) {
            return ;
        }
        
        if(root->left != nullptr) {
            if(root->val <= root->left->val) {
                res = false;
                return ;
            }
            for(int i=0; i<rootvalue.size(); i++) {
                bool br = rootvalue[i].blr == 0 ? root->left->val < rootvalue[i].val : root->left->val > rootvalue[i].val;
                if(br == false) {
                    res = false;
                    return ;
                }
            }
            rootinfo tmp;
            tmp.val = root->val;
            tmp.blr = 0;
            rootvalue.push_back(tmp);
            DFS(root->left, rootvalue, res);
            rootvalue.pop_back();
        }
        
        if(root->right != nullptr) {
            if(root->val >= root->right->val) {
                res = false;
                return ;
            }
            for(int i=0; i<rootvalue.size(); i++) {
                bool br = rootvalue[i].blr == 0 ? root->right->val < rootvalue[i].val : root->right->val > rootvalue[i].val;
                if(br == false) {
                    res = false;
                    return ;
                }
            }
            rootinfo tmp;
            tmp.val = root->val;
            tmp.blr = 1;
            rootvalue.push_back(tmp);
            DFS(root->right, rootvalue, res);
            rootvalue.pop_back();
        }
    }
    bool isValidBST(TreeNode* root) {
        bool bResult = true;
        if(root == nullptr) {
            return false;
        }
        
        vector<rootinfo> rootvalue;
        rootvalue.clear();
        DFS(root, rootvalue, bResult);
        return bResult;
    }
};

上述的方案比较暴力,直接判断每一个支流的节点数值。另外一个方案是对树进行一个中序遍历,如果是二叉搜索树,就是一个有序的数组。下面来进行中序遍历的遍历流程,也是以刚刚的树为例子

1-1.png 从跟节点开始遍历,节点5遍历左子节点2,再遍节点2的左子节点1,左子节点1的左右子节点都为空,放进数组;回退到节点2,将2放进数组;节点2左子节点已经遍历完,再遍历右子节点3,右子节点3的左右子节点都为空,将3放进数组;回退到节点2,节点2左右节点遍历完;

2-2.png 回退到节点5,将5放进数组;节点5的左子节点已经遍历完,再到右子节点8,到节点8的左子节点7,节点7的左右子节点都为空,将7放进数组;回退节点8,将8放进数组;

2-3.png 再到遍历节点8的右子节点9,节点9左右都为空,将9放进数组;基本遍历完成;

2-4.png 最后对数组进行比较,确定是从小到大排序,此树就为二叉搜索树。 其实中序遍历的遍历规则是先将左子节点放进数组,再到父节点放进数组,最后到右子节点放入数组,按此顺序遍历完整棵树。

实现代码:

class Solution {
public:
    void DFS(TreeNode* root, vector<int>& resNum) {
        if(root == nullptr) {
            return ;
        }
        if(root->left != nullptr) {
            DFS(root->left, resNum);
        }
        resNum.push_back(root->val);
        if(root->right != nullptr) {
            DFS(root->right, resNum);
        }
    } 
    bool isValidBST(TreeNode* root) {
        bool bRes = true;
        vector<int> resNum;
        resNum.clear();
        DFS(root, resNum);
        for(int i=0; i<resNum.size()-1; i++) {
            if(resNum[i] >= resNum[i+1]) {
                bRes = false;
                break;
            }
        }
        return bRes;
    }
};