二叉树层序遍历和N叉树OJ题

目录

二叉树层序遍历

 二叉树最左边的节点:

 左子树之和

二叉树的右视图

二叉树每层的最大值

从上到下打印二叉树 III

 二叉树的层序遍历 II

二叉树的最大宽度

把二叉树转成累加树:

N叉树的前序遍历

N叉树的后序遍历 

N叉树层序遍历


二叉树层序遍历

对应letecode链接:

力扣

题目描述:

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例:
二叉树:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其层序遍历结果:

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

解题思路:

定义一个队列:将根节点放入队列中 如果左孩子和右孩子存在就将其放入队列中,在不断遍历队列,在遍历的过程中我们需要定义一个容器将一层的节点保存起来

首先拿出根节点,如果左子树或者右子树不为空,就将他们放入队列中。第一遍处理完后,根节点已经从队列中拿走了,而根节点的两个孩子已放入队列中了,现在队列中就有两个节点 2 和 5。

 第二次处理,会将 2 和 5 这两个节点从队列中拿走,然后再将 2 和 5 的子节点放入队列中,现在队列中就有三个节点 346

我们把每层遍历到的节点都放入到一个结果集中,最后返回这个结果集就可以了。 

 对应代码:


class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
            vector<vector<int>>ans;//保存答案
        
            queue<TreeNode*>q;//队列遍历
            if(root){//如果不为空则入root
                q.push(root);
            }
            while(!q.empty()){

                vector<int>res;
                int len=q.size();//遍历当前层
                for(int i=0;i<len;i++){
                        
                    TreeNode*ret=q.front();
                    q.pop();
                    res.push_back(ret->val);

                    if(ret->left){//左子树不为空则入左子树
                        q.push(ret->left);
                    }

                    if(ret->right){//右子树不为空则入右子树
                        q.push(ret->right);
                    }

                }
                ans.push_back(res);
            }
            return tmp;
    }
};

 二叉树最左边的节点:

对应letecode链接:

力扣

 题目描述:

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

输入: root = [2,1,3]
输出: 1
示例 2:

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

二叉树的节点个数的范围是 [1,104]
-231 <= Node.val <= 231 - 1 

 解题思路:

要找到最左节点我们可以利用层序遍历的思想:最后一层的第一个节点就是就是最左节点因此我们可以定义一个指针变量ans记录每一层第一个节点即可遍历完之后ans保存的就是最左边的节点就是层序遍历稍微变形如果层序遍历不太清楚的老铁可以去看一个我的二叉树基础博客

对应代码:

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
            queue<TreeNode*>q;
            if(root)q.push(root);
            TreeNode*ans=nullptr;//保存最左边的节点
            while(!q.empty()){
                int len=q.size();
                for(int i=0;i<len;i++){
                    auto top=q.front();
                    q.pop();
                    if(i==0){
                        ans=top;
                    }
                    if(top->left)q.push(top->left);
                    if(top->right)q.push(top->right);
                }
            } 

               return ans->val;   
    }
   
};

 左子树之和

对应letecode链接:

力扣

题目描述:

计算给定二叉树的所有左叶子之和。

示例:

    3
   / \
  9  20
    /  \
   15   7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

解题思路:我们依然可以使用层序遍历来解决这个问题:定义一个ans记录左叶子之和。先将根节点放入队列中,将其从队列中取出,如果他的左子树不为空则在判断左子树的左孩子和右孩子是否为空如果为空则将其值加到ans中。否则将其左子树放入队列中,在判断他的右子树是否为空如果不为空就将其放入队列中。重复上述操作我们就得到所有左叶子之和

我们会发现3这个节点的左节点是一个叶子节点则将其加到ans里面去不需要在将其放入队列中,在判断他的右孩子是不是空的如果不是空则将其放入队列中重复上述操作即可得到答案

对应代码:

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
               int ans=0;
               queue<TreeNode*>q;
               if(root)q.push(root);
               while(!q.empty()){
                   auto node=q.front();
                     q.pop();
                    if(node->left){
                        if(!node->left->left&&!node->left->right){//如果当前节点的左孩子是叶子节点则将其放入ans中
                        ans+=node->left->val;
                        }
                        else{//如果不是则将其放入队列中
                            q.push(node->left);
                        }
                    }
                    if(node->right){//右孩子不为空则将其放入队列中
                         q.push(node->right);
                    }
               }   

               return ans; 
    }
};

 递归写法:

class Solution {
public:
bool isLeafNode(TreeNode*node){
    return !node->left&&!node->right;
}
     int dfs(TreeNode*node){
         int ans=0;
         if(node->left){
           ans+=isLeafNode(node->left)?node->left->val:dfs(node->left);
         }

         if(node->right&&!isLeafNode(node->right)){
              ans+=dfs(node->right);
         }
         return ans;
     }
    int sumOfLeftLeaves(TreeNode* root) {
               return root==nullptr?0:dfs(root);
    }
};

