题解 | #判断一棵二叉树是否为搜索二叉树和完全二叉树#

判断一棵二叉树是否为搜索二叉树和完全二叉树

http://www.nowcoder.com/practice/f31fc6d3caf24e7f8b4deb5cd9b5fa97

解法一:中序遍历(递归)+ 层次遍历

一棵「二叉搜索树」需要满足要求:对于每个结点,左子树上的所有结点小于它,右子树上的所有结点大于它。

判断一棵二叉树是否为「二叉搜索树」的通用方法为:对该二叉树进行中序遍历,若遍历结果为「严格」单调递增的,则是一棵二叉搜索树,否则不是。

这是因为:中序遍历的步骤是:对于每个结点,先访问其「左子树」,再访问该结点,最后访问其「右子树」;而一棵二叉搜索树左子树结点必小于该结点、右子树上的结点必大于该结点,因此中序遍历的结果为严格单调递增。解法一通过「递归」的方式进行中序遍历,并维护了一个数组用以保存中序遍历的结果,遍历数组来判断是否为严格递增的。

判断一棵树是否为「完全二叉树」的方式为:对其进行层次遍历,若遇到一个空结点,则其后面的结点必须全为空结点,否则不是完全二叉树。具体思路如图所示。

图中,黄色结点为空结点,当遍历到「结点6出队列」后,此时队列中的所有元素必须全为空,否则不是一颗完全二叉树。

图片说明

根据上述思路,实现的代码如下:

/**
 * struct TreeNode {
 *    int val;
 *    struct TreeNode *left;
 *    struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 the root
     * @return bool布尔型vector
     */
    vector<bool> judgeIt(TreeNode* root) {
        vector<bool> res(2, false); 
        if (!root) {
            res[0] = false; 
            res[1] = false; 
            return res; 
        }
        vector<int> inorderRes; 
        inorder(root, inorderRes); // 中序遍历
        res[0] = true;
        // 判断中序遍历结果是否升序
        for (int i = 0; i < inorderRes.size() - 1; i ++) {
            if (inorderRes[i] >= inorderRes[i + 1]) {
                res[0] = false;
                break;
            }
        }
          // 层次遍历
        res[1] = levelOrder(root); 
        return res; 
    }

    void inorder(TreeNode* root, vector<int>& inorderRes) {
        if (!root) return; 
        inorder(root->left, inorderRes); // 访问左子树
        inorderRes.push_back(root->val); // 访问根结点
        inorder(root->right, inorderRes); // 访问右子树
        return; 
    }
    bool levelOrder(TreeNode* root) {
        queue<TreeNode*> q; 
        q.push(root); 
        while (q.front()) {
            TreeNode* tmp = q.front(); 
            q.pop(); 
            q.push(tmp->left); 
            q.push(tmp->right); 
        }
        // 队列元素必须全为空
        while (q.size() && !q.front()) {
            q.pop(); 
        }
        return q.empty(); 
    }
};

该方法需要遍历两次树,因此时间复杂度为O(N);该方法定义了数组和队列分别用来进行中序遍历和层次遍历,因此空间复杂度为O(N)。

解法二:中序遍历(非递归)+ 层次遍历优化

解法二给出了中序遍历的「非递归实现」,其实现思路如图所示。

图片说明

步骤如下:

  1. 定义一个栈s,用来存储待访问的结点;
  2. 对每个结点(设为root),若其左孩子不为空,则将其入栈,并将root置于其左孩子的位置;
  3. 若其左孩子为空,则访问栈s的栈顶元素(记为t)、并出栈,并将root置为t的右孩子;
  4. 重复上述步骤,直至栈s为空且root为空指针。

对于层次遍历,可以优化解法一的两层while循环为一层循环:设标志位flag,若检测到当前结点为空,则置flag为true,若此时后续遇到非空结点,说明不是完全二叉树。

根据上述思路,实现的代码如下:

