13.树(2) | 二叉树的层序遍历、翻转二叉树II、对称二叉树

        今天的题目有下面10道:

         题目比较多,但第2~8道均是在第1道层序遍历的模板基础上根据题目稍作修改即可。每一道题都有递归和迭代2种解法,2种方法都灵活使用还是有难度的。

        第1道题(102.二叉树的层序遍历)是二叉树的第4种遍历方式——层序遍历。同样有递归和迭代2种实现方式。迭代方式和广度优先是一样的,用队列实现,只需要将队头节点的val不断放入结果res中,并添加其左右子节点进队列,直到队列空为止,相对较容易。其中需要注意要在内部循环开始前就记录队列的size(),否则在内部循环途中其size()值会动态变化。

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if (root == nullptr) {
            return res;
        }
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            vector<int> layer;
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                TreeNode *cur = que.front();
                que.pop();
                layer.push_back(cur->val);
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
            }
            res.push_back(layer);
        }
        return res;
    }
};

        递归方式的函数参数除了当前节点和用于保存结果的vector之外,还需要当前节点的深度值depth,用来当作vector的下标。递归的终止条件是当前节点为空,之后要先根据depth和vector当前大小来扩展vector(添加vector<int>()),再向vector的第depth个元素中添加当前元素的val,最后再分别递归当前节点的左、右子节点。

class Solution {
public:
    void layerOrder(TreeNode *cur, int depth, vector<vector<int>>& res) {
        if (cur == nullptr) {
            return;
        }
        if (depth == res.size()) {
            res.push_back(vector<int>());
        }
        res[depth].push_back(cur->val);
        layerOrder(cur->left, depth + 1, res);
        layerOrder(cur->right, depth + 1, res);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        layerOrder(root, 0, res);
        return res;
    }
};

        第2道题(107.二叉树的层次遍历 II)只需要在第1道题(102.二叉树的层序遍历)基础上将结果vector进行reverse()即可。

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> res;
        if (root == nullptr) {
            return res;
        }
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            vector<int> layer;
            for (int i = 0; i < size; i++) {
                TreeNode *cur = que.front();
                que.pop();
                layer.push_back(cur->val);
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
            }
            res.push_back(layer);
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

        第3道题(199.二叉树的右视图)只需要在第1道题(102.二叉树的层序遍历)的基础上将用于保存结果的vector改为仅在每层最后一个元素时才添加val即可。

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> res;
        if (root == nullptr) {
            return res;
        }
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                TreeNode *cur = que.front();
                que.pop();
                if (i == size - 1) {
                    res.push_back(cur->val);
                }
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
            }
        }
        return res;
    }
};

        第4道题(637.二叉树的层平均值)只需在第1道题(102.二叉树的层序遍历)基础上,在每一层中记录当前层的val之和,并在当前层结束后计算平均值即可。

class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        vector<double> res;
        if (root == nullptr) {
            return res;
        }
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            double sum = 0;
            for (int i = 0; i < size; ++i) {
                TreeNode *cur = que.front();
                que.pop();
                sum += (double)cur->val;
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
            }
            sum /= size;
            res.push_back(sum);
        }
        return res;
    }
};

                第5道题(429.N叉树的层序遍历)只需在第1道题(102.二叉树的层序遍历)基础上,在循环中再添加内层循环,用来添加当前节点的所有子节点进队列。

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> res;
        if (root == nullptr) {
            return res;
        }
        queue<Node*> que;
        que.push(root);
        while (!que.empty()) {
            vector<int> layer;
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                Node *cur = que.front();
                que.pop();
                layer.push_back(cur->val);
                vector<Node*> children = cur->children;
                for (int j = 0; j < children.size(); ++j) {
                    que.push(children[j]);
                }
            }
            res.push_back(layer);
        }
        return res;
    }
};

        第6道题(515.在每个树行中找最大值)只需在第1道题(102.二叉树的层序遍历)基础上,在每层中记录当前层的val最大值即可。

class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int> res;
        if (root == nullptr) {
            return res;
        }
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size(), maxx = INT_MIN;
            for (int i = 0; i < size; ++i) {
                TreeNode *cur = que.front();
                que.pop();
                maxx = max(maxx, cur->val);
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
            }
            res.push_back(maxx);
        }
        return res;
    }
};

        第7题(116.填充每个节点的下一个右侧节点指针)需在第1道题(102.二叉树的层序遍历)基础上,在内部循环中将每一个节点的next指向下一个节点,也就是当前节点被pop()后的队首元素即可。要注意对每层最后一个元素区别对待,使其next指向NULL。

class Solution {
public:
    Node* connect(Node* root) {
        if (root == nullptr) {
            return root;
        }
        queue<Node*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                Node *cur = que.front();
                que.pop();
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
                if (i != size - 1) {
                    cur->next = que.front();
                }
                else {
                    cur->next = NULL;
                }
            }
        }
        return root;
    }
};

         二刷各种树的定义

  • 完美二叉树:深度为k,节点数为2^(k + 1) - 1的树;
  • 完全二叉树:除最下面一层外其他层是满的,且最后一层靠左分布;
  • 满二叉树:每个节点都是0个或2个子节点,没有只有1个子节点的节点。

        第8题(117.填充每个节点的下一个右侧节点指针II)与  第7题(116.填充每个节点的下一个右侧节点指针)的区别在于,前者是二叉树,后者更具体,是“完美二叉树”,但在解决方法上没有区别,代码实现与上一题一致。

