代码随想录算法训练营第14天 | LeetCode226.反转二叉树、LeetCode101.对称二叉树、LeetCode104.二叉树的最大深度、LeetCode111.二叉树的最小深度

目录

LeetCode226.反转二叉树

1. 前序遍历

1.1 递归法

1.2 迭代法

1.3 统一迭代法

2. 层序遍历

 3.(类)中序遍历法

 3.1 递归法

3.2 统一迭代法

 LeetCode101.对称二叉树

1.递归法

2.迭代法

2.1 使用队列解决

2.2 使用栈解决

LeetCode104.二叉树的最大深度

1.递归法

2.迭代法

LeetCode111.二叉树的最小深度

1. 递归法

2.遍历法


LeetCode226.反转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 

思路:将二叉树反转,想要达到此效果,那么只需将每一个结点的左右结点都交换一次即可。
适合的遍历方法有前序遍历后序遍历、以及层序遍历,中序遍历不太适合,因为可能有的结点会被交换两次,最后得出的结果有误。
前序遍历和后序遍历的代码相差不大,只是有的代码顺序可能需要交换,这里以前序遍历来说明。

1. 前序遍历

1.1 递归法

//前序遍历
TreeNode* invertTree(TreeNode* root) {
        if(root == NULL) return NULL;
        swap(root -> left, root -> right);//中
        invertTree(root -> left);//左
        invertTree(root -> right);//右
        return root;
    }

1.2 迭代法

TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> st;
        if(root != NULL) st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top();
            st.pop();
            swap(node -> left, node -> right);//中
            if(node -> right) st.push(node -> right);//右
            if(node -> left) st.push(node -> left);//左  注意顺序
        }
        return root;
    }

1.3 统一迭代法

TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> st;
        if(root != NULL) st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top();
            if(node != NULL){
                st.pop();

                if(node -> right) st.push(node -> right);//右
                if(node -> left) st.push(node -> left);//左
                st.push(node);
                st.push(NULL);//中
            }else{
                st.pop();
                node = st.top();
                st.pop();
                swap(node -> left, node -> right);
            }
        }
        return root;
    }

2. 层序遍历

TreeNode* invertTree(TreeNode* root) {
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            for(int i = 0; i < size; i ++){
                TreeNode* node = que.front();
                que.pop();
                swap(node -> left, node -> right);
                if(node -> left) que.push(node -> left);//左
                if(node -> right) que.push(node -> right);//右
            }
        }
        return root;
    }

当然,如果是需要使用中序的方法,也不是不行,只要将可能交换两次的操作代码替换掉即可。下面将展示使用中序的方法,

3.(类)中序遍历法

 3.1 递归法

使用递归法来进行交换,将中序遍历中本来要递归的右子树的语句替换成左子树,这样可以避免中间结点交换两次,造成错误的结果。
当然,此时也不叫中序遍历了,它只是有中序遍历的递归壳子,对其进行了改进,使得其能够适应本题。

TreeNode* invertTree(TreeNode* root) {
        if(root == NULL) return NULL;
        invertTree(root -> left);
        swap(root -> left, root -> right);
        invertTree(root -> left); //这里依然是交换左子数
        return root;
    }

3.2 统一迭代法

当然,使用统一迭代的方法是可以使用中序遍历的方法的,原因在于递归中使用的是指针,而这里使用的是栈,避免了多次交换的错误。

TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> st;
        if(root != NULL) st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top();
            if(node != NULL){
                st.pop();

                if(node -> right) st.push(node -> right);//右
                st.push(node);
                st.push(NULL);//中
                if(node -> left) st.push(node -> left);//左
            }else{
                st.pop();
                node = st.top();
                st.pop();
                swap(node -> left, node -> right);
            }
        }
        return root;
    }

LeetCode101.对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。 

思路:比较左右两棵子树的对应位置元素是否完全一致

这里有递归法迭代法两种。

1.递归法

这里需要注意递归终止条件的判断,当一方为空一方不为空,或者两方都不为空但是值不相等时,直接返回false;当两方都为空时,返回true;当两方都不为空并且值相等时,继续进行其他位置判断。

    bool compare(TreeNode* left, TreeNode* right){
        if(left == NULL && right != NULL) return false;
        else if(left != NULL && right == NULL) return false;//左右结点一方为空则返回false
        else if(left == NULL && right == NULL) return true;
        else if(left -> val != right -> val) return false;
        //左右结点都不为空,但是值不等,返回false

        //下面是剩下了左右结点不为空且值相等的情况
        bool outside = compare(left -> left, right -> right);
        bool inside = compare(left -> right, right -> left);
        bool result = outside && inside;
        return result;
    }
    bool isSymmetric(TreeNode* root) {
        if(root == NULL) return true;
        return compare(root -> left, root -> right);
    }

