树的BFS专题练习

本文详细介绍了使用广度优先搜索(BFS)解决多种二叉树问题,包括对称二叉树的判断、锯齿形层序遍历、最小深度计算、每个树行的最大值查找以及N叉树的层序遍历。通过递归和迭代两种方法,展示了如何利用BFS的特性解决这些树形结构的问题,并给出了相应的代码实现。
摘要由CSDN通过智能技术生成

树的BFS专题练习


树的BFS类型题目,大致思路为:

  1. 创建一个队列,将所有入口节点放入其中

  2. 当队列非空时

    1. 对每个在队列中的节点
    2. 提取出元素
    3. 扩展该节点,将其子节点有序的放入队列中
    4. 进入下一层
101.对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

1

/
2 2
/ \ /
3 4 4 3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

1

/
2 2
\
3 3

进阶:

你可以运用递归和迭代两种方法解决这个问题吗?

这个题目在 剑指offer中看到过,剑指offer是用递归写的。思路是分别对左子树和右子树,进行前序遍历。左子树优先左节点,右子树优先右节点。代码大致如下:

bool isSymmetrical(BinaryTreeNode* pRoot)
{
    return isSymmetrical(pRoot, pRoot);
}

bool isSymmetrical(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
    if(!pRoot1 && !pRoot2)
        return true;
    if(!pRoot1 || !pRoot2)
        return false;
    if(pRoot1->val != pRoot2->val)
        return false;
    
    return isSymmetrical(pRoot1->left, pRoot2->right) && 
        isSymmetrical(pRoot1->right, pRoot2->left);
}

上述解决思路是用递归解决的,如果用迭代解决可以用bfs来解决,思路是初始化时把根节点的左右子节点加入队列中,循环时每次从队列中取出两个节点,如果两个节点值不同则表示其非对称二叉树。然后扩展这两个结点时,先取第一个节点的左子节点和第二个节点的右子节点加入队列,然后再将第一个节点的右子节点以及第二节点的左子节点加入队列。然后继续迭代。代码如下:

    bool isSymmetric(TreeNode* root) {
        // 用BFS 来做,除了root结点外,每次取两个结点,判断是否相等。若不相等则不行。
        if(root == nullptr || (root->left == nullptr && root->right==nullptr))
            return true;
        queue<TreeNode*> q;
        q.push(root->left);
        q.push(root->right);
        bool isOk = true;
        while(!q.empty())
        {
            TreeNode* left = q.front(); q.pop();
            TreeNode* right = q.front(); q.pop();

            if(!left && !right) continue;
            if(!left || !right)
            {
                isOk = false;
                break;
            }
            if(left->val != right->val)
            {
                isOk = false;
                break;
            }      
            q.push(left->left);
            q.push(right->right);
            q.push(left->right);
            q.push(right->left);
            //if(left == nullptr && right == nullptr)

        }

        return isOk;
    }
103.二叉树的锯齿形层序遍历

给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回锯齿形层序遍历如下:

[
[3],
[20,9],
[15,7]
]

这个题目是层序遍历的进阶版,它的难点在于奇数层和偶数层的打印顺序不一样,所以我们如果像层序遍历那样只用队列实现,那么只能完成奇数层的打印。偶数层是从右往左的,可以理解为先进后出,这很像栈。确实,我们完全可以维护一个队列和一个栈分别保存奇数层和偶数层的节点,然后根据层数的奇偶分别处理。这里我们先不这样麻烦,stl中提供了双端队列deque。那我们其实将节点依然保存在队列中,但是对每一层来说,都创建一个deque。如果当前是奇数层我们将数据从后往前插,反之则从前往后插。那么代码如下:

    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ans;
        if(root == nullptr)
            return ans;
        
        int levels = 1;
        queue<TreeNode* > Nodes;
        Nodes.push(root);

        while(!Nodes.empty())
        {
            deque<int> newVals;
            int size = Nodes.size();
            while(size--)
            {
                TreeNode* p = Nodes.front();
                Nodes.pop();

                if(levels %2 != 0) // 奇数层
                    newVals.push_back(p->val);
                else
                    newVals.push_front(p->val);

                if(p->left) Nodes.push(p->left);
                if(p->right) Nodes.push(p->right);
            }
            levels++;
            ans.push_back(vector<int>(newVals.begin(), newVals.end()));
        }

        return ans;
    }

