LeetCode题解 二叉树(五):226 翻转二叉树;101 对称二叉树;100 相同的树;572 另一个树的子树

226 翻转二叉树 easy

这道题有一段广为人知的传说:曾有人说Homebrew(适用于macOS和Linux的开源软件包管理器)的作者Max Howell,没有在白板上写出这道题目,被Google拒绝了。

至于是不是真的因为没做出来这道题就被拒绝,我想只有面试官自己本人清楚了。

回到正题,翻转二叉树的意思,就是将一棵数的左右子树完全交换

什么叫做完全交换呢?参考下图,不仅2与7发生了交换,且7的左右子树6和9也发生了交换。

226.翻转二叉树

此前我们讲完了二叉树的深度遍历和层序遍历。也是从这道题开始,之后的题目,大多情况下我们都需要想一想应该用那种遍历方式。

那么这道题应该用哪种呢?

我首先想到的是层序遍历,将每一层每两个节点做一次交换就可以。

或者也可以用前序遍历或者后序遍历,因为在这两种遍历中,左右节点是依次处理的。中序遍历却不可以。

所以这道题,可以用深度优先的递归法和迭代法 +广度优先遍历的迭代法,三种方法来解决,颇有种三英战吕布(easy level)的意味。

深度优先,递归法——

void reverse(TreeNode* cur) {
    if (!cur) return;
    swap(cur->left, cur->right);
    reverse(cur->left);
    reverse(cur->right);
}
TreeNode* invertTree(TreeNode* root) {
    if (!root) return root;
    reverse(root);
    return root;
}

还有一种更加简洁的写法,直接调用本函数完成递归,代码如下:

TreeNode* invertTree(TreeNode* root) {
    if (!root) return root;
    swap(root->left, root->right);
    invertTree(root->left);
    invertTree(root->right);
    return root;
}

深度优先,迭代法——

刚刚提到使用前序或者后序遍历都可以,此处我们就使用前序遍历来完成,代码如下:

TreeNode* invertTree(TreeNode* root) {
    stack<TreeNode*> stk;
    if (root) stk.push(root);
    while (!stk.empty()) {
        TreeNode *cur = stk.top();
        stk.pop();
        swap(cur->left, cur->right);
        if (cur->left) stk.push(cur->left);
        if (cur->right) stk.push(cur->right);
    }
    return root;
}

此前我们也跟着随想录,走了一遍如何使用统一写法(基于标记法),完成三种遍历,所以此处也附上使用统一写法的前序遍历,代码如下——

TreeNode* invertTree(TreeNode* root) {
    stack<TreeNode*> stk;
    if (root) stk.push(root);
    while (!stk.empty()) {
        TreeNode *cur = stk.top();
        // 统一写法中最关键的地方
        if (cur) {
            stk.pop();
            if (cur->right) stk.push(cur->right);
            if (cur->left) stk.push(cur->left);
            stk.push(cur);
            stk.push(nullptr);
        } else {
            stk.pop();
            cur = stk.top();
            stk.pop();
            swap(cur->left, cur->right);
        }
    }
    return root;
}

最后一种方法,就是广度优先遍历,即层序遍历,代码如下:

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;
}
101 对称二叉树 easy

这道题教会了我们对称二叉树的定义,见下图即可:

101. 对称二叉树

首先判断应该用那种遍历方式。对称,从感觉上讲层序遍历可以解决问题,但对每一层的值进行判断,会相当耗费时间。

那么这道题可以从深度遍历的角度考虑,前中后序应该用哪一种又成了问题。

其实这道题只能用“后序”遍历,因为对于这道题而言,是否为对称,要先看左右子树是否符合条件,根节点的优先级在最后。为什么要给后序加一个引号呢?是因为这道题已经并非是正统的后序遍历了。我们要对左右子树进行判断,那么比较的对象就是:

左结点的左孩子与右结点的右孩子,左节点的右孩子和右结点的左孩子,顺序是相反的。

这道题可以使用递归,迭代来完成。

递归法要通过返回比较结果,所以返回值是bool类型;那么结束条件就分四种:

{左右都为空,返回true},{左右其中一个不存在,返回false},{左右值都存在,但是却不相同,返回false},那么最终剩下的结果,就是左右都存在且值相同。

递归法的代码(简洁版)如下:

bool compare(TreeNode* left, TreeNode* right) {
    if (left == NULL && right != NULL) return false;
    else if (left != NULL && right == NULL) return false;
    else if (left == NULL && right == NULL) return true;
    else if (left->val != right->val) return false;
    else return compare(left->left, right->right) && compare(left->right, right->left);
}

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

那么使用迭代法呢?刚刚我们说过了这道题并非正统的后序遍历,所以遍历顺序自然要符合题意。迭代法我们首先会想到使用栈,但这道题因为每两个节点就要比较一次,所以使用队列也是可以的,在代码中用哪种都可以,此处我们使用队列,代码如下:

bool isSymmetric(TreeNode* root) {
    if (!root) 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;
        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;
}

接下来提到的这两道题,属于对称二叉树的变种,稍微修改一下就可以AC,所以也就不再深入的理解了。

100 相同的树 easy

检测两棵数是否相同,方法从比较一棵树,变成了真的比较“左右两棵子树”

递归法代码如下:

bool compare(TreeNode *cur1, TreeNode *cur2) {
    if (cur1 == nullptr && cur2 == nullptr) return true;
    else if (cur1 != nullptr && cur2 == nullptr) return false;
    else if (cur1 == nullptr && cur2 != nullptr) return false;
    else if (cur1->val != cur2->val) return false;
    else return compare(cur1->left, cur2->left) && compare(cur1->right, cur2->right);
}

bool isSameTree(TreeNode* p, TreeNode* q) {
    return compare(p, q);
}
572 另一个树的子树 easy

判断一棵树中是否包含另一棵树,见下图:

img

同样使用迭代法,代码如下:

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;

    return compare(left->left, right->left) && compare(left->right, right->right);
}

bool isSubtree(TreeNode* root, TreeNode* subRoot) {
    if (root == nullptr) return false;

    return compare(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值