这是对上面代码的精化,如果不习惯或者不熟悉,建议不要采用这种方式。

bool compare(TreeNode* left, TreeNode* right){
        if(left == NULL && right != NULL) return false;
        else if(left != NULL && right == NULL) return false;//左右结点一方为空则返回false
        else if(left == NULL && right == NULL) return true;
        else if(left -> val != right -> val) return false;
        //左右结点都不为空,但是值不等,返回false
        else return compare(left -> left, right -> right) && compare(right -> left, left -> right);
        //下面是剩下了左右结点不为空且值相等的情况
    }
    bool isSymmetric(TreeNode* root) {
        if(root == NULL) return true;
        return compare(root -> left, root -> right);
    }

2.迭代法

这里选用了队列或者栈来处理,条件判断逻辑和递归的时候是一样的,这里需要注意的是放入元素时的顺序一定要相对应,这样才能正确比较。

2.1 使用队列解决

bool isSymmetric(TreeNode* root) {
        if(root == NULL) return true;
        queue<TreeNode*> que;
        que.push(root -> left);
        que.push(root -> right);
        while(!que.empty()){
            TreeNode* left = que.front(); que.pop();
            TreeNode* right = que.front(); que.pop();
            if(left == NULL && right == NULL) continue;
            //此时比较到了叶子结点,都为空,说明相等,继续循环

            if(left == NULL || right == NULL || left -> val != right -> val) return false;
            //当两个结点一方为空,一方不为空,或者两者都不为空,但是值不一样时,返回false

            que.push(left -> left);
            que.push(right -> right);
            que.push(left -> right);
            que.push(right -> left);
            //继续放入结点,按照对称位置放置,方便取出进行比较
        }
        return true;

    }

2.2 使用栈解决

bool isSymmetric(TreeNode* root) {
        if(root == NULL) return true;
        stack<TreeNode*> st;
        st.push(root -> left);
        st.push(root -> right);
        while(!st.empty()){
            TreeNode* left = st.top(); st.pop();
            TreeNode* right = st.top(); st.pop();
            if(left == NULL && right == NULL) continue;
            //此时比较到了叶子结点,都为空,说明相等,继续循环

            if(left == NULL || right == NULL || left -> val != right -> val) return false;
            //当两个结点一方为空,一方不为空,或者两者都不为空,但是值不一样时,返回false

            st.push(left -> left);
            st.push(right -> right);
            st.push(left -> right);
            st.push(right -> left);
            //继续放入结点,按照对称位置放置,方便取出进行比较
        }
        return true;

    }

LeetCode104.二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

思路:对于二叉树求深度或者求高度的问题,都有相似之处。深度是从根节点到某一结点之间经过的结点数或者链路数;高度则是从根节点到叶子结点所经过的结点树或者链路数。

这里采用递归法以及迭代法来解决问题。

1.递归法

递归法采用了后序遍历的写法,首先对左子树的深度进行计算,再计算右子树的深度,最后返回的时候取两者的最大值。

    int getDepth(TreeNode* cur){
        if(cur == NULL) return 0;
        int leftmax = getDepth(cur -> left) + 1;//统计左子树的最大深度
        int rightmax = getDepth(cur -> right) + 1;//统计右子树的最大深度
        return max(leftmax, rightmax);//取最大值
    }
    int maxDepth(TreeNode* root) {
        return getDepth(root);
    }

当然还可以采用前序遍历的写法,这也是深度计算的真正逻辑所在。

首先使result与depth进行比较,取其大值,然后分别对左、右子树遍历,这里面涉及到了回溯,使得深度增减。

    int result = 0; //统计最大深度
    void getDepth(TreeNode* cur, int depth){
        result = result > depth ? result : depth;//每一次递归获取最大值
        if(cur -> left == NULL && cur -> right == NULL) return;//到达叶子结点

        if(cur -> left){
            depth ++;
            getDepth(cur -> left, depth);
            depth --; //回溯需要减1
        }

        if(cur -> right){
            depth ++;
            getDepth(cur -> right, depth);
            depth --;//回溯需要减1
        }
        return;

    }
    int maxDepth(TreeNode* root) {
        if(root == NULL) return result;
        int depth = 1;
        getDepth(root, depth);
        return result;
    }