class Solution {
public:
    Node* connect(Node* root) {
        if (root == nullptr) {
            return root;
        }
        queue<Node*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                Node *cur = que.front();
                que.pop();
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
                if (i != size - 1) {
                    cur->next = que.front();
                }
                else {
                    cur->next = NULL;
                }
            }
        }
        return root;
    }
};

        第9题(226.翻转二叉树)也分为递归解法和迭代解法。递归解法较简单,分前序、中序、后序遍历3种。前序首先交换当前节点的左、右2个子节点,再分别对左、右子节点进行反转操作;后序遍历首先分别对左、右子节点进行反转操作,再将已经反转好的左、右2个子节点进行交换;中序遍历首先对左子节点进行反转,再将左右子节点交换,注意此时新的左子节点是旧的未经过反转的右子节点,新的右子节点才是旧的已经反转好的左子节点,所以最后一步应该对新的左子节点进行反转操作。3种方式分别如下:

        前序递归完整代码:

class Solution {
public:
    void invert(TreeNode* cur) {
        if (cur == nullptr) {
            return;
        }
        swap(cur->left, cur->right);
        invert(cur->left);
        invert(cur->right);
    }
    TreeNode* invertTree(TreeNode* root) {
        invert(root);
        return root;
    }
};

        后序递归的invert():

    void invert(TreeNode* cur) {
        if (cur == nullptr) {
            return;
        }
        invert(cur->left);
        invert(cur->right);
        swap(cur->left, cur->right);
    }

        中序递归的的invert():

    void invert(TreeNode* cur) {
        if (cur == nullptr) {
            return;
        }
        invert(cur->left);
        swap(cur->left, cur->right);
        invert(cur->left);
    }

        这道题的迭代方式在有了层序遍历的基础之后也比较容易,只需在层序遍历的过程中对每个当前节点进行左、右子节点的swap()操作即可。

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) {
            return root;
        }
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                TreeNode *cur = que.front();
                que.pop();
                swap(cur->left, cur->right);
                if (cur->left) {
                    que.push(cur->left);
                }
                if (cur->right) {
                    que.push(cur->right);
                }
            }
        }
        return root;
    }
};

        另外,该实现方法中的队列也可换用栈(递归能实现的,栈也能实现),其他地方不变。而该实现方法既是层序遍历,也是一种广度优先的搜索,也可以用深度优先,既迭代方式的前序遍历(非统一方式的即为将上面的队列换成栈,下面代码为统一方式的):

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) {
            return root;
        }
        stack<TreeNode*> st;
        st.push(root);
        while (!st.empty()) {
            TreeNode *cur = st.top();
            st.pop();
            if (cur != nullptr) {
                if (cur->right) {
                    st.push(cur->right);
                }
                if (cur->left) {
                    st.push(cur->left);
                }
                st.push(cur);
                st.push(nullptr);
            }
            else {
                cur = st.top();
                st.pop();
                swap(cur->left, cur->right);
            }
        }
        return root;
    }
};

        第10题(101. 对称二叉树)同样有递归和迭代2种解法。递归法要判断左右子树是否对称,即两个子树的外侧元素、内侧元素是否相等,所以遍历方式只能是后序遍历,但不是绝对的“左->右->中”后序,而是对左子树和右子树分别进行“左->右->中”和“右->左->中”的“后序遍历”。递归函数参数为左、有两子树,需要判断两子树是否空的各种相关情况,两个都空返回true,只有一个空则返回false,其余情况要根据它们内内侧和外侧的对称结果进行判断。

class Solution {
public:
    bool areSym(TreeNode *l, TreeNode *r) {
        if (l == nullptr && r == nullptr) {
            return true;
        }
        if (l && r && l->val == r->val) {
            bool sameOutside = areSym(l->left, r->right);
            bool sameInside = areSym(l->right, r->left);
            return sameOutside && sameInside;
        }
        else {
            return false;
        }
    }
    bool isSymmetric(TreeNode* root) {
        if (root == nullptr) {
            return true;
        }
        return areSym(root->left, root->right);
    }
};

        迭代法让树的左子树,右子树分别按“中->左->右”的“先序”和“中->右->左”的“先序”将其元素放入队列中,每取出一对,就进行比较,并将其各自的左右/右左节点放入队列中,直到某次比较不相等就返回false,或队列为空就返回true。

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 *cur1 = que.front();
            que.pop();
            TreeNode *cur2 = que.front();
            que.pop();
            if (cur1 == nullptr && cur2 == nullptr) {
                continue;
            }
            if (cur1 == nullptr || cur2 == nullptr || cur1->val != cur2->val) {
                return false;
            }
            que.push(cur1->left);
            que.push(cur2->right);
            que.push(cur1->right);
            que.push(cur2->left);
        }
        return true;
    }
};

代码中的队列仅为一个容器,仅要求可以成对放入和按照放入的“对”取出即可,所以替换成栈(其余地方不改)也可以实现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值