有兴趣的老铁可以自己下去研究一个在这里就直接给出代码

二叉树的右视图

对应letecode链接

力扣

 题目描述:

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例 1:

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]
示例 2:

输入: [1,null,3]
输出: [1,3]
示例 3:

输入: []
输出: []

提示:

二叉树的节点个数的范围是 [0,100]
-100 <= Node.val <= 100

 解题思路:

同样是使用层序遍历:我也只需要记录每一次的最后一个节点即可,然后将每一层最后一个节点放入容器中即可与二叉树的最左节点一样

对应代码:


class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
                  queue<TreeNode*>q;
                  vector<int>list;
                  if(root)q.push(root);
                  TreeNode*ans=nullptr;//记录每一层最后一个节点
                  while(!q.empty()){
                      int len=q.size();
                      for(int i=0;i<len;i++){
                          auto node=q.front();
                          q.pop();
                          if(i==len-1){//如果是当前层的最后一个节点就可以赋值给node;
                            ans=node;
                          }
                          if(node->left)q.push(node->left);
                          if(node->right)q.push(node->right);
                      }
                      list.push_back(ans->val);//将其放入容器中
                  }
                  return list;
    }
};

二叉树每层的最大值

对应letecode链接:

力扣

题目描述:

给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。

示例1:

输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]
解释:
          1
         / \
        3   2
       / \   \  
      5   3   9 
示例2:

输入: root = [1,2,3]
输出: [1,3]
解释:
          1
         / \
        2   3
示例3:

输入: root = [1]
输出: [1]
示例4:

输入: root = [1,null,2]
输出: [1,2]
解释:      
           1 
            \
             2     
示例5:

输入: root = []
输出: []

提示:

二叉树的节点个数的范围是 [0,104]
-231 <= Node.val <= 231 - 1

和前面层序遍历的思路差不多定义一个变量记录每一层里面的最大值,在变量的过程中不断更新最大值

对应代码:

class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
                 vector<int>res;
                 queue<TreeNode*>q;
                 if(root) q.push(root);
                 while(!q.empty()){
                     int len=q.size();

                     int maxval=INT_MIN;//记录每一层的最大值

                     for(int i=0;i<len;i++){
                         TreeNode*top=q.front();
                         q.pop();
                         if(top->val>maxval) maxval=top->val;//更新最大值
                         、
                         if(top->left)q.push(top->left);
                         if(top->right)q.push(top->right);
                     }
                     
                     res.push_back(maxval);//将其放入容器中

                 }
                 return res;
    }
};

从上到下打印二叉树 III

对应letecod链接:

力扣

 题目描述:

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

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

    3
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

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

这个题和前面的题几乎是一模一样就只需要改一两行代码,我们只需要定义一个变量flag初始时设置成1然后每变量一层就改成相反数,如果flag是1就不需要翻转直接放入容器中即可。否则就要需要翻转才能放入容器中


class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
                queue<TreeNode*>q;
                int flag=1;
                vector<vector<int>>ans;
                if(root)q.push(root);
                while(!q.empty()){
                    int len=q.size();
                    vector<int>tmp;
                    for(int i=0;i<len;i++){
                        auto node=q.front();
                        q.pop();
                        tmp.push_back(node->val);
                        if(node->left){
                            q.push(node->left);
                        }
                        if(node->right){
                            q.push(node->right);
                        }
                    }
                    if(flag==1){
                        ans.push_back(tmp);
                    }
                    else{
                        reverse(tmp.begin(),tmp.end());//翻转当前层的元素
                        ans.push_back(tmp);
                    }
                    flag=-flag;
                }
                return ans;
    }
};

 二叉树的层序遍历 II

对应letecode链接:

力扣

题目描述:

给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

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

    3
   / \
  9  20
    /  \
   15   7
返回其自底向上的层序遍历为:

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

和之前的层序遍历一样我们可以用之前的层序遍历在每一层都放到一个容器里,然后在将其放到一个答案容器中在遍历结束之后将其翻转一下即可在这里我们可以使用算法库里面的reverse

对应代码:

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

               queue<TreeNode*>q;

               if(root)q.push(root);
               while(!q.empty()){
                   vector<int>res;
                   int len=q.size();
                   for(int i=0;i<len;i++){
                       auto top=q.front();
                       q.pop();
                       res.push_back(top->val);
                       if(top->left){
                           q.push(top->left);
                       }

                       if(top->right){
                           q.push(top->right);
                       }
                   }
                       ans.push_back(res);//将每一层放入容器中
               }
               reverse(ans.begin(),ans.end());//翻转
               return ans;
    }
};