这是对上面代码的一种精化,前期如果不熟悉,建议不要使用,不然不方便理解。

int result = 0; //统计最大深度
    void getDepth(TreeNode* cur, int depth){
        result = result > depth ? result : depth;//每一次递归获取最大值
        if(cur -> left == NULL && cur -> right == NULL) return;//到达叶子结点
        if(cur -> left) getDepth(cur -> left, depth + 1);
        if(cur -> right) getDepth(cur -> right, depth + 1);//简化判断逻辑
    }
    int maxDepth(TreeNode* root) {
        if(root == NULL) return result;
        int depth = 1;
        getDepth(root, depth);
        return result;
    }

2.迭代法

这里的迭代法采用了层序遍历的方法。本身层序遍历就是能够计算出层的大小,根节点到最深叶子结点的深度即为高度,这样在层序遍历过程中depth逐渐增加,等到最后循环结束时,即可得到最后的最大深度。

int maxDepth(TreeNode* root) {
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        int depth = 0;
        while(!que.empty()){
            int size = que.size();
            depth ++; //记录深度
            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);
            }
        }
        return depth;
    }

LeetCode111.二叉树的最小深度

 给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明:叶子节点是指没有子节点的节点。

思路:求最小深度和最大深度虽然看起来很像,但实际上存在较大差别。主要差别就是对找到最小值时的结点判断,当结点左节点为空、右节点不为空或者左节点不为空、右节点为空时,此时该结点不是我们要找的最小深度结点,因为它不是叶子结点。

这里同样采用递归法迭代法来进行讲解。

1. 递归法

递归法采用了后序遍历的顺序来处理,首先是获取左边子树的深度,然后是右边子树的深度,当存在左子树为空,右子树不为空时,返回1+右子树的长度;而当右子树为空,左子树不为空时,则返回1+左子树的长度,当两者都不为空时,则选择其中较小者+1返回。

int getDepth(TreeNode* cur){
        if(cur == NULL) return 0;
        int leftdepth = getDepth(cur -> left); //左
        int rightdepth = getDepth(cur -> right); //右
        //中
        if(cur -> left == NULL && cur -> right != NULL){
            //当某个结点的左节点为空时,说明最小深度出现在右子树
            return 1 + rightdepth;
        }
        if(cur -> left != NULL && cur -> right == NULL){
            //当某个结点的右节点为空时,说明最小深度出现在左子树
            return 1 + leftdepth;
        }

        int result = 1 + min(leftdepth, rightdepth); 
        //当某个结点左右子树不为空时,取最短深度,返回result
        return result;

    }
    int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        return getDepth(root);  
    }

这是对上面代码的一个精化。

int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        if(root -> left == NULL && root -> right != NULL){
            return 1 + minDepth(root -> right);
        } 
        if(root -> left != NULL && root -> right == NULL){
            return 1 + minDepth(root -> left);
        }
        return 1 + min(minDepth(root -> right), minDepth(root -> left));
    }

当然,除了使用后序的方法,还可以使用前序的顺序处理。首先每次进入递归对result的值判断,与depth比较,取最小值;然后左边子树不为空,则尝试计算左边子树的深度;右边子树不为空,尝试计算右边子树的深度。最后返回的是经过左右两棵子树经过比较过后的最小深度值,返回即可。

    int result = INT_MAX;
    void getDepth(TreeNode* cur, int depth){
        if(cur == NULL) return;
        if(cur -> left == NULL && cur -> right == NULL){
            result = min(result, depth); //中
        }
        if(cur -> left) getDepth(cur -> left, depth + 1);//左
        if(cur -> right) getDepth(cur -> right, depth + 1);//右
    }
    int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        int depth = 1;
        getDepth(root, depth);
        return result;
    }

2.遍历法

遍历法同样采用了层序遍历的思路来进行,在进入每层时depth增加,当某个结点左子树为空且右子树为空时,立即返回depth,这就是要找的最小深度值。

int minDepth(TreeNode* root) {
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        int depth = 0;
        while(!que.empty()){
            int size = que.size();
            depth ++;
            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);
                if(node -> left == NULL && node -> right == NULL){
                    //当出现一个结点的左右结点皆为空时,到达了最小深度处
                    return depth;
                }

            }
        }
        return depth;
    }

感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。

如果有什么问题欢迎评论区讨论!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值