这个题目剑指offer中也有,在剑指中叫 “之字形打印二叉树“,剑指中是用双栈实现的,其实想法是类似的。双栈也类似为双端队列。

111.二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明:叶子节点是指没有子节点的节点。

在这里插入图片描述

输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:

输入:root = [2,null,3,null,4,null,5,null,6]
输出:5

提示:

树中节点数的范围在 [0, 105] 内
-1000 <= Node.val <= 1000

这个题目找最小的深度,我们用bfs来写,因为bfs是一层一层,逐渐向下的。所以当我们找到第一个叶子节点时,那么此时的深度一定是该二叉树的最小深度。代码如下

    int minDepth(TreeNode* root) {
        // bfs 找到第一个 叶子结点即可
        int minDepth = 0;
        if(root == nullptr)
            return minDepth;
        queue<TreeNode* > Nodes;
        Nodes.push(root);
        int flag = false;   // 是否找到最小深度
        while(!Nodes.empty() && !flag)
        {
            int size = Nodes.size();
            minDepth ++;
            while(size--)
            {
                TreeNode* pNode = Nodes.front();
                Nodes.pop();

                // 找到第一个叶子结点,退出。
                if(!pNode->left && !pNode->right)
                {
                    flag = true;
                    break;
                }
                if(pNode->left) Nodes.push(pNode->left);
                if(pNode->right) Nodes.push(pNode->right);
            }
        }

        return minDepth;
    }
515.在每个树行中找最大值

您需要在二叉树的每一行中找到最大的值。

示例:

输入:

      1
     / \
    3   2
   / \   \  
  5   3   9 

输出: [1, 3, 9]

找到每一个行的最大值,我们只要对其层序遍历,然后每层找一个最大值即可。代码如下

    vector<int> largestValues(TreeNode* root) {
        vector<int> ans;
        if(root == nullptr)
            return ans;
        queue<TreeNode*> Nodes;
        Nodes.push(root);

        while(!Nodes.empty())
        {
            int size = Nodes.size();
            int maxRowNum = Nodes.front()->val;
            while(size--)
            {
                TreeNode* pNode = Nodes.front();
                Nodes.pop();

                if(maxRowNum < pNode->val)
                    maxRowNum = pNode->val;
                
                if(pNode->left) Nodes.push(pNode->left);
                if(pNode->right) Nodes.push(pNode->right);
            }
            ans.push_back(maxRowNum);
        }

        return ans;
    }
429.N叉树的层序遍历

给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

示例 1:
在这里插入图片描述

输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]
示例 2:
在这里插入图片描述

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]

提示:

树的高度不会超过 1000
树的节点总数在 [0, 10^4] 之间

题目提供节点结构如下

class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};

这个题目和二叉树的层序遍历没什么区别,只不过是扩展节点稍微有点变化,二叉树是扩展其left和right子节点。这里是其子节点都在 children这个vector中,所以我们扩展时只要遍历children即可。代码如下:

    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> ans;
        if(root == nullptr)
            return ans;
        queue<Node*> Nodes;
        Nodes.push(root);

        while(!Nodes.empty())
        {
            int size = Nodes.size();
            vector<int> v;
            while(size--)
            {
                Node* pNode = Nodes.front();
                Nodes.pop();

                v.push_back(pNode->val);
                for(auto e: pNode->children)
                {
                    if(e) Nodes.push(e);
                }
            }
            ans.push_back(v);
        }  
        
        return ans;
    }

纸上得来终觉浅,绝知此事要躬行,继续加油。做一个快乐的菜鸟。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值