力扣二叉树专题(二)-二叉树的层序遍历的10道题集 迭代和递归C++实现 思路 详细注释

二叉树的层序遍历

力扣二叉树专题(一)介绍了二叉树的深度优先遍历,前中后序的递归、迭代、统一迭代实现。二叉树还有一种遍历方式就是广度优先遍历,而层序遍历就是应用在二叉树上的广度优先遍历。

层序遍历一个二叉树,就是从左到右一层一层的去遍历二叉树。

栈先进后出适合模拟深度优先遍历,即递归逻辑;队列先进先出,符合一层一层遍历的逻辑,即层序遍历的逻辑。

再次回顾,对于二叉树的遍历总共8种:

  • 前序遍历
  • 中序遍历
  • 后续遍历
  • 深度优先搜索(DFS)
  • 宽度优先搜索(BFS)
  • Morris(莫里斯)的前中后3种遍历方式

深度优先搜索(DFS)力扣二叉树专题(一),以下是BFS宽度优先搜索(BFS)中层序遍历的C++实现

为了巩固,每个题都用了DFS和BFS实现

★★★1. 二叉树的层序遍历-题102-遍历法和迭代法

这道题提供了层序遍历的模板!后面的九个题都可以套用!

迭代法思路: 先处理根节点,即存入大vector容器result中;再使用队列从左到右访问单层节点,并把当前层的所有节点值存在小vector容器v中。

迭代法做法:

  1. 使用队列que遍历节点
  2. 先处理根节点,往队列que存入根节点
  3. 再从上之下,处理单层节点
  4. 单层处理时,从左至右依次遍历节点,遍历次数就是队列que当前的大小,并准备一个小容器存放当前层所有节点值
  5. 遍历当前层时,访问队列头部节点,弹出,存入小容器。然后队列需要按照从左至右的顺序更新为下一层的节点。
  6. 当前层遍历结束后,小容器存入大容器,即当前层所有节点值存入
class Solution {
public:
   //1.迭代法
   vector<vector<int>> levelOrder(TreeNode* root) {
       vector<vector<int>> result;

       //1.使用队列遍历节点
       queue<TreeNode*> que;
       //2.先处理根节点 队列存入根节点
       if(root!=nullptr) que.push(root);

       //3.再处理下一层节点
       while(!que.empty())
       {
           //4.每一层处理 每一层从左至右依次遍历节点 遍历次数就是队列的大小
           int size = que.size();
           vector<int> v;//存放每一层的节点值

           for(int i=0; i<size ;i++)
           {
               TreeNode* cur = que.front();//访问队列头部节点
               que.pop();
               v.push_back(cur->val);//存入当前层的所有节点值

               //5.当前层节点处理完,队列元素需要更新为下一层的节点 左到右的顺序
               if(cur->left) que.push(cur->left);
               if(cur->right) que.push(cur->right);
           }
           //6.当前层的所有节点值存入大容器中
           result.push_back(v);
       }
       return result;
   }
};

递归法思路: 单层所有节点依次存入容器中,注意递归法的三个确定。

递归三个确定

  1. 确定参数和返回值 参数应该是要传入指针 存放结果的容器 当前层的节点个数;不需要返回值 存入结果即可
  2. 确定结束条件:所有的节点都遍历完了,指针为空则结束
  3. 确定单次递归操作:当前层的节点依次存入容器

递归法做法:

  1. 初始状态应该是传入根节点、存放结果的容器以及默认为0的深度
  2. 首先判断节点是否为空,为空则说明遍历结束,直接返回;
  3. 然后,剔除空树的情况
  4. 接着,才是单层处理,当前层的所有节点存入容器即可
  5. 最后,递归,按照从左到右的顺序,依次处理下一层的左节点、右节点