二叉树的最大宽度

对应letecode链接:

力扣

题目描述:

给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。

每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

示例 1:

输入: 

           1
         /   \
        3     2
       / \     \  
      5   3     9 

输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。
示例 2:

输入: 

          1
         /  
        3    
       / \       
      5   3     

输出: 2
解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。
示例 3:

输入: 

          1
         / \
        3   2 
       /        
      5      

输出: 2
解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)。
示例 4:

输入: 

          1
         / \
        3   2
       /     \  
      5       9 
     /         \
    6           7
输出: 8
解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)。
注意: 答案在32位有符号整数的表示范围内。

解题思路:

将节点映射到数组中,那么不难发现一个节点和其子节点的下标呈这样的关系:

左子节点的下标 = 节点下标 * 2 + 1
右子节点的下标 = 节点下标 * 2 + 2

关键点在于这一层的长度是由这一层的最左侧节点与最右侧节点来计算。与树里面的值没有关系。于是我们可以利用之前的思路使用层序遍历,但此时我们还需要保存每一个节点的下标,所以我们队列里面存的应该是一个pair数组。我们将根节点的编号设置成1.在遍历每一层的时候定义一个变量start记录当前层的起始位置,定义一个变量index记录当前层最后一个节点的下标,这样我们就知道这一层的宽度了。由于题目要我们求最大的宽度所以我们在遍历每一层的时候需要定义一个变量maxwidth记录二叉树中的最大宽度,并在遍历的过程中不断更新maxwidth.

注意:每一层还要减去起始位置的下标

对应代码:

class Solution {
public:
    int widthOfBinaryTree(TreeNode* root) {
                  if(!root)return 0;
           queue<pair<TreeNode*,int>>q;
        q.push({root,1});
        int maxwidth=0;
        while(!q.empty()){
            int len=q.size();
            int start=q.front().second;
            int index=0;
            while(len--){
                auto  tmp=q.front().first;
                index=q.front().second;//起始位置
                q.pop();
                if(tmp->left){
                    q.push({tmp->left,2*index+1-2*start});
                }
                if(tmp->right){
                    q.push({tmp->right,2*index+2-2*start});
                }
            }
            maxwidth=max(maxwidth,index-start+1);//更新最大宽度
        }
        return maxwidth;
    }
};

把二叉树转成累加树:

对应letecode链接:

力扣

题目描述:

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。

示例 1:

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
示例 2:

输入:root = [0,null,1]
输出:[1,null,1]
示例 3:

输入:root = [1,0,2]
输出:[3,3,2]
示例 4:

输入:root = [3,2,4,1]
输出:[7,9,4,10]

提示:

树中的节点数介于 0 和 104 之间。
每个节点的值介于 -104 和 104 之间。
树中的所有值 互不相同 。
给定的树为二叉搜索树。

解题思路:

访问每个节点时,时刻维护变量 sum,保存「比当前节点值大的所有节点值的和」。
二叉搜索树的中序遍历,访问的节点值是递增的。
如果先访问右子树,反向的中序遍历,访问的节点值是递减的,之前访问的节点值都比当前的大,每次累加给 sum 即可。

对于每一个节点,先递归它的右子树,里面的节点值都比当前节点值大。

再 “处理” 当前节点,sum 累加上当前节点值,并更新当前节点值

再递归它的左子树,新访问的节点值比之前的都小,sum 保存着「比当前节点大的所有节点值的和」,累加上当前节点值,更新当前节点值。

不管访问哪个节点,sum 始终保存「比当前节点值大的所有节点值的和」。

对应代码:

class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
                     if(root==nullptr)return nullptr;
                     return dfs(root);
    }
    TreeNode* dfs(TreeNode*root){
        if(root==nullptr)return nullptr;
        stack<TreeNode*>st;
        TreeNode*node=root;
        long long sum=0;
        while(node||!st.empty()){
            while(node!=nullptr){
                st.push(node);//先入右节点
                node=node->right;
            }
            TreeNode*temp=st.top();
            st.pop();
            sum+=temp->val;//累加
            temp->val=sum;
            node=temp->left;
        }
        return root;
    }
};

N叉树的前序遍历

对应letecode链接:

力扣

题目描述:

给定一个 N 叉树,返回其节点值的 前序遍历 。

N 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。

进阶:

递归法很简单,你可以使用迭代法完成此题吗?

示例 1:

输入:root = [1,null,3,2,4,null,5,6]
输出:[1,3,5,6,2,4]
示例 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,6,7,11,14,4,8,12,5,9,13,10]

提示:

