222. 完全二叉树的节点个数

image.png
image.png

思路一:普通二叉树

递归法

求解二叉树的节点个数,可以将问题分解为求左子树的节点个数和右子树的节点个数,再求和

  1. class Solution {
  2. private:
  3. int getNodesNum(TreeNode* cur) {
  4. if (cur == 0) return 0;
  5. int leftNum = getNodesNum(cur->left); // 左
  6. int rightNum = getNodesNum(cur->right); // 右
  7. int treeNum = leftNum + rightNum + 1; // 中
  8. return treeNum;
  9. }
  10. public:
  11. int countNodes(TreeNode* root) {
  12. return getNodesNum(root);
  13. }
  14. };

代码精简后:

class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == NULL) return 0;
        return 1 + countNodes(root->left) + countNodes(root->right);
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(logn),算上了递归系统栈占用的空间

    迭代法

    class Solution {
    public:
      int countNodes(TreeNode* root) {
          queue<TreeNode*> que;
          if (root == nullptr) return 0;
          que.push(root);
          int count = 0;
          while (!que.empty()) {
              int size = que.size(); // 当前层的节点个数
              count += size;
              for (int i = 0; i < size; i++) {
                  TreeNode* node = que.front();
                  que.pop();
                  if (node->left != nullptr) que.push(node->left);
                  if (node->right != nullptr) que.push(node->right);
              }
          }
          return count;
      }
    };
    
    image.png

    思路二:完全二叉树

    完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。

对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。

对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。

如何判断一个数是不是满二叉树:遍历到树的最左端,记录一个深度,遍历到最右端,记一个深度,如果两个深度相同,那么就是满二叉树

class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        // 左节点一直往左,右节点一直往右,如果是满二叉树,就可以用2^n - 1来计算节点哥舒翰
        int lLen = 0, rLen = 0;
        while (left) {
            left = left->left;
            ++lLen;
        }
        while (right) {
            right = right->right;
            ++rLen;
        }
        if (rLen == lLen) {
            return (2 << lLen) - 1; // 相当于 2^(len + 1) - 1, len 为子树的深度,总的树高应为 len + 1
        }
        // 如果不是子树不是满二叉树,那么递归处理左右子树,早晚得碰到满二叉树(最差的情况子树一个节点)
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};
  • 时间复杂度:O(logn * logn)
  • 空间复杂度:O(logn)

110. 平衡二叉树

image.png
image.png

递归法

自底向上递归

  1. 明确返回值和参数

参数为传入的节点指针,返回值要返回传入节点为根节点树的深度。
那么如何标记左右子树是否差值大于1呢。
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。

int getDepth(TreeNode* node)
  1. 明确终止条件:遇到空节点终止,返回0,表示当前节点为根结点时,树的高度为0

    if (!node) return 0;
    
  2. 明确单层递归的逻辑

判断是否为平衡二叉树,要考虑左右子树的高度差,如果差值小于1,则返回当前二叉树的高度,否则返回-1,表示不是平衡二叉树

int leftDepth = depth(node->left); // 左
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right); // 右
if (rightDepth == -1) return -1;

int result;
if (abs(leftDepth - rightDepth) > 1) {  // 中
    result = -1;
} else {
    result = 1 + max(leftDepth, rightDepth); // 以当前节点为根节点的最大高度
}

return result;

代码精简后:

int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
if (rightDepth == -1) return -1;
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);

getDepth的整体代码

int getDepth(TreeNode* node) {
    if (node == NULL) {
        return 0;
    }
    int leftDepth = getDepth(node->left);
    if (leftDepth == -1) return -1;
    int rightDepth = getDepth(node->right);
    if (rightDepth == -1) return -1;
    return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
}

本题的完整代码:

class Solution {
public:
    // 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
    int getDepth(TreeNode* node) {
        if (node == NULL) {
            return 0;
        }
        int leftDepth = getDepth(node->left);
        if (leftDepth == -1) return -1; // 说明左子树已经不是二叉平衡树
        int rightDepth = getDepth(node->right);
        if (rightDepth == -1) return -1; // 说明右子树已经不是二叉平衡树
        return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
    }
    bool isBalanced(TreeNode* root) {
        return getDepth(root) == -1 ? false : true;
    }
};
class Solution {
public:
    int dfs(TreeNode* root) {
        if (root == NULL) return 0;
        int left = dfs(root->left);
        int right = dfs(root->right);

        if (left == -1 || right == -1 || abs(left - right) > 1)
            return -1;
        return max(left, right) + 1;
    }

    bool isBalanced(TreeNode* root) {
        return dfs(root) == -1 ? false : true;

    }
};

image.png

自顶向下递归

class Solution {
public:
    int getHeight(TreeNode* root) {
        if (root == nullptr) return 0;
        return max(getHeight(root->left), getHeight(root->right)) + 1;
    }
    bool isBalanced(TreeNode* root) {
        if (root == nullptr) return true;
        return abs(getHeight(root->left) - getHeight(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }
};

image.png


迭代法

此题用迭代法,其实效率很低,因为没有很好的模拟回溯的过程,所以迭代法有很多重复的计算。
虽然理论上所有的递归都可以用迭代来实现,但是有的场景难度可能比较大。
因为对于回溯算法已经是非常复杂的递归了,如果在用迭代的话,就是自己给自己找麻烦,效率也并不一定高。

本题的迭代方式可以先定义一个函数,专门用来求高度。
这个函数通过栈模拟的后序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度)
代码如下:

// cur节点的最大深度,就是cur的高度
int getDepth(TreeNode* cur) {
    stack<TreeNode*> st;
    if (cur != NULL) st.push(cur);
    int depth = 0; // 记录深度
    int result = 0;
    while (!st.empty()) {
        TreeNode* node = st.top();
        if (node != NULL) {
            st.pop();
            st.push(node);                          // 中
            st.push(NULL);
            depth++;
            if (node->right) st.push(node->right);  // 右
            if (node->left) st.push(node->left);    // 左

        } else {
            st.pop();
            node = st.top();
            st.pop();
            depth--;
        }
        result = result > depth ? result : depth;
    }
    return result;
}

然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:

bool isBalanced(TreeNode* root) {
    stack<TreeNode*> st;
    if (root == NULL) return true;
    st.push(root);
    while (!st.empty()) {
        TreeNode* node = st.top();                       // 中
        st.pop();
        if (abs(getDepth(node->left) - getDepth(node->right)) > 1) { // 判断左右孩子高度是否符合
            return false;
        }
        if (node->right) st.push(node->right);           // 右(空节点不入栈)
        if (node->left) st.push(node->left);             // 左(空节点不入栈)
    }
    return true;
}

整体代码:

class Solution {
private:
    int getDepth(TreeNode* cur) {
        stack<TreeNode*> st;
        if (cur != NULL) st.push(cur);
        int depth = 0; // 记录深度
        int result = 0;
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();
                st.push(node);                          // 中
                st.push(NULL);
                depth++;
                if (node->right) st.push(node->right);  // 右
                if (node->left) st.push(node->left);    // 左

            } else {
                st.pop();
                node = st.top();
                st.pop();
                depth--;
            }
            result = result > depth ? result : depth;
        }
        return result;
    }

public:
    bool isBalanced(TreeNode* root) {
        stack<TreeNode*> st;
        if (root == NULL) return true;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                       // 中
            st.pop();
            if (abs(getDepth(node->left) - getDepth(node->right)) > 1) {
                return false;
            }
            if (node->right) st.push(node->right);           // 右(空节点不入栈)
            if (node->left) st.push(node->left);             // 左(空节点不入栈)
        }
        return true;
    }
};