class Solution {
public:
	  //2.递归
	  void order(TreeNode* cur, vector<vector<int>>& result, int depth)
	   {
	       //1.递归结束 空指针不入队列
	       if(cur==nullptr) return;
	       //2.空树情况
	       if(result.size()==depth) result.push_back(vector<int>());
	       //3.当前层节点存入容器
	       result[depth].push_back(cur->val);
	
	       //下一层节点 深度要+1 假设每个节点都有两个孩子 当前节点有两个子节点
	       order(cur->left, result, depth+1);//左
	       order(cur->right, result, depth+1);//右
	   }
	   vector<vector<int>> levelOrder(TreeNode* root)
	   {
	       vector<vector<int>> result;
	       int depth = 0;
	       order(root, result, depth);
	       return result;
    }
};

2. 二叉树的层次遍历II-题107

在题102的基础上,反转数组即可。

递归法

class Solution {
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int depth)
    {
        //递归结束条件
        if(cur==nullptr) return;
        //空树
        if(result.size()==depth) result.push_back(vector<int>());
        //单层存入
        result[depth].push_back(cur->val);
        //递归 下一层 从左至右
        order(cur->left, result, depth+1);
        order(cur->right, result, depth+1);
    }
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> result;
        int depth=0;
        order(root, result, depth);
        reverse(result.begin(), result.end());
        return result;
    }
};

迭代法

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> result;
        queue<TreeNode*> que;

        //1.根节点
        if(root!=nullptr) que.push(root);
        
        //下一层节点
        while(!que.empty())
        {
            int size = que.size();//遍历次数
            vector<int> v;//存放每一层所有节点值

            for(int i=0; i<size; i++)
            {
                TreeNode* cur=que.front();
                que.pop();
                v.push_back(cur->val);//存放当前层节点
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            result.push_back(v);//存放当前层所有节点
        }

        reverse(result.begin(), result.end());
        return result;
    }
};

3. 二叉树的右视图-题199

迭代法: 每次存节点时,保证存放的是当前层最后一个节点即可

class Solution {
public:
	  //1.迭代法
	   vector<int> rightSideView(TreeNode* root) {
	       vector<int> result;
	       queue<TreeNode*> que;
	       //先处理根节点
	       if(root!=nullptr) que.push(root);
	       while(!que.empty())
	       {
	           int size = que.size();
	           for(int i=0;i<size;i++)
	           {
	               TreeNode* cur=que.front();
	               que.pop();
	               //注意!不能只存放右节点!而是存放右视图看到的节点!
	               if(i==size-1) result.push_back(cur->val);//存放右节点 也就是每一层最后一个节点
	               if(cur->left) que.push(cur->left);
	               if(cur->right) que.push(cur->right);
	           }
	       }
	       return result;
	   }
};

递归法——搬运sweetiee的思路

按照 「根结点 -> 右子树 -> 左子树」 的顺序访问,就可以保证每层都是最先访问最右边的节点,获取右视图的节点。

那么左视图就可以按照先序遍历的顺序 「根结点 -> 左子树 -> 右子树」 得到,先序遍历每层最先访问的是最左边的节点。

开始的时候,写的是根节点-左节点-右节点,输出一直是左视图。看到甜姨的思路才恍然大悟!

要注意,不能只存放右节点值,而是存放右视图看到的节点。如果只存放右节点,对于根节点只有一个左孩子的类似情况,无法正确存入。

class Solution {
public:
    //递归法
    void order(TreeNode* cur, vector<int>& result, int depth)
    {
        //1.递归结束条件 指针为空
        if(cur==nullptr) return;

        //3.只存放右视图看到的节点
        if(depth==result.size()) result.push_back(cur->val);
        depth++;
        order(cur->right, result, depth);
        order(cur->left, result, depth);
    }
    vector<int> rightSideView(TreeNode* root) {
        vector<int> result;
        int depth=0;
        order(root, result, depth);
        return result;
    }
};

4. 二叉树的层平均值-题637

思路: 在题107的基础上多了一个求和求均值的步骤,其他一样

迭代法:

class Solution {
public:
    //迭代法
    vector<double> averageOfLevels(TreeNode* root) {
        vector<double> result;
        queue<TreeNode*> que;
        //先处理根节点
        if(root!=nullptr) que.push(root);
        while(!que.empty())
        {
            double size = que.size();
            double sum = 0;
            for(int i=0;i<size;i++)
            {
                TreeNode* cur = que.front();
                que.pop();
                sum += cur->val;//所有节点值求和
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            result.push_back(sum/size);//存入当前层的平均值
        }
        return result;
    }
};

递归法

