代码随想录day15-二叉树(3)

代码随想录day15-二叉树(3)

1、LeetCode 101 对称二叉树

题目分析:

本题的核心在于如何判定一颗树是不是镜像对称的呢?如果说一棵树的左孩子的左侧和右孩子的右侧是相等的,左孩子的右侧和右孩子的左侧是相等的,如此下去,这个数就是镜像对称的,所以,这个题目我们要根据这一个点进行判断。
本题还是使用递归法,提到递归法,我们就得心里有递归的三部曲:

  • 递归的返回值以及参数
    由于要判断就一个结点的左右子树是不是相等的,所以传入的参数应该就是两个结点,而相等与否说明返回值就是布尔类型。
bool symmetric(TreeNode* leftNode, TreeNode* rightNode)
  • 递归的终止条件
    在之前写遍历的的时候,递归的终止条件一般都是if (node == nullptr) return;,但是这个题明显就不是。
    其实递归的终止条件我们不能想复杂了,他不就是把一些特殊的情况剔出来嘛。为什么我们之前遍历二叉树的时候需要有上面那个判定呢?
    我们递归的代码是:
ans.empalce_back(node->val);
traversal(node->left);
traversal(node->right);

这每一条语句都有一个大前提,那就是node不能为空,node要是为空的话,每一句话都会报错,所以这就是为什么要寻找递归的终止条件。
我们回到这个题目,很明显,我们进行递归的时候的处理如下:

bool outSide = symmetric(leftNode->left, rightNode->right);  // 外侧进行判定
bool inSide = symmetric(leftNode->right, rightNode->left);  // 内侧进行判定

这里要判定就是左孩子的左结点和右孩子的右结点,那么前提是什么呢,肯定要是这两个结点不为空啊,如果为空,不是直接就报错了吗,所以递归的终止条件就是在这个上面做文章。那么这两个结点是否为空一共有四种情况:

  • 两者都为空,说明都同时走到了叶子结点,表明这一侧是对称的,符合条件;
  • 两者中只有一个为空,说明肯定不是对称的,不符合条件;
  • 两者都不为空,但是两者的值不相等,说明不是对称的,不符合条件;
  • 两者都不为空,且两者相等,说明两者暂时是对称的,需要递归进下一层继续查看。
    注意: 最后一个情况说的是暂时是对称的,具体是不是还需要进入到下面的层去比较,这里不就是应该进入到递归了吗。
    根据以上的分析,我们可以得到递归的终止条件为:
if (!leftNode && !rightNode) return true;  // 左右都是空,这个树肯定是对称的
else if (leftNode && !rightNode) return false;  // 左结点为空,右结点不为空,肯定不是对称的
else if (!leftNode && rightNode) return false;  // 同理
else if (leftNode->val != rightNode->val) return false;  // 都不为空,但是值不相等

所以递归函数为:

    bool symmetric(TreeNode* leftNode, TreeNode* rightNode) {
        // 这个递归的终止条件有一点复杂,一定要注意你这个递归函数是干什么的
        if (!leftNode && !rightNode) return true;  // 左右都是空,这个树肯定是对称的
        else if (leftNode && !rightNode) return false;  // 左结点为空,右结点不为空,肯定不是对称的
        else if (!leftNode && rightNode) return false;  // 同理
        else if (leftNode->val != rightNode->val) return false;  // 都不为空,但是值不相等

        // 接下来就是需要递归的情况了
        bool outSide = symmetric(leftNode->left, rightNode->right);  // 外侧进行判定
        bool inSide = symmetric(leftNode->right, rightNode->left);  // 内侧进行判定
        return outSide && inSide;
    }
题目解答:

递归法:

class Solution {
public:
    bool symmetric(TreeNode* leftNode, TreeNode* rightNode) {
        // 这个递归的终止条件有一点复杂,一定要注意你这个递归函数是干什么的
        if (!leftNode && !rightNode) return true;  // 左右都是空,这个树肯定是对称的
        else if (leftNode && !rightNode) return false;  // 左结点为空,右结点不为空,肯定不是对称的
        else if (!leftNode && rightNode) return false;  // 同理
        else if (leftNode->val != rightNode->val) return false;  // 都不为空,但是值不相等

        // 接下来就是需要递归的情况了
        bool outSide = symmetric(leftNode->left, rightNode->right);  // 外侧进行判定
        bool inSide = symmetric(leftNode->right, rightNode->left);  // 内侧进行判定
        return outSide && inSide;
    }

    bool isSymmetric(TreeNode* root) {
        // 判断对称其实就是判断左右子树是不是对称的
        TreeNode* leftNode = root->left;
        TreeNode* rightNode = root->right;
        return symmetric(leftNode, rightNode);
    }
};

当然本题使用迭代法也是可以解决的。

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        // 迭代法使用队列来实现
        if (root == nullptr) return true;

        queue<TreeNode*> que;
        que.push(root->left);
        que.push(root->right);

        while (!que.empty()) {
            // 每次取出外面的两个
            TreeNode* leftNode = que.front();
            que.pop();
            TreeNode* rightNode = que.front();
            que.pop();

            // 还是判断那几个条件
            if (!leftNode && !rightNode) continue;  //  注意这个时候千万不可以return,因为只能说明外侧是对称的,内侧的情况还不知道呢
            if (!leftNode || !rightNode || (leftNode->val != rightNode->val)) return false;

            que.push(leftNode->left);  // 左左
            que.push(rightNode->right);  // 右右
            que.push(leftNode->right);  // 左右
            que.push(rightNode->left);  // 右左
        }
        return true;
    }
};

