二叉树——队列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;
    }
};

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

相关推荐
小森77671 小时前
(三)机器学习---线性回归及其Python实现
人工智能·python·算法·机器学习·回归·线性回归
振鹏Dong1 小时前
超大规模数据场景(思路)——面试高频算法题目
算法·面试
uhakadotcom1 小时前
Python 与 ClickHouse Connect 集成:基础知识和实践
算法·面试·github
uhakadotcom1 小时前
Python 量化计算入门:基础库和实用案例
后端·算法·面试
uhakadotcom2 小时前
使用 Python 与 BigQuery 进行交互:基础知识与实践
算法·面试
uhakadotcom2 小时前
使用 Hadoop MapReduce 和 Bigtable 进行单词统计
算法·面试·github
XYY3692 小时前
前缀和 一维差分和二维差分 差分&差分矩阵
数据结构·c++·算法·前缀和·差分
longlong int3 小时前
【每日算法】Day 16-1:跳表(Skip List)——Redis有序集合的核心实现原理(C++手写实现)
数据库·c++·redis·算法·缓存
24白菜头3 小时前
C和C++(list)的链表初步
c语言·数据结构·c++·笔记·算法·链表
刺客-Andy3 小时前
前端加密方式 AES对称加密 RSA非对称加密 以及 MD5哈希算法详解
前端·javascript·算法·哈希算法