N 叉树的高度小于或等于 1000
节点总数在范围 [0, 10^4] 内

  1. 参考二叉树的递归遍历方式:先遍历根节点,然后递归遍历左子树,再递归遍历右子树。
  2. 二N叉树的前序遍历就是先遍历根节点,然后依次递归遍历每个子树。

 

 如果不是很懂二叉树的前中后序遍历的可以看一下我的二叉树基础那篇文章

对应代码:

class Solution {
public:
    vector<int> preorder(Node* root) {
          vector<int>ans;
          dfs(root,ans);//前序遍历
          return ans;
    }
  void dfs(Node*root,vector<int>&ans){
      if(!root)return;
      ans.push_back(root->val);//先访问他的根
        int len=root->children.size();
      for(int i=0;i<len;i++){
          auto n=root->children[i];
          if(n){//如果不为空则递归
              dfs(n,ans);
          }
      }
  }
};

非递归:

我们使用栈来帮助我们得到前序遍历,需要保证栈顶的节点就是我们当前遍历到的节点。

我们首先把根节点入栈,因为根节点是前序遍历中的第一个节点。随后每次我们从栈顶取出一个节点 u,它是我们当前遍历到的节点,并把 u 的所有子节点逆序推入栈中。例如 u 的子节点从左到右为 v1, v2, v3,那么推入栈的顺序应当为 v3, v2, v1,这样就保证了下一个遍历到的节点(即 u 的第一个子节点 v1)出现在栈顶的位置。

对应代码:

class Solution {
public:
    vector<int> preorder(Node* root) {
        stack<Node*>ans;
        vector<int>ret;
        if(root)ans.push(root);
        while(!ans.empty()){
            auto node=ans.top();
            ans.pop();
            int n=node->children.size();
            ret.push_back(node->val);
            for(int i=n-1;i>=0;i--){
                if(node->children[i])ans.push(node->children[i]);
            }
        }
        return ret;
    }
};

N叉树的后序遍历 

对应letecode链接:

力扣

题目描述:

给定一个 N 叉树,返回其节点值的 后序遍历 。

N 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。

进阶:

递归法很简单,你可以使用迭代法完成此题吗?

示例 1:

输入:root = [1,null,3,2,4,null,5,6]
输出:[5,6,3,2,4,1]
示例 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]
输出:[2,6,14,11,7,3,12,8,4,13,9,10,5,1]

提示:

N 叉树的高度小于或等于 1000
节点总数在范围 [0, 10^4] 内

对应代码:

lass Solution {
private:
    vector<int> result;
    void traversal (Node* root) {
        if (root == NULL) return;
        for (int i = 0; i < root->children.size(); i++) { // 子孩子
            traversal(root->children[i]);
        }
        result.push_back(root->val); // 中

    }

public:
    vector<int> postorder(Node* root) {
        result.clear();
        traversal(root);
        return result;

    }
};

非递归:

class Solution {
public:
    vector<int> postorder(Node* root) {
        vector<int> result;
        if (root == NULL) return result;
        stack<Node*> st;
        st.push(root);
        while (!st.empty()) {
            Node* node = st.top();
            st.pop();
            result.push_back(node->val);
            for (int i = 0; i < node->children.size(); i++) { // 相对于前序遍历,这里反过来
                if (node->children[i] != NULL) {
                    st.push(node->children[i]);
                }
            }
        }
        reverse(result.begin(), result.end()); // 反转数组
        return result;
    }
};

相对于前序遍历只需要稍微改一下即可 

N叉树层序遍历

对应letecode链接:

力扣

题目描述:

给定一个 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] 之间

解题思路:找到一层的起始位置(准备要遍历的数据)
在 1 的基础上遍历一层
在 2 的基础上遍历的同时向下遍历每个节点的子节点
在 3 的基础上,把遍历时遇到的子节点按左——>右的顺序保存起来
重复 1 ,直到在 3 里面没有子节点

对应代码:

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>>ans;
        queue<Node*>q;
        if(root)q.push(root);
        while(!q.empty()){
            int len=q.size();
            vector<int>tmp;
            for(int i=0;i<len;i++){
                auto top=q.front();
                q.pop();
                tmp.push_back(top->val);
                for(auto &cur:top->children){//将其子节点全部队列中
                    q.push(cur);
                }
            }
            ans.push_back(tmp);//保存到容器中把每一层
        }
        return ans;
    }
};

🙌🙌🙌🙌
结语:对于个人来讲,在leetcode上进行探索以及单人闯关是一件有趣的时间,一个程序员,如果不喜欢编程,那么可能就失去了这份职业的乐趣。刷到我的文章的人,我希望你们可以驻足一小会,忙里偷闲的阅读一下我的文章,可能文章的内容对你来说很简单,(^▽^)不过文章中的每一个字都是我认真专注的见证!希望您看完之后,若是能帮到您,劳烦请您简单动动手指鼓励我,我必回报更大的付出~ 

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个追梦的少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值