二叉树——队列bfs专题

1.N叉树的层序遍历

我们之前遇到过二叉树的层序遍历,只需要用队列先进先出的特性就可以达到层序遍历的目的。

而这里不是二叉树,也就是说让节点的孩子入队列时不仅仅是左右孩子了,而是它的所有孩子。而我们看这棵多叉树的构造,它的孩子是存储在数组中的。所以我们在让孩子入队时只需要依次让数组中的所有节点入队列即可。

cpp 复制代码
class Node 
{
public:
    int val;
    vector<Node*> children;
};

而这道题的要求是返回一个二维数组,数组的元素就是每一层的层序遍历结果。

我们关键点就在于如果确定元素属于那一层,方法就是在进行层序遍历前,我们先统计该队列的大小,大小即使这一层元素的个数,当这个数变为0,表示这一层的元素已经统计完了,这时队列中的就是下一层的元素了。此时重复求大小和层序遍历的过程即可。

cpp 复制代码
class Solution 
{
public:
    vector<vector<int>> levelOrder(Node* root) 
    {
        if(root == nullptr) return {};

        queue<Node*> q;
        q.push(root);
        vector<vector<int>> ret;

        int currentFloorSize = 0;
        while(!q.empty())
        {
            vector<int> currentFloor;
            currentFloorSize = q.size();
            while(currentFloorSize--)
            {
                Node* head = q.front(); //获取队头元素
                for(int i=0; i<head->children.size(); ++i) //让队头孩子入队
                {
                    q.emplace(head->children[i]);
                }
                currentFloor.emplace_back(head->val);//保存队头val
                q.pop();//出队
            }
            ret.emplace_back(currentFloor);
        }
        return ret;
    }
};

2.二叉树的锯齿形层序遍历

依旧是二叉树的层序遍历,只不过在遍历的时候有顺序之分,奇数层从左到右,偶数层从右到左。

解法依旧是先采取正常的层序遍历,多定义一个flag变量用了记录当前是奇数还是偶数。当把这一行统计完毕后,在将该结果插入到数组之前,先对flag进行判断,如果是偶数就插入反转之后的数组,如果奇数则直接插入即可。

cpp 复制代码
class Solution 
{
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) 
    {
        if(root == nullptr) return {};
        
        // 根节点入队列
        queue<TreeNode*> q;
        q.emplace(root);
        vector<vector<int>> ret;

        int currentFloorSize =0;
        int flag = 1; // 遍历层的方向,奇数层left->right, 偶数层right->left
        while(!q.empty())
        {
            currentFloorSize = q.size();
            vector<int> currentFloorEele;
            while(currentFloorSize--)
            {   
                // 保存节点数据
                TreeNode* head = q.front();
                q.pop();
                currentFloorEele.emplace_back(head->val);

                // 孩子入队列
                if(head->left) q.emplace(head->left);
                if(head->right) q.emplace(head->right);
            }
            if(flag % 2 == 0)
                reverse(currentFloorEele.begin(), currentFloorEele.end());
            ret.emplace_back(currentFloorEele);
            flag++;
        }
        return ret;
    }
};

3.在每个树行中找最大值

题目要求返回一个数组,数组的元素都是二叉树每一层的最大值。

解法:我们只需要在进行层序遍历的过程中,进行最大值的统计即可。当遍历完该层后,直接保存最大值即可。

cpp 复制代码
class Solution 
{
public:
vector<int> largestValues(TreeNode* root) 
    {
        if(root == nullptr) return {};

        queue<TreeNode*> q;
        q.emplace(root);
        vector<int> ret;
        
        int currentFloorSize = 0;
        while(!q.empty())
        {
            currentFloorSize = q.size();
            int maxVal = INT_MIN;
            while(currentFloorSize--)
            {
                auto head = q.front();
                q.pop();
                
                maxVal = max(maxVal, head->val);
                if(head->left) q.emplace(head->left);
                if(head->right) q.emplace(head->right);
            }
            ret.emplace_back(maxVal);
        }
        return ret;
    }
};

4.二叉树最大深度

返回所有层中最宽的。其中,我们只需要看每一层的最左和最右即可,其中也要计算中间的空节点。

解法:我们将二叉树以顺序方式进行存储,存储对用的根节点以及对应的下标。其实就是堆的存储方式。当我们根节点从0开始,他的左右孩子可以通过2*x+1,2*x+2得来。这样下来,每一层的宽度其实就是最左与最右节点的下标的差值+1.

cpp 复制代码
class Solution 
{
public:
    int widthOfBinaryTree(TreeNode* root) 
    {
        vector<pair<TreeNode*, unsigned int>> LevelSize; // 模拟队列,将二叉树以顺序结构存储
        unsigned int ret = 0; // 统计结果
        LevelSize.emplace_back(root, 0);

        while(!LevelSize.empty())
        {
            // 取出该层的首尾节点,并更新结果
            auto& [x1, y1] = LevelSize[0];
            auto& [x2, y2] = LevelSize.back();
            ret = max(ret, y2 - y1 + 1);

            // 让孩子入队
            vector<pair<TreeNode*, unsigned int>> tmp; // 临时存储下一层的节点,最后覆盖原队列
            for(auto& [x, y] : LevelSize)
            {
                if(x->left) tmp.emplace_back(x->left, y * 2 + 1);
                if(x->right) tmp.emplace_back(x->right, y * 2 + 2);
            }

            // 更新层
            LevelSize = tmp;
        }
        return ret;
    }
};

说明:我们这里使用数组来模拟的队列,因为我们让孩子入队列后得头删上一层的元素,在数组中头删消耗比较大,所以我们可以用一个临时数组来统计下一层的元素,之后用临时数组覆盖原数组即可。

相关推荐
C语言魔术师17 分钟前
62.不同路径
算法·leetcode·动态规划
fantasy_417 分钟前
LeetCode238☞除自身以外数组的乘积
java·数据结构·python·算法·leetcode
椰羊~王小美22 分钟前
贪心算法和动态规划
算法·贪心算法·动态规划
啊阿狸不会拉杆1 小时前
人工智能数学基础(二):初等数学
人工智能·python·算法
元亓亓亓1 小时前
LeetCode热题100--560.和为K的子数组(前缀和)--中等
算法·leetcode·职场和发展
Phoebe鑫1 小时前
数据结构每日一题day12(链表)★★★★★
数据结构·算法·链表
Vacant Seat2 小时前
贪心算法-跳跃游戏II
算法·游戏·贪心算法
夜松云2 小时前
从对数变换到深度框架:逻辑回归与交叉熵的数学原理及PyTorch实战
pytorch·算法·逻辑回归·梯度下降·交叉熵·对数变换·sigmoid函数
八股文领域大手子2 小时前
深入浅出限流算法(三):追求极致精确的滑动日志
开发语言·数据结构·算法·leetcode·mybatis·哈希算法
新时代苦力工3 小时前
处理对象集合,输出Map<String, Map<String, List<MyObject>>>格式数据,无序组合键处理方法
java·数据结构·list