迭代法使用的是队列来实现的,需要注意的有一个点:

  • leftNode == nullptr && rghtNode == nullptr的时候,这个时候不能直接return,因为此时只能判断外侧是对称的,内测是什么情况完全不知道。

2、LeetCode 104 二叉树的最大深度

题目分析:

本题考查求二叉树的最大深度,首先要明确深度的定义。对于这个题目,我们可以使用前序遍历,后序遍历以及层序遍历都是可以做的。
使用前序遍历才是真正的求深度,里面有回溯的思想;使用后序遍历求的其实是高度,也是可以接受;使用层序遍历的话直接用模板,最后看看有几个vector即可。

首先来分析后序遍历:

  • 确定函数的参数以及返回值
    由于是需要求深度,所以返回值肯定就是深度,参数就是node。
int getDepth(TreeNode* node)
  • 确定递归的终止条件
    确定递归的终止条件,我们就得明白我们这个函数是干嘛的,我们这个函数求的就是当前结点的深度,当然使用后序遍历就是高度。如果我们的递归终止条件设置为if (node == nullptr) return 0;,这里的含义就是,此时node已经是空结点了,所以相应的高度就是0,这是可以的。
    但同时,我们根据题目,[二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。],这里面到了叶子结点,那么我们的递归的终止条件能不能改成if (!node->left && !node->right) return 1;,因为当前的是叶子结点,所以这里的高度就是1,此时就可以返回1,所以终止条件改成这个也是合理的。
    但是这里很容易犯错,就是我们使用了node->left来判定,我们对node进行了为空的判定吗?所以这里要使用第二种,还要对node进行判定。
if (node == nullptr) return 0;
if (!node->left && !node->right) return 1;

但是其实这样的话,第二个其实就可要可不要了,但是这个也反映了我们思考的过程

  • 单层的递归逻辑
    由于我们这里使用的是后序遍历,所以单层的递归逻辑如下:
 int leftDepth = getDepth(node->left);  // 左
 int rightDepth = getDepth(node->right);  // 右
 return 1 + max(leftDepth, rightDepth);  // 需要加上当前结点,所以需要加1

层序遍历:
本题使用层序遍历就直接套模板就行了,最后返回vector的个数即可。

前序遍历的思路分析:
前序遍历可以参看这里的总结。

题目解答:

后序遍历:

class Solution {
public:
    int getDepth(TreeNode* node) {
        // 求深度可以使用后序遍历也可以使用前序遍历
        // 使用后序遍历本质上求的是高度
        if (!node) return 0;
        if (!node->left && !node->right) return 1;  // 这里我两个条件都写了

        // 这里先使用后序遍历
        int leftDepth = getDepth(node->left);  // 左
        int rightDepth = getDepth(node->right);  // 右
        return 1 + max(leftDepth, rightDepth);  // 需要加上当前结点,所以需要加1
    }

    int maxDepth(TreeNode* root) {
        return getDepth(root);
    }
};

层序遍历:

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (!root) return 0;
        // 说白了就是找层数,可以使用广度优先搜索
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            depth++;  // 遍历完一层深度加1
        }
        return depth;
    }
};

3、LeetCode 111 二叉树的最小深度

题目分析:
本题和二叉树的最大深度非常类似,但是有一个地方很容易出错。
代码随想录day15-二叉树(3)_第1张图片
图片来源
就是如果根节点只有左孩子或者右孩子的情况,最小的深度不是1

题目解答:
后序递归法:

class Solution {
public:
    int getDepth(TreeNode* node) {
        if (node == nullptr) return 0;

        // 注意处理根节点只有左子树或只有右子树的情况
        if (!node->left && node->right) return 1 + getDepth(node->right);  // 左子树没有的情况
        if (node->left && !node->right) return 1 + getDepth(node->left);  // 右子树没有的情况

        // 正常的情况
        int leftDepth = getDepth(node->left);
        int rightDepth = getDepth(node->right);
        return 1 + min(leftDepth, rightDepth);
    }

    int minDepth(TreeNode* root) {
        return getDepth(root);
    }
};

层序遍历法:

class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;

        queue<TreeNode*> que;
        que.push(root);
        int depth = 1;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                // 注意这里使用了node的left和right,所以我们要保证node不为空
                if (!node->left && !node->right) return depth;  // 如果当前结点为叶子结点,直接返回即可
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            depth++;
        }
        return depth;
    }
};

这里第二个题的前序遍历的解法涉及到回溯和递归的配合,和第513题找树左下角的值非常之类似。还与层序遍历的递归解法以及257题二叉树的所有路径相似,我们到时候一起总结一下这几个。我们在这里进行了总结。

你可能感兴趣的:(代码随想录刷题,leetcode,算法,数据结构)