  • 想用map key表示节点数,value表示和,但是节点数不一定是1、2、3…,有可能每一层只有一个节点
  • 用一个vector,这个直接存放均值,但是不知道怎么维护节点值的和
  • 可以用vector和pair来创建对组,first存放单层节点数,second存放单层节点和,但最后还是要另外每一组求均值存在另一个vector中
  • 直接用两个vector,一个维护节点和,一个维护节点数
class Solution {
public:
    //递归法
    void order(TreeNode* cur, vector<double>& sum, vector<int>& num, int depth)//3.确定递归传入的参数和返回值
    {
        //1.确定递归结束条件
        if(cur==nullptr) return;
        //2.确定递归单次操作 求和 保存单层节点数
        //多节点
        if(depth<sum.size())
        {
            sum[depth] += cur->val;
            num[depth] += 1;
        }
        //单节点
        else
        {
            sum.push_back(1.0* cur->val);
            num.push_back(1);
        }
        if(cur->left) order(cur->left, sum, num, depth+1);
        if(cur->right) order(cur->right, sum, num, depth+1);
    }

    vector<double> averageOfLevels(TreeNode* root) {
        //求和 节点数
        vector<double> sum;
        vector<int> num;
        int depth=0;
        order(root, sum, num, depth);

        //求均值
        vector<double> result;
        int size = sum.size();
        for(int i=0;i<size;i++)
        {
            result.push_back(sum[i]/num[i]);
        }
        return result;
    }
};

★★★5. N叉树的层序遍历-题429-遍历法和迭代法

N叉树模板!

迭代法: 重点在于孩子节点依次存入队列中

class Solution {
public:
    //迭代法
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> result;
        queue<Node*> que;
        if(root!=NULL) que.push(root);
        while(!que.empty())
        {
            int size=que.size();
            vector<int> v;
            for(int i=0;i<size;i++)
            {
                Node* cur=que.front();
                que.pop();
                v.push_back(cur->val);
                //不同点 孩子节点放进队列中
                for(int j=0;j<cur->children.size();j++)
                {
                    if(cur->children[j]) que.push(cur->children[j]);
                }
            }
            result.push_back(v);
        }
        return result;
    }
};

递归法: 重点在于递归时孩子节点的更新

class Solution {
public:
    //递归法
    void order(Node* cur, vector<vector<int>>& result, int depth)
    {
        if(cur==NULL) return;//递归结束条件
        if(depth==result.size()) result.push_back(vector<int>());//空树
        result[depth].push_back(cur->val);
        //更新节点
        for(int i=0;i<cur->children.size();i++)
        {
            order(cur->children[i], result, depth+1);
        }

    }
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> result;
        int depth=0;
        order(root, result, depth);
        return result;
    }
};

6. 在每个树行中找最大值-题515

迭代法思路: 层序遍历,取每一层的最大值。关键在于,先假设一个最大值,最大值与节点值比较

class Solution {
public:
    //迭代法
    vector<int> largestValues(TreeNode* root) {
        vector<int> result;
        queue<TreeNode*> que;
        if(root!=nullptr) que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            int max = INT_MIN;
            for(int i=0;i<size;i++)
            {
                TreeNode* cur = que.front();
                que.pop();
                //找最大值
                max = max > cur->val? max : cur->val;
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            result.push_back(max);
        }
        return result;
    }
};

递归法思路: 递归法还是传入存放结果的容器、节点指针、树的深度。在找最大值时,通过深度控制,不停地比较容器存放的值与当前节点值。深度与容器大小相等时,说明该层节点都比较完了。

class Solution {
public:
    //递归
    void order(TreeNode* cur, vector<int>& result, int depth)
    {
        if(cur==nullptr) return;
        //单次操作 找最大值 更新节点
        //找最大值时,不停比较 单层节点都比较
        if(depth==result.size()) result.push_back(cur->val);
        else result[depth] = max(result[depth], cur->val);
        order(cur->left, result, depth+1);
        order(cur->right, result, depth+1);
    }
    vector<int> largestValues(TreeNode* root)
    {
        vector<int> result;
        int depth = 0;
        order(root, result, depth);
        return result;
    }
};

