代码随想录算法训练营第15天| Leetcode 102.二叉树的层序遍历、226.翻转二叉树、101.对称二叉树

目录

Leetcode 102.二叉树的层序遍历

Leetcode 226.翻转二叉树

Leetcode 101.对称二叉树


Leetcode 102.二叉树的层序遍历

题目链接:Leetcode 102.二叉树的层序遍历

题目描述:给你二叉树的根节点root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

思路:因为要一层一层遍历,因此需要利用队列辅助。因为队列先进先出符合一层一层遍历的逻辑。本题仍然有递归法和迭代法,不过虽然有两种方法,底层逻辑是相同的,只不过实现的方式不同。从根节点出发,根据每层节点个数依次将节点按顺序放入队列中,然后进入下一层。

代码如下:(递归法)

class Solution {
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int depth) {
        if (cur == nullptr)
            return;
        //由于递归调用时层数在变化,因此需要加上判断条件防止重复
        if (result.size() == depth)
            result.push_back(vector<int>());
        result[depth].push_back(cur->val);
        order(cur->left, result, depth + 1);
        order(cur->right, result, depth + 1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        int depth = 0;
        order(root, result, depth);
        return result;
    }
};

(迭代法)

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != nullptr)//如果根节点不为空,加入根节点
            que.push(root);
        vector<vector<int>> result;
        while (!que.empty()) {//只要队列不为空
            int size = que.size();//保存每一层的大小
            vector<int> vec;
            for (int i = 0; i < size; i++) {//遍历每一层所有节点
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if (node->left)
                    que.push(node->left);
                if (node->right)
                    que.push(node->right);
            }
            result.push_back(vec);
        }
        return result;
    }
};

Leetcode 226.翻转二叉树

题目链接:Leetcode 226.翻转二叉树

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

思路:由于要翻转二叉树,因此将每个节点的左右孩子翻转就可以了。至于遍历方式,dfs和bfs都可以,遍历只是个工具,具体的行为需要根据题意灵活转换。这道题需要注意的是中序遍历并不适合本题,中序遍历会将某些节点的左右孩子翻转两次。为什么呢?首先要知道中序遍历的搜索方式是左中右,当所有的左孩子反转之后,我们将中间节点进行翻转,此时的左孩子变成了右孩子,右孩子变成了左孩子,而按照中序遍历,接下来该访问右孩子,但是此时的右孩子就是之前翻转过的左孩子,这导致了最初的左孩子翻转了两次,右孩子没被翻转

文字看起来很绕对吧?我简单画图举个例子:(图片中箭头代表中序遍历的顺序)

根据这个例子我们可以发现,有两个节点没交换过,有两个节点被交换了两次。不过画图之后我们发现:只要以左中左的遍历方式,仍然可以实现我们想要的“中序遍历”的结果。只不过这样严格意义上不算中序遍历了。

代码如下:(递归dfs,先实现交换再递归,对应前/后序遍历)

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr)
            return root;
        swap(root->left, root->right); //中
        //这两行顺序可以改变
        invertTree(root->left);  //左
        invertTree(root->right); //右
        return root;
    }
};

(递归bfs)

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr)
            return nullptr;
        TreeNode* left = invertTree(root->left);
        TreeNode* right = invertTree(root->right);
        //从最深层开始交换,逐渐向上返回
        root->left = right;
        root->right = left;
        return root;
    }
};

(迭代dfs)

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr)
            return root;
        stack<TreeNode*> st;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            swap(node->left, node->right);
            //这两个顺序可以调换
            if (node->left)
                st.push(node->left);
            if (node->right)
                st.push(node->right);
        }
        return root;
    }
};

(迭代bfs)

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != nullptr)
            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;
    }
};

通过上面代码我们发现,其实本质上解题的基础代码还是二叉树的遍历搜索,唯一有区别的是本题搜索之后需要交换左右孩子节点。

Leetcode 101.对称二叉树

题目链接:​​​​​​Leetcode 101.对称二叉树

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

思路:首先要思考什么叫做二叉树的轴对称?这可不是简单的比较一个节点的左右孩子就可以了,而是要比较一个结点的左子树和右子树的内测和外侧是否相等。

注:上述图片来源于《代码随想录》

上道题我们提到过:遍历只是个工具,具体的行为需要根据题意灵活转换。因此接下来我们需要思考一下这道题该用什么遍历方式。因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。由于中间节点最后遍历,这和后序遍历类似,因此本题用后序遍历来实现。

代码如下:(递归法)

class Solution {
public:
    bool compare(TreeNode* l, TreeNode* r) {
        //首先排除空节点的情况
        if (l == nullptr && r != nullptr)
            return false;
        else if (l != nullptr && r == nullptr)
            return false;
        else if (l == nullptr && r == nullptr)
            return true;
        //再排除数值不相同的情况
        else if (l->val != r->val)
            return false;
        //数值相同,则递归下一层
        bool outside = compare(l->left, r->right);
        bool inside = compare(l->right, r->left);
        bool isSame = outside && inside;
        return isSame;
    }
    bool isSymmetric(TreeNode* root) {
        if (root == nullptr)
            return true;
        return compare(root->left, root->right);
    }
};

(迭代法)

仍然是借助队列来辅助。

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* lnode = que.front();
            que.pop();
            TreeNode* rnode = que.front();
            que.pop();
            if (!lnode && !rnode)
                continue; //左右节点均为空,说明对称
            //有一个节点为空或者两节点数值不同,说明不对称
            if (!lnode || !rnode || (lnode->val != rnode->val))
                return false;
            que.push(lnode->left);  // 加入左节点左孩子
            que.push(rnode->right); // 加入右节点右孩子
            que.push(lnode->right); // 加入左节点右孩子
            que.push(rnode->left);  // 加入右节点左孩子
        }
        return true;
    }
};

总结:这几天忙着练车和出去玩,进度有点落下了。不过题目难度还可以,二叉树的很多题目都是以遍历为基础,额外的操作简单思考过后也可以轻松解决。还是那句话:遍历只是个工具,具体的行为需要根据题意灵活转换。

最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!

  • 20
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值