算法学习 | day13/60 二叉树层序遍历/翻转二叉树/对称二叉树

本文详细探讨了LeetCode中关于二叉树和N叉树的层序遍历问题,包括二叉树的右视图、层平均值、最大值查找、填充相邻节点指针、最小深度计算以及翻转和对称性判断等,并提供了相应的代码实现和解题思路。
摘要由CSDN通过智能技术生成

一、题目打卡

        直接上 10 道题目,因为基本都是层序遍历的思路,之前也接触过,就直接开始写了:

        1.1 层序遍历

        题目链接:. - 力扣(LeetCode)

class Solution {
private:
    vector<int> tmp_;
    vector<vector<int>> res;
    queue<TreeNode*> q;
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(!root) return {};
        q.push(root);
        while(!q.empty()){
            int size = q.size();
            while(size--){
                TreeNode* tmp = q.front();
                q.pop();
                tmp_.push_back(tmp->val);
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            res.push_back(tmp_);
            tmp_.clear();
        }
        return res;
    }
};

        思路整体是比较简单的,主要就是动态维护一个 queue 队列,队列里面存储的是每一层树的节点,然后按照这个动态 queue 的 size 进行一个遍历输出就可以了。

        1.2 二叉树的层序遍历II

        其实就是多了一个结果的逆序:

class Solution {
private:
    vector<int> _tmp;
    vector<vector<int>> _res;
    queue<TreeNode*> q;
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        if(!root) return {};
        q.push(root);
        while(!q.empty()){
            int size = q.size();
            while(size--){
                TreeNode* tmp = q.front();
                q.pop();
                _tmp.push_back(tmp->val);
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            _res.push_back(_tmp);
            _tmp.clear();
        }
        reverse(_res.begin(),_res.end());
        return _res;
    }
};

        1.3 二叉树的右视图

        对于这个题目而言,由于事先不知道二叉树的形状,如果二叉树是一个满二叉树,那么其实递归可以很好地解决:

class Solution {
private:
    void recur(TreeNode* root, vector<int> &res){
        if(!root) return;
        res.push_back(root->val);
        recur(root->right,res);
        return;
    }
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> res;
        recur(root,res);
        return res;
    }
};

        但是由于本身二叉树的形状不确定,因而持续取右会出错。因而还是根据层序遍历,然后取末尾的值比较好:

class Solution {
private:
    vector<int> _tmp;
    vector<vector<int>> _res;
    queue<TreeNode*> q;
public:
    vector<int> rightSideView(TreeNode* root) {
        if(!root) return {};
        q.push(root);
        while(!q.empty()){
            int size = q.size();
            while(size--){
                TreeNode* tmp = q.front();
                q.pop();
                _tmp.push_back(tmp->val);
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            _res.push_back(_tmp);
            _tmp.clear();
        }
        vector<int> res;
        for(auto& it : _res){
            res.push_back(it.back());
        }
        return res;
    }
};

        1.4 二叉树的层平均值

        leetcode链接:. - 力扣(LeetCode)

class Solution {
private:
    vector<int> _tmp;
    vector<vector<int>> _res;
    queue<TreeNode*> q;

    double sum(vector<int> & nums){
        double res = 0;
        for(auto &it : nums){
            res += it;
        }
        return res;
    }
public:
    vector<double> averageOfLevels(TreeNode* root) {
        if(!root) return {};
        q.push(root);
        while(!q.empty()){
            int size = q.size();
            while(size--){
                TreeNode* tmp = q.front();
                q.pop();
                _tmp.push_back(tmp->val);
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            _res.push_back(_tmp);
            _tmp.clear();
        }
        vector<double> res;
        for(auto& it : _res){
            res.push_back((double)sum(it)/it.size());
        }
        return res;
    }
};

        没什么特别的,注意结果的数据转换。

        贴一个答案的深度优先搜索:

class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        auto counts = vector<int>();
        auto sums = vector<double>();
        dfs(root, 0, counts, sums);
        auto averages = vector<double>();
        int size = sums.size();
        for (int i = 0; i < size; i++) {
            averages.push_back(sums[i] / counts[i]);
        }
        return averages;
    }