7. 填充每个节点的下一个右侧节点指针-题116-满二叉树

迭代法

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if(root!=NULL) que.push(root);
        while(!que.empty())
        {
            int size=que.size();
            Node* cur;
            Node* pre;
            for(int i=0;i<size;i++)
            {
                //根节点
                if(i==0)
                {
                    pre = que.front();
                    que.pop();
                    cur=pre;//记录当前层头节点
                }
                else
                {
                    cur = que.front();
                    que.pop();
                    pre->next=cur;//上一层父节点连向其左子节点;左子节点连向右子节点  本层前一个节点next指向本节点
                    pre=pre->next;//pre更新为左子节点;pre更新为当前层的下一个节点    节点偏移
                }
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            pre->next=NULL;//当前层最后一个节点指向空 
        }
        return root;
    }
};

递归法

class Solution {
public:
    //递归法
    void link(Node* root)
    {
        if(root==NULL || root->left==NULL) return;//说明遍历结束
        root->left->next = root->right;//左节点指向右节点
        if(root->next) root->right->next = root->next->left;
        link(root->left);
        link(root->right);
    }
    Node* connect(Node* root)
    {
        link(root);
        return root;
    }
};

if(root->next) root->right->next = root->next->left;的解释:

这是个满二叉树,前面已经判断过root->left !=null 才能走到这个逻辑。

所以,root的下一层必然是满的,因此if(root->next),也就是说root->next !=null表明了,root必不是该层的最右节点。

所以root->right、root->right-next、root->next->left都不可能为null,只有root到该层最右节点时,root->right、root->right-next、root->next->left为null,最右节点指向null

8. 填充每个节点的下一个右侧节点指针II-题117-二叉树

和上一题类似,只是这个是二叉树,不是满二叉树。

迭代法: 可以直接套用上一题的代码

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        //1.空指针不入队列
        if(root!=NULL) que.push(root);

        //2.开始遍历所有节点
        while(!que.empty())
        {
            //3.两个指针 遍历当前层的节点数 一个指针记录当前层头节点 一个用来连接
            Node* pre;
            Node* cur;
            int size = que.size();

            //4.遍历当前层节点
            for(int i=0; i<size; i++)
            {
                //5.处理头节点 
                if(i==0)
                {
                    pre = que.front();
                    que.pop();
                    cur = pre;//记录头节点
                }
                else
                {
                    cur = que.front();//访问要处理的节点
                    que.pop();
                    pre->next = cur;//本层上一个节点 连向 本层的节点
                    pre = pre->next;//pre偏移 更新  让当前节点成为前一个节点
                }
                //6.更新队列
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            //最右节点指向空
            pre->next = NULL;
        }
        return root;
    }
};

递归法:
首先判断当前节点的状态:1.节点为空 2.节点有左右子节点 3.节点只有左子节点 4.节点有右子节点。根据节点的不同状态作不同的处理,最后对函数做递归调用实现对节点左右子树的遍历。具体过程看注释

注意: 一定要先遍历右子树,完成对右子树节点next域的构建,然后再对左子树遍历,才能实现对左子树中孤立左子节点和右子节点next域的构建

class Solution {
public:
    //递归法
    //1.找到有效节点
    Node* findNode(Node* node)
    {
        if(node==NULL) return NULL;//4.空节点 返回null
        else
        {
            //节点非空
            while(node)
            {
                if(node->left) return node->left;//2.只有左子节点
                if(node->right) return node->right;//3.右只有右子节点
                node = node->next;//1.节点有左右子节点
            }
            return node;
        }
    }

