【C++代码】二叉树的最大深度,二叉树的最小深度,完全二叉树的节点个数--代码随想录

题目:二叉树的最大深度

  • 给定一个二叉树 root ,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
题解
  • 如果我们知道了左子树和右子树的最大深度 l 和 r,那么该二叉树的最大深度即为 m a x ( l , r ) + 1 max(l,r)+1 max(l,r)+1 。而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们可以用「深度优先搜索」的方法来计算二叉树的最大深度。具体而言,在计算当前二叉树的最大深度时,可以先递归计算出其左子树和右子树的最大深度,然后在 O(1) 时间内计算出当前二叉树的最大深度。递归在访问到空节点时退出。

  • /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
     * };
     */
    class Solution {
          
          
    public:
        int maxDepth(TreeNode* root) {
          
          
            if(root==nullptr)
            return 0;
            return max(maxDepth(root->left),maxDepth(root->right))+1;
        }
    };
    
  • 时间复杂度:O(n),其中 n 为二叉树节点的个数。每个节点在递归中只被遍历一次。空间复杂度:O(height),其中 height 表示二叉树的高度。递归函数需要栈空间,而栈空间取决于递归的深度,因此空间复杂度等价于二叉树的高度。

  • 广度优先搜索

    • 用「广度优先搜索」的方法来解决这道题目,但我们需要对其进行一些修改,此时我们广度优先搜索的队列里存放的是「当前层的所有节点」。每次拓展下一层的时候,不同于广度优先搜索的每次只从队列里拿出一个节点,我们需要将队列里的所有节点都拿出来进行拓展,这样能保证每次拓展完的时候队列里存放的是当前层的所有节点,即我们是一层一层地进行拓展,最后我们用一个变量 ans 来维护拓展的次数,该二叉树的最大深度即为 ans。

    • class Solution {
              
              
      public:
          int maxDepth(TreeNode* root) {
              
              
              if(root==nullptr)
              return 0;
              // return max(maxDepth(root->left),maxDepth(root->right))+1;
              queue<TreeNode*> temp_que;
              temp_que.push(root);
              int ans = 0;
              while(!temp_que.empty()){
              
              
                  int sz = temp_que.size();
                  // cout<<sz<<endl;
                  while(sz>0){
              
              
                      TreeNode* node=temp_que.front();
                      temp_que.pop();
                      if(node->left){
              
              
                          temp_que.push(node->left);
                      }
                      if(node->right){
              
              
                          temp_que.push(node->right);
                      }
                      sz-=1;
                  }
                  ans+=1;
              }
              return ans;
          }
      };
      

题目:二叉树的最小深度

  • 给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
题解
  • 首先可以想到使用深度优先搜索的方法,遍历整棵树,记录最小深度。对于每一个非叶子节点,我们只需要分别计算其左右子树的最小叶子节点深度。这样就将一个大问题转化为了小问题,可以递归地解决该问题。

  • /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
     * };
     */
    class Solution {
          
          
    public:
        int minDepth(TreeNode* root) {
          
          
            if(root==nullptr){
          
          
                return 0;
            }
            if(root->left==nullptr&&root->right==nullptr){
          
          
                return 1;
            }
            int min_depth=INT_MAX;
            if(root->left!=nullptr){
          
          
                min_depth=min(minDepth(root->left),min_depth);
            }
            if(root->right!=nullptr){
          
          
                min_depth=min(minDepth(root->right),min_depth);
            }
            return min_depth+1;
        }
    };
    
  • 时间复杂度:O(N),其中 N 是树的节点数。对每个节点访问一次。空间复杂度:O(H),其中 H 是树的高度。空间复杂度主要取决于递归时栈空间的开销,最坏情况下,树呈现链状,空间复杂度为 O(N)。平均情况下树的高度与节点数的对数正相关,空间复杂度为 O(log⁡N)。

  • 广度优先搜索

    • 当我们找到一个叶子节点时,直接返回这个叶子节点的深度。广度优先搜索的性质保证了最先搜索到的叶子节点的深度一定最小

    • class Solution {
              
              
      public:
          int minDepth(TreeNode* root) {
              
              
              if(root==nullptr){
              
              
                  return 0;
              }
              queue<pair<TreeNode *,int>> temp_que;
              temp_que.emplace(root,1);
              while(!temp_que.empty()){
              
              
                  TreeNode *temp_node = temp_que.front().first;
                  int depth = temp_que.front().second;
                  temp_que.pop();
                  if(temp_node->left==nullptr&&temp_node->right==nullptr){
              
              
                      return depth;
                  }
                  if(temp_node->left!=nullptr){
              
              
                      temp_que.emplace(temp_node->left,depth+1);
                  }
                  if(temp_node->right!=nullptr){
              
              
                      temp_que.emplace(temp_node->right,depth+1);
                  }
              }
              return 0;
          }
      };
      
    • 时间复杂度:O(N),其中 N 是树的节点数。对每个节点访问一次。空间复杂度:O(N),其中 N 是树的节点数。空间复杂度主要取决于队列的开销,队列中的元素个数不会超过树的节点数