/**
 * struct TreeNode {
 *    int val;
 *    struct TreeNode *left;
 *    struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 the root
     * @return bool布尔型vector
     */
    vector<bool> judgeIt(TreeNode* root) {
        vector<bool> res(2); 
        if (!root) {
            res[0] = false; res[1] = false; 
            return res; 
        }
        // 定义栈 中序遍历
        stack<TreeNode*> s; 
        TreeNode* p = root; 
        int min_ = INT_MIN; 
        res[0] = true;
        while (p || s.size()) {
            while (p) {
                s.push(p); 
                p = p->left; 
            }
            if (s.size()) {
                p = s.top(); 
                // 判断是否严格递增
                if (min_ < p->val) 
                    min_ = p->val; 
                else {
                    res[0] = false;
                    break;
                }
                s.pop(); 
                p = p->right;
            }
        }
        // 定义队列 层次遍历
        queue<TreeNode*> q; 
        bool flag = false; 
        res[1] = true; 
        q.push(root); 
        while (q.size()) {
            TreeNode* tmp = q.front(); 
            q.pop(); 
            // 遇到空结点 flag置为true
            if (!tmp) {
                flag = true; 
                continue; 
            } else {
                // 遇到非空结点
                if (flag) {
                    res[1] = false;
                    break;
                } else { // flag仍为false时,继续遍历
                    q.push(tmp->left); 
                    q.push(tmp->right); 
                }
            }
        }
        return res; 
    }
};

该方法需要遍历两次树,因此时间复杂度为O(N);该方法定义了栈和队列分别用来进行中序遍历和层次遍历,因此空间复杂度为O(N)。

解法优化:优化中序遍历(递归)

对于解法一中的递归进行中序遍历,可以优化其空间复杂度,即无需定义数组保存结果。注意:此优化方法在递归时需要使用栈空间,空间复杂度为O(N)。

在每次递归时,比较当前元素是否在low到high所定义的范围内;若满足条件则进行下一次递归,否则直接返回false。

实现的代码如下:

    bool inorder(TreeNode* root, int low, int high) {
        if (!root) 
            return true; 
          // 不满足中序遍历递增要求
        if (!(root->val > low && root->val < high)) 
            return false; 
          // 分别递归左子树和右子树,同时更新low和high
        return inorder(root->left, low, root->val) && inorder(root->right, root->val, high); 
    }

在调用该函数时,初始化low和high分别为INT_MIN和INT_MAX。

全部评论
root为空应该输出2个true
点赞 回复 分享
发布于 2022-04-28 16:12
空树输出true
点赞 回复 分享
发布于 2022-03-17 11:37

相关推荐

03-04 07:14
门头沟学院 C++
后测速成辅导一两个月...:老板:都给工作机会了还想要工资,哪来这么多好事
点赞 评论 收藏
分享
02-28 13:25
已编辑
门头沟学院 Java
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
正在热议
更多
# 长得好看会提高面试通过率吗? #
4471次浏览 48人参与
# 离家近房租贵VS离家远但房租低,怎么选 #
16917次浏览 137人参与
# 巨人网络春招 #
11540次浏览 228人参与
# 沪漂/北漂你觉得哪个更苦? #
1616次浏览 41人参与
# 你的实习产出是真实的还是包装的? #
3230次浏览 54人参与
# 春招至今,你的战绩如何? #
16021次浏览 146人参与
# 米连集团26产品管培生项目 #
7383次浏览 226人参与
# HR最不可信的一句话是__ #
1107次浏览 32人参与
# AI面会问哪些问题? #
971次浏览 24人参与
# 你做过最难的笔试是哪家公司 #
1306次浏览 23人参与
# AI时代,哪个岗位还有“活路” #
2930次浏览 53人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152945次浏览 889人参与
# 简历第一个项目做什么 #
32180次浏览 363人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
8029次浏览 43人参与
# XX请雇我工作 #
51164次浏览 171人参与
# 简历中的项目经历要怎么写? #
311119次浏览 4271人参与
# 投格力的你,拿到offer了吗? #
178382次浏览 891人参与
# 你最满意的offer薪资是哪家公司? #
77008次浏览 375人参与
# AI时代,哪些岗位最容易被淘汰 #
64819次浏览 891人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187635次浏览 1123人参与
# 你怎么看待AI面试 #
180882次浏览 1318人参与
# 正在春招的你,也参与了去年秋招吗? #
364407次浏览 2642人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务