    void dfs(TreeNode* root, int level, vector<int> &counts, vector<double> &sums) {
        if (root == nullptr) {
            return;
        }
        if (level < sums.size()) {
            sums[level] += root->val;
            counts[level] += 1;
        } else {
            sums.push_back(1.0 * root->val);
            counts.push_back(1);
        }
        dfs(root->left, level + 1, counts, sums);
        dfs(root->right, level + 1, counts, sums);
    }
};

        1.5 N叉树的层序遍历

        leetcode链接:. - 力扣(LeetCode)

class Solution {
private:
    vector<vector<int>> _res;
    vector<int> _tmp;
    queue<Node*> q;
public:
    vector<vector<int>> levelOrder(Node* root) {
        if(!root) return {};
        q.push(root);
        while(!q.empty()){
            int size = q.size();
            while(size--){
                Node* tmp = q.front();
                q.pop();
                if(tmp){
                    _tmp.push_back(tmp->val);
                    for(auto & it : tmp->children){
                        if(tmp) q.push(it);
                    }
                }
            }
            _res.push_back(_tmp);
            _tmp.clear();
        }
        return _res;
    }
};

        1.6 在树的每一行寻找最大值

        leetcode题目链接:. - 力扣(LeetCode)

class Solution {
private:
    vector<int> tmp_;
    vector<vector<int>> res;
    queue<TreeNode*> q;
public:
    vector<int> largestValues(TreeNode* root) {
        if(!root) return {};
        q.push(root);
        while(!q.empty()){
            int size = q.size();
            while(size--){
                TreeNode* tmp = q.front();
                q.pop();
                tmp_.push_back(tmp->val);
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            res.push_back(tmp_);
            tmp_.clear();
        }
        vector<int> ans;
        for(auto &it : res){
            auto m = max_element(it.begin(),it.end());
            ans.push_back(*m);
        }
        return ans;
    }
};

        本身在第二个while循环的逻辑进行处理就可以,随时更新最大值的大小,不过时间原因,直接复用的之前的代码,并使用 max_element 方法直接返回。

         这个题目如果采用的是递归的话,可以变的很简洁:

class Solution {
private:
    void recur(TreeNode* root, int level, vector<int> &res){
        if(level == res.size()) res.push_back(root->val); // 说明开辟出了新的深度
        else res[level] = max(res[level],root->val);

        if(root->left) recur(root->left,level+1,res);
        if(root->right) recur(root->right,level+1,res);
    }

public:
    vector<int> largestValues(TreeNode* root) {
        if(!root) return {};
        vector<int> res;
        recur(root,0,res);
        return res;
    }
};

        1.7 填充每个节点的下一个右侧节点指针(答案更简洁)

        题目链接:. - 力扣(LeetCode)

class Solution {
private:
    vector<Node*> _tmp;
    vector<vector<Node*>> _res;
    queue<Node*> _q;
public:
    Node* connect(Node* root) {
        if(!root) return root;
        _q.push(root);
        while(!_q.empty()){
            int size = _q.size();
            while(size--){
                Node* tmp = _q.front();
                _q.pop();
                _tmp.push_back(tmp);
                if(tmp->left) _q.push(tmp->left);
                if(tmp->right) _q.push(tmp->right);
            }
            _res.push_back(_tmp);
            _tmp.clear();
        }
        for(auto& it: _res){
            for(int i = 0; i < it.size() - 1;i++){
                if(it[i] && it[i+1]) it[i]->next = it[i+1];
            }
            it.back()->next = nullptr;
        }
        return root;
    }
};

        时间原因,继续复用,答案还是把处理的逻辑放在了里面。

        1.8 填充右侧节点II

        题目没有什么特别的,上个题的答案可以直接通过,这里就直接贴一个链接:. - 力扣(LeetCode)

        1.9 二叉树最大深度

        层序遍历没有什么特别的,依旧是进行队列的遍历模拟,只是不需要添加返回值了,用一个计数器记录深度,可以理解为正向的思想,这里我也就不贴答案了,尝试使用 dfs 进行求解,不过我使用的依然是正向的思维:

class Solution {
private:
    int max_res = 0;
    void recur(TreeNode* root,int level){
        if(!root) return;
        max_res = max(max_res,level);
        if(root->left) recur(root->left,level+1);
        if(root->right) recur(root->right,level+1);
    }
public:
    int maxDepth(TreeNode* root) {
        if(!root) return 0;
        recur(root,1);
        return max_res;
    }
};

