进阶刷题-BFS(更新中)

BFS的核心思想形象地将就是当面对岔路口时我们要每个都进去走一步,重点在于保存每个路口。一般用到队列的数据结构,使用其先进先出的特性。
LeetCode102-二叉树的层序遍历 Middle
典型的DFS题目,使用队列保存树的每层节点,按层遍历。
在这里插入图片描述

class Solution
{
public:
    vector<vector<int>> levelOrder(TreeNode *root)
    {
        queue<TreeNode *> q; //保存树的节点
        vector<vector<int>> res;
        if (!root)
        {
            return res;
        }
        q.push(root);
        while (!q.empty()) //每次进入while循环都是对于一层节点的遍历
        {
            res.push_back(vector<int>());
            int len = q.size();
            for (int i = 0; i < len; i++) //对于当前一层的节点一次遍历并入队其存在的左右子树
            {
                TreeNode *temp = q.front();
                res.back().push_back(temp->val);
                q.pop(); //每个节点访问后都出队,这样每次都只访问新的一层的节点
                if (temp->left)
                    q.push(temp->left);
                if (temp->right)
                    q.push(temp->right);
            }
        }
        return res;
    }
};

LeetCode107-二叉树的层序遍历II Middle
绝了,就是102题的代码加一行reverse~,就当复习BFS编程了。
在这里插入图片描述

class Solution
{
public:
    vector<vector<int>> levelOrderBottom(TreeNode *root)
    {
        queue<TreeNode *> q;
        vector<vector<int>> res;
        if (!root)
            return res;
        q.push(root);
        while (!q.empty())
        {
            int len = q.size();
            res.push_back(vector<int>());
            for (int i = 0; i < len; i++)
            {
                TreeNode *temp = q.front();
                q.pop();
                res.back().push_back(temp->val);
                if (temp->left != nullptr)
                    q.push(temp->left);
                if (temp->right != nullptr)
                    q.push(temp->right);
            }
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

LeetCode103-二叉树的锯齿形层序遍历 Middle
笑开花了,这两道题过得也太容易了。还是基础BFS加上reverse~
在这里插入图片描述

class Solution
{
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode *root)
    {
        queue<TreeNode *> q;
        vector<vector<int>> res;
        if (!root)
            return res;
        q.push(root);
        while (!q.empty())
        {
            int len = q.size();
            res.push_back(vector<int>());
            for (int i = 0; i < len; i++)
            {
                TreeNode *temp = q.front();
                q.pop();
                res.back().push_back(temp->val);
                if (temp->left != nullptr)
                    q.push(temp->left);
                if (temp->right != nullptr)
                    q.push(temp->right);
            }
        }
        for (int i = 0; i < res.size(); i++)
        {
            if (i % 2 != 0)
                reverse(res[i].begin(), res[i].end());
        }
        return res;
    }
};

LeetCode226-翻转二叉树 Easy
核心思路:对于每个非叶子节点,交换其左右子树,递归或迭代的交换下去
编程其实很简单,三种方法一看就会。递归与迭代两种方法时间复杂的均为O(N),但空间复杂度迭代法使用O(叶子节点数目)的空间,递归法使用O(树高度)的空间。
递归(自下而上)

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

        return;
    }
};

递归(自上而下)

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

BFS-迭代

class Solution
{
public:
    TreeNode *invertTree(TreeNode *root)
    {
        queue<TreeNode *> q;
        if (root == nullptr)
            return root;
        q.push(root);
        while (!q.empty())
        {
            int len = q.size();
            TreeNode *temp;
            for (int i = 0; i < len; i++)
            {
                temp = q.front();
                TreeNode *tmp = temp->left;
                temp->left = temp->right;
                temp->right = tmp;
                q.pop();
                if (temp->left != nullptr)
                    q.push(temp->left);
                if (temp->right != nullptr)
                    q.push(temp->right);
            }
        }
        return root;
    }
};

116. 填充每个节点的下一个右侧节点指针 Middle
对于结果是层次遍历的二叉树,没啥好说的,直接BFS,但是看到题目额外要求不使用额外空间,我们便应该想到使用题目中已有的空间或元素进行空间优化。
先来看一下直接BFS的思路以及代码:
直接使用了经典BFS的模板进行修改,用额外的队列保存一层的所有节点,达到横向的遍历。

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node *> q; //保存树的节点
        vector<vector<int>> res;
        if (!root)
        {
            return root;
        }
        q.push(root);
        while (!q.empty()) //每次进入while循环都是对于一层节点的遍历
        {
            res.push_back(vector<int>());
            int len = q.size();
            for (int i = 0; i < len; i++) //对于当前一层的节点一次遍历并入队其存在的左右子树
            {
                Node *temp = q.front();
                res.back().push_back(temp->val);
                q.pop(); //每个节点访问后都出队,这样每次都只访问新的一层的节点
                if(i<len-1) temp->next=q.front();
                if (temp->left)
                    q.push(temp->left);
                if (temp->right)
                    q.push(temp->right);
            }
        }
        return root;
    }
    
};

接下来考虑优化,在遍历每一层时我们遇到的问题就是需要找到当前节点的next节点,上一种方法中,我们遍历每一层节点的同时,通过队列保存的下一个节点,对当前节点进行next连接的建立。
当不使用额外的队列用于保存当前层次节点时,我们面对的问题是想找到next节点只能通过上层节点的指针,那我们不妨在每一层节点上对下一层节点进行连接建立,这样可以方便的使用本层所建立的连接,用于寻找相关节点。
由于题目限制了二叉树为完美二叉树,所以,对于当前层次每一个节点,其左节点next指向右节点,右节点通过当前节点的next找到兄弟节点,并将next指向兄弟节点的左节点。
上代码:

class Solution
{
public:
    Node *connect(Node *root)
    {
        if (root == nullptr)
        {
            return root;
        }
        Node *head = root, *l = root;//l用于保存下一层最左侧的节点
        while (l->left != nullptr)//外层循环用于一层一层向下遍历
        {
            head = l;
            while (head != nullptr)//内层循环用于一层内的横向遍历
            {
                head->left->next = head->right;//先将左儿子的next指向右儿子
                if (head->next != nullptr)
                {
                    head->right->next = head->next->left;//通过当前节点的next为右儿子寻找next
                }
                head = head->next;
            }
            l = l->left;
        }
        return root;
    }
};

117. 填充每个节点的下一个右侧节点指针 II Middle
本题代码源自官方题解
说实话这题给我搞的心态有点崩,总体的思路和上一题类似,但是当为一个儿子寻找next时,我最初的想法是为每个儿子找到他的next,但是这样就需要分情况讨论,最后代码写的自己都看不下去,完全就是思路的混乱。
看了题解发现自己就是绕进去了,完全可以转换角度,我们每次保存当前最后一个没有建立next连接的儿子,然后对于遍历到的每个儿子节点,都用于和最后一个有效的儿子节点建立连接,只需要额外写一个函数就好…

class Solution
{
public:
    void handle(Node *&last, Node *&p, Node *&nextStart)
    {
        if (last)
        {
            last->next = p;
        }
        if (!nextStart)
        {
            nextStart = p;
        }
        last = p;
    }

    Node *connect(Node *root)
    {
        if (!root)
        {
            return nullptr;
        }
        Node *start = root;
        while (start)
        {
            Node *last = nullptr, *nextStart = nullptr;
            for (Node *p = start; p != nullptr; p = p->next)
            {
                if (p->left)
                {
                    handle(last, p->left, nextStart);
                }
                if (p->right)
                {
                    handle(last, p->right, nextStart);
                }
            }
            start = nextStart;
        }
        return root;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值