题目:完全二叉树的节点个数

  • 给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1 ~ 2 h 1~ 2^h 12h 个节点。

    • 在这里插入图片描述
题解
  • 深度优先遍历数的所有节点,不过没有使用到完全二叉树的性质。时间复杂度为 O(n),空间复杂度为 O(1)【不考虑递归调用栈】

  • class Solution {
          
          
    public:
        int countNodes(TreeNode* root) {
          
          
            int count=0;
            if(root==nullptr){
          
          
                return 0;
            }
            int left=countNodes(root->left);
            int right=countNodes(root->right);
            return left+right+1;
        }
    };
    
  • 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。确定终止条件:如果为空节点的话,就返回0,表示节点数为0。确定单层递归的逻辑:先求它的左子树的节点数量,再求右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量

  • 对于任意二叉树,都可以通过广度优先搜索或深度优先搜索计算节点个数,时间复杂度和空间复杂度都是 O(n),其中 n 是二叉树的节点个数。这道题规定了给出的是完全二叉树,因此可以利用完全二叉树的特性计算节点个数。规定根节点位于第 0 层,完全二叉树的最大层数为 h。根据完全二叉树的特性可知,完全二叉树的最左边的节点一定位于最底层,因此从根节点出发,每次访问左子节点,直到遇到叶子节点,该叶子节点即为完全二叉树的最左边的节点,经过的路径长度即为最大层数 h。当 0 ≤ i < h 0≤i<h 0i<h 时,第 i 层包含 2 i 2^i 2i个节点,最底层包含的节点数最少为 1,最多为 2 h 2^h 2h

    • 当最底层包含 1 个节点时,完全二叉树的节点个数是 ∑ i = 0 h − 1 2 i + 1 = 2 h \sum_{i=0}^{h-1}2^i+1=2^h i=0h12i+1=2h;

    • 当最底层包含 2 h 2^h 2h 个节点时,完全二叉树的节点个数是 ∑ i = 0 h 2 i = 2 h + 1 − 1 \sum_{i=0}^{h}2^i=2^{h+1}-1 i=0h2i=2h+11

  • 因此对于最大层数为 h 的完全二叉树,节点个数一定在 [ 2 h , 2 h + 1 − 1 ] [2^h,2^{h+1}-1] [2h,2h+11] 的范围内,可以在该范围内通过二分查找的方式得到完全二叉树的节点个数。具体做法是,根据节点个数范围的上下界得到当前需要判断的节点个数 k,如果第 k 个节点存在,则节点个数一定大于或等于 k,如果第 k 个节点不存在,则节点个数一定小于 k,由此可以将查找的范围缩小一半,直到得到节点个数

  • 在这里插入图片描述

  • 如何判断第 k 个节点是否存在呢?如果第 k 个节点位于第 h 层,则 k 的二进制表示包含 h+1 位,其中最高位是 1,其余各位从高到低表示从根节点到第 k 个节点的路径,0 表示移动到左子节点,1 表示移动到右子节点。通过位运算得到第 k 个节点对应的路径,判断该路径对应的节点是否存在,即可判断第 k 个节点是否存在222. 完全二叉树的节点个数 - 力扣(LeetCode)

  • class Solution {
          
          
    public:
        bool exists(TreeNode *root,int high,int k){
          
          
            int bits=1<<(high-1);
            TreeNode* node =root;
            while(node!=nullptr&&bits>0){
          
          
                if(!(bits&k)){
          
          
                    node=node->left;
                }else{
          
          
                    node=node->right;
                }
                bits>>=1;
            }
            return node!=nullptr;
        }
        int countNodes(TreeNode* root) {
          
          
            if(root==nullptr){
          
          
                return 0;
            }
            // int left=countNodes(root->left);
            // int right=countNodes(root->right);
            // return left+right+1;
            int high=0;
            TreeNode *node = root;
            while(node->left!=nullptr){
          
          
                high++;
                node = node->left;
            }
            int min_count=1<<high,max_count=(1<<(high+1))-1;
            while(min_count<max_count){
          
          
                int mid=(max_count-min_count+1)/2+min_count;
                if(exists(root,high,mid)){
          
          
                    min_count=mid;
                }else{
          
          
                    max_count=mid-1;
                }
            }
            return min_count;
        }
    };
    
  • 时间复杂度: O ( log ⁡ 2 n ) O(\log^2 n) O(log2n),其中 n 是完全二叉树的节点数。 首先需要 O(h) 的时间得到完全二叉树的最大层数,其中 h 是完全二叉树的最大层数。 使用二分查找确定节点个数时,需要查找的次数为 O ( log ⁡ 2 h ) = O ( h ) O(\log 2^h)=O(h) O(log2h)=O(h),每次查找需要遍历从根节点开始的一条长度为 h 的路径,需要 O(h) 的时间,因此二分查找的总时间复杂度是 O ( h 2 ) O(h^2) O(h2)。 因此总时间复杂度是 O ( h 2 O(h^2 O(h2。由于完全二叉树满足 2 h ≤ n < 2 h + 1 2^h \le n < 2^{h+1} 2hn<2h+1,因此有 O ( h ) = O ( log ⁡ n ) O(h)=O(\log n) O(h)=O(logn) O ( h 2 ) = O ( log ⁡ 2 n ) O(h^2)=O(\log^2 n) O(h2)=O(log2n)。空间复杂度:O(1)。只需要维护有限的额外空间。

  • 解法3:判断其子树是不是满二叉树,如果是则利用公式计算这个子树(满二叉树)的节点数量,如果不是则继续递归,那么 在递归三部曲中,第二部:终止条件的写法应该是这样的:

    • if (root == nullptr) return 0; 
      // 开始根据左深度和右深度是否相同来判断该子树是不是满二叉树
      TreeNode* left = root->left;
      TreeNode* right = root->right;
      int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
      while (left) {
              
                // 求左子树深度
          left = left->left;
          leftDepth++;
      }
      while (right) {
              
               // 求右子树深度
          right = right->right;
          rightDepth++;
      }
      if (leftDepth == rightDepth) {
              
              
          return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,返回满足满二叉树的子树节点数量
      }
      
  • 递归三部曲,第三部,单层递归的逻辑:(可以看出使用后序遍历)

    • int leftTreeNum = countNodes(root->left);       // 左
      int rightTreeNum = countNodes(root->right);     // 右
      int result = leftTreeNum + rightTreeNum + 1;    // 中
      return result;
      

猜你喜欢

转载自blog.csdn.net/weixin_43424450/article/details/133048396