        不过其实可以和答案一样,采用逆向的思维,可以更简洁:

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

        1.10 二叉树的最小深度

        题目链接:. - 力扣(LeetCode)

class Solution {
public:
    int minDepth(TreeNode* root) {
        if(!root) return 0;
        int m = minDepth(root->left);
        int n = minDepth(root->right);
        if(m == 0 || n == 0) return m + n + 1;
        else return min(m,n) + 1;
    }
};

        思考这个题目的时候,还是踩的一些坑的,这里最小深度的理解需要注意一点,对于那种只有一段有子树的情况,这一个节点并不是叶子节点,而且不要去思考递归的过程,容易陷进去,还是以每一个过程进行思考:

        对于每一个递归后的返回值,其实对应的就是两个子树的深度,而非叶子节点,返回的应该是含有子树那一端的最小子树大小,而不是直接返回 1,这是我踩的坑,就是我标记的位置,记录一下:

        思考这个问题的时候,也应该把一个单独的位置拿出来进行思考,就是一个三角的小结构,分析这个问题,也可以把整个大问题分为小问题:左子树的最小深度和右子树的最小深度,而需要返回的是应该是这两个值的最小值(在两边都存在的时候),如果有一边不存在,那么应该返回的是存在的那一边的数值,最后一种情况就是叶子节点,这时候直接返回 0 就可以了。

        1.11 翻转二叉树

        题目链接:. - 力扣(LeetCode)        

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(!root) return nullptr;
        TreeNode* left = invertTree(root->right);
        TreeNode* right = invertTree(root->left);
        root->left = left;
        root->right = right;
        return root;
    }
};

        这个题目,第一次写的时候出现了情况:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(!root) return nullptr;
        root->left = invertTree(root->right);
        root->right = invertTree(root->left);
        return root;
    }
};

        这个代码是错误的,虽然没有太想明白,大概的感觉是,如果在第一步就直接改了左节点的索引,那么在下一步调用左索引的时候,是会出现错误的,所以正确的做法应该是把这两个返回值存起来,在处理完所有的节点了以后再统一进行索引的修改。

        1.12 对称二叉树

        题目链接:. - 力扣(LeetCode)

class Solution {
private:
    // TreeNode* reverseTree(TreeNode* root){
    //     if(!root) return nullptr;
    //     TreeNode* left = reverseTree(root->left);
    //     TreeNode* right = reverseTree(root->right);
    //     root->left = right;
    //     root->right = left;
    //     return root;
    // }

    // bool j(TreeNode* root1, TreeNode* root2){
    //     if (!root1 && !root2) return true;
    //     if (!root1 || !root2) return false;
    //     // return root1->val == root2->val && j(root1->left,root2->left) && j(root1->right,root2->right); // 这个是判断两个是否完全相等,但是相等不一定对称
    // }
    bool judge(TreeNode* root1, TreeNode* root2){
        if(!root1 && !root2) return true;
        if(!root1 || !root2) return false;
        return root1->val == root2->val && judge(root1->left,root2->right) && judge(root1->right,root2->left);
    }
public:
    bool isSymmetric(TreeNode* root) {
        // if(!root) return true;
        // if(root->left->val != root->right->val) return false;
        // return root->left->left->val == root->right->right->val && root->left->right->val == root->right->left->val;
        // TreeNode* reversedTree =  reverseTree(root);

        if(!root) return true;
        if(root && !root->left && !root->right) return true;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        
        return judge(left,right);
    }
};

        这个题目在做的时候还踩了挺多坑的,首先是这个判断的条件,应该是头结点的两个子树,递归的不应该是左右节点,第二个就是判断的是对称,而不是两个是否相等,一开始考虑成两个是否相等了(想法本来是把原本的进行翻转,然后看两个是否相等),但是这样只能针对满二叉树,最后就是找到正确思路的时候,需要谨慎处理为空的情况。

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值