    //2.递归
    Node* connect(Node* root)
    {
        //1.如果节点为空,返回null 遍历结束
        if(root==NULL)  return root;

        //如果节点的左右子节点都不为空,三种情况判断
        //3.只有右节点非空,遍历当前节点的next域,寻找符合条件的next指向
        if(root->right) root->right->next = findNode(root->next);
        //4.只有左节点非空 还需要判断右节点是否为空
        //右节点不为空 左节点指向右节点;
        //右节点为空,就要遍历当前节点的next域,寻找符合条件的next指向 左节点指向符合条件的next指向
        if(root->left) root->left->next = root->right ? root->right : findNode(root->next);
        //5.递归  先更新右子树节点,再是左子树 不能反
        //因为要先把右子树的next域构建好,左子树才能在节点的next域找到正确的next指向。
        //如果反着来,会先构建左子树,导致当前节点的next域还未构建好,遗漏掉一些节点的next域,使结果出错
        connect(root->right);
        connect(root->left);
        return root;
    }
};

★★★9. 二叉树的最大深度-题104

找深度模板!

  1. 迭代法,两个数组存节点,最后返回大数组的大小,超笨的方法
class Solution {
public:
    //1.迭代法
    int maxDepth(TreeNode* root) {
        queue<TreeNode*> que;
        vector<vector<int>> V;
        if(root!=nullptr) que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            vector<int> v;
            for(int i=0;i<size;i++)
            {
                TreeNode* cur = que.front();
                que.pop();
                v.push_back(cur->val);
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            V.push_back(v);
        }
        return (int)V.size();

    }
};
  1. 迭代法,一个数组存节点,当前层遍历完,计数,时间提升了
class Solution {
public:
    //2.迭代法
    int maxDepth(TreeNode* root) {
        queue<TreeNode*> que;
        int count = 0;
        if(root!=nullptr) que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            vector<int> v;
            for(int i=0;i<size;i++)
            {
                TreeNode* cur = que.front();
                que.pop();
                v.push_back(cur->val);
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            count++;
        }
        return count;
    }
};
  1. 迭代法,不用数组存节点,当前层遍历完,计数,时间介于1、2之间,内存比1、2好一点点,汗
class Solution {
public:
    //3.迭代法
	int maxDepth(TreeNode* root)
    {
        queue<TreeNode*> que;
        int depth=0;
        if(root) que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            //遍历当前层所有节点
            for(int i=0;i<size;i++)
            {
                TreeNode* cur = que.front();
                que.pop();
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            depth++;
        }
        return depth;
    }
};
  1. 递归法
    找出终止条件:当前节点为空
    找出返回值:节点为空时,说明高度为0,返回0;节点不为空时,则分别求左右子树的高度的最大值max,同时加1( 表示当前节点的高度),返回max+1
class Solution {
public:
    //4.递归法
    int maxDepth(TreeNode* root)
    {
        if(root==nullptr) return 0;
        int l = maxDepth(root->left);
        int r = maxDepth(root->right);
        return max(l,r)+1;
    }
};

10. 二叉树的最小深度-题111

和上面那题很像,先说递归法,分四种情况

  1. 空节点,返回0;
  2. 父节点有两个左右子节点,返回1+左右子树的最小值,1表示上一层;
  3. 只有左子节点,返回1+左子树;
  4. 只有右节点,返回1+右子树
class Solution {
public:
    //递归法
    int minDepth(TreeNode* root) 
    {
        if(root==nullptr) return 0;
        int num=1;
        int l=0, r=0;
        if(root->left!=nullptr && root->right!=nullptr)
        {
            l = minDepth(root->left);
            r = minDepth(root->right);
            num +=  min(l, r);
        }
        else if(root->left!=nullptr && root->right==nullptr)
        {
            num += minDepth(root->left);
        }
        else num += minDepth(root->right);
        return num;
    }
};

迭代法:要注意只有左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点

class Solution {
public:
    //迭代法
    int minDepth(TreeNode* root)
    {
        queue<TreeNode*> que;
        if(root==nullptr) return 0; 
        int depth = 0;
        que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            depth++;
            for(int i=0;i<size;i++)
            {
                TreeNode* cur=que.front();
                que.pop();
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
                if(cur->left==nullptr && cur->right==nullptr) return depth;//遍历到最低点
            }
        }
        return depth;
    }
};

太好了,这10个题结束了!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值