代码随想录算法训练营day14

代码随想录算法训练营

—day14


前言

今天是算法营的第14天,希望自己能够坚持下来!
今日任务:
● 226.翻转二叉树
● 101. 对称二叉树
● 104.二叉树的最大深度
● 111.二叉树的最小深度


一、226.翻转二叉树

题目链接
文章讲解
视频讲解

递归法

思路:

  • 跟二叉树的遍历是一样的思路,只是处理变成了将左右节点交换。
  • 这里需要注意的是,如果是用中序遍历的话,原本按照中序遍历是要递归右节点的,这道题需要改成递归左节点。
  • 那是因为递归了中级节点后,节点的左右节点已经被交换了,右节点变成了左节点。

代码如下:

/**
 * 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:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) return root;

        //这里用前序遍历或者后序遍历都可以,顺序交换一下
        swap(root->left, root->right);
        invertTree(root->left);
        invertTree(root->right); //如果是中序遍历的话这些需要传入root->left

        return root;
    }
};

迭代法

思路:

  • 跟二叉树的遍历是一样的思路,只是处理变成了将左右节点交换。

代码如下:

/**
 * 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:
    //迭代法,前序
    TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> st;
        if (root == nullptr) return root;

        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;
    }
};

统一迭代法

思路:

  • 跟二叉树的遍历是一样的思路,只是处理变成了将左右节点交换。

代码如下:

/**
 * 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:
    TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> st;
        if (root == nullptr) return root;

        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            
            //因为是栈,而且存储的是遍历顺序,左中右顺序都需要倒置 前序:中左右
            if (node != nullptr) {
                st.pop();
                if (node->right) st.push(node->right); //右
                if (node->left) st.push(node->left); //左
                st.push(node); //中
                st.push(nullptr);
            } else {
                st.pop();
                node = st.top();
                st.pop();
                swap(node->left, node->right); //节点处理逻辑
            }
        }

        return root;
    }
};

二、101. 对称二叉树

题目链接
文章讲解
视频讲解

递归法

思路:
同时遍历两个节点,分别判断它们对应的子节点是否相等(对称是外侧跟外侧节点比,内侧跟内侧节点比)

  1. 函数参数,左节点和右节点,返回是否两节点是否对称;
  2. 终止条件:左空,右不为空;右空,左不为空;左右都为空;左右不空,但左右值不相同则返回false;
  3. 处理:判断两个处理节点的外侧和内侧节点是否对称。

代码如下:

/**
 * 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:
    //递归法
    bool compare(TreeNode* left, TreeNode* right) {
        //终止条件: 
        //左空,右不为空;右空,左不为空;左右都为空;左右不空,但左右值不相同;
        if (left == nullptr && right != nullptr) return false;
        else if (left != nullptr && right == nullptr) return false;
        else if (left == nullptr && right == nullptr) return true;
        else if (left->val != right->val) return false;
        
        bool outsize = compare(left->left, right->right); //左节点的左节点跟右节点的右节点比较
        bool insize = compare(left->right, right->left); //左节点的右节点跟右节点的左节点比较
        bool isSame = outsize && insize; // 左右节点堆成的话,说明这个中节点以下的部分是对称的

        return isSame;

    }
    bool isSymmetric(TreeNode* root) {
        if (root == nullptr) return true;
        return compare(root->left, root->right);
    }
};

迭代法

思路就是将需要对比的节点放入队列中,再取出来对比。这里换成栈也是一样的

代码如下:

/**
 * 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:
    //迭代法,思路就是将需要对比的节点放入队列中,再取出来对比。这里换成栈也是一样的
    bool isSymmetric(TreeNode* root) {
        queue<TreeNode*> que;
        if (root == nullptr) return true;

        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 && !right) continue;//左右都是空节点,需要继续遍历其他节点
            //左右节点各有一个是空节点,或者左右节点数值不相等直接返回false
            if (!left || !right || left->val != right->val) return false;

            //将下次要遍历的两组节点依次放入队列中
            que.push(left->left);
            que.push(right->right);
            que.push(left->right);
            que.push(right->left);
            


        }
        return true;
    }
};

100.相同的树

题目链接

思路是一样的,只是递归的时候是左子树跟左子树比较,右子树跟右子树比较。
代码如下:

/**
 * 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:
    //递归法,同时遍历两个树,递归分别比较两树的左子树,右子树是否相等
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (!p && !q) return true;
        if (!p || !q || p->val != q->val) return false;

        return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
};

572. 另一棵树的子树

题目链接

思路是递归判断root的当前节点剩余部分是否与subroot相等。
需要注意的是不能递归使用isSubtree,因为题目要求子树是包括 tree 的某个节点和这个节点的所有后代节点。用isSubtree会忽略节点的后代节点。
所以这里需要另外写一个isSameTree函数。
代码如下:

/**
 * 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:
    bool isSameTree (TreeNode* p, TreeNode* q) {
        if (!p && !q) return true;
        else if (!p || !q || p->val != q->val) return false;

        return isSameTree(p->left, q->left)&&isSameTree(p->right, q->right);
    }

    //思路是递归判断root的当前节点剩余部分是否与subroot相等
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if (!root && !subRoot) return true;
        if (!root || !subRoot) return false;

        if (isSameTree(root, subRoot)) return true;

        return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot); //左子树或者右子树有跟subroot相等就可以了
    }
};

三、104.二叉树的最大深度

题目链接
文章讲解
视频讲解

递归法(后序)

深度:节点到根节点的距离 (前序,将上层的结果返回给下层)
高度:节点到子节点的距离 (后序,将下层的结果返回给上层)

因为根节点的高度就是该二叉树的最大深度,所以可以用后序求根节点的高度。

/**
 * 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) return 0; //空节点的高度为0

        int leftHight = maxDepth(root->left); //左节点的高度
        int rightHight = maxDepth(root->right); //右节点的高度

        return 1+max(leftHight, rightHight); //中节点的高度
    }
};

递归法(前序)

/**
 * 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 result; //记录层数

    void getDepth(TreeNode* node, int depth) {
        result = depth > result? depth:result; //中  每一层都更新最大层数

        if (node->left == nullptr && node->right == nullptr) return;

        //分别从左节点和右节点去记录最大深度,取其中的最大值
        if (node->left) { //左
            depth++; //进入下一层,深度+1
            getDepth(node->left, depth);
            depth--; //从下一层返回来,深度-1
        }

        if (node->right) { //右
            depth++; //进入下一层,深度+1
            getDepth(node->right, depth);
            depth--; //从下一层返回来,深度-1
        }

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

        return result;
    }
};

层序遍历法

昨天学的层序遍历法其实已经做过了,也写在这里做个对比。
思路是利用队列一层层遍历,记录最大层数。

/**
 * 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) {
        queue<TreeNode*> que;
        int maxDepth = 0;
        if (root == nullptr) return maxDepth;

        que.push(root);
        while (!que.empty()) {
            int size = que.size();

            while (size--) {
                TreeNode* node = que.front();
                que.pop();

                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            //记录二叉树的层数
            maxDepth++;
        }

        return maxDepth;
    }
};

559. N 叉树的最大深度

题目链接
这道题的思路跟二叉树最大深度是一样的,只是获取深度的时候是获取所有子节点的深度取最大值。

代码如下:

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    //递归法
    int maxDepth(Node* root) {
        int depth = 0;
        if (root == nullptr) return depth;

        //循环获取子节点的深度,并取最大值
        for (Node* child: root->children) {
            depth = max(depth, maxDepth(child));
        }

        return depth+1;
    }
};

四、111.二叉树的最小深度

题目链接
文章讲解
视频讲解

递归法(后序)

其实整体思路跟最大深度是一样的,但是需要注意的点是:

  • 题目写了最小深度是是从根节点到最近叶子节点的最短路径上的节点数量。
  • 而叶子节点是指没有子节点的节点。即左右都为空。
  • 所以当左右节点有一个为空的时候,需要取另一个不为空的节点的深度,而不是单单取左右节点深度的最小值。

代码如下:

/**
 * 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;

        int leftDepth = minDepth(root->left);
        int rightDepth = minDepth(root->right);

        //当左右节点只有一个是空的时候,需要取不为空的节点的深度
        if (!leftDepth && rightDepth) return 1+rightDepth;
        if (leftDepth && !rightDepth) return 1+leftDepth;

        return 1+min(leftDepth, rightDepth);
    }
};

递归法(前序)

同样需要判断是否为叶子节点。
代码如下:

/**
 * 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 result;

    void getDepth(TreeNode* node, int depth) {
        if (node == nullptr) return;

        //判断是否为叶子结点
        if (node->left == nullptr && node->right == nullptr) {
            result = min(result, depth);
        }

        if (node->left) getDepth(node->left, depth + 1);
        if (node->right) getDepth(node->right, depth + 1);
        
        return;
    }

    int minDepth(TreeNode* root) {
        result = INT_MAX;//求最小值,先定义个最大值
        if (root == nullptr) return 0;

        getDepth(root, 1);

        return result;
    }
};

层序遍历法

也是昨天做过的,同样需要判断是否为叶子节点。
代码如下:

/**
 * 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) {
        queue<TreeNode*> que;
        int depth = 0;
        if (root == nullptr) return depth;

        que.push(root);
        while (!que.empty()) {
            int size = que.size();

            //记录二叉树的层数
            depth++;
            while (size--) {
                TreeNode* node = que.front();
                que.pop();

                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);

                //当左右节点都为空时证明已经是最短一层了
                if (!node->left && !node->right) return depth;
            }
        }

        return depth;
    }
};

总结

今天做的题有些昨天已经做过了,又分别用递归法和迭代法用了一遍,加深了这两种方法的印象。二叉树的题感觉看完了的时候觉得会了,第二天感觉又忘了,还是需要理解其中的核心思路,并且强化才行!

明天继续加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值