数据结构以学带练day15——二叉树的层序遍历、反转二叉树、对称二叉树

127 阅读9分钟

二叉树的层序遍历

层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑

102. 二叉树的层序遍历

image.png

思路

利用一个队列存入每层的节点,同时定义size计算每层节点的数量,size的大小即循环的次数。循环内部获取队列front的值,并同时获取该节点的左右子树并存入队列中。

102二叉树的层序遍历.gif

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        //定义一个二维数组,用于存放结果
        vector<vector<int>> res;
        //定义一个类型为TreeNode的队列
        queue<TreeNode*> q;
        //如果树不为空,则将根节点放入队列中
        if(root!=nullptr) q.push(root);
        //当队列不为空时
        while(q.empty()==false){
            //定义一个size,记录每层节点数,也是for循环的次数
            int size = q.size();
            //定义一个一维数组,存放每层节点的数值
            vector<int> vec;
            //循环遍历每层的节点
            for(int i=0;i<size;i++){
                //获取队列首部节点
                TreeNode* node = q.front();
                //将队列首部节点的值存入vec
                vec.push_back(node->val);
                //弹出队列首部节点
                q.pop();
                //将下一层的节点存入队列中
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            //将本层存入res
            res.push_back(vec);
        }
        return res;
    }
};
# 递归法
class Solution {
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int depth)
    {
        //1、递归结束条件:如果指针指向空,则整个递归结束
        if (cur == nullptr) return;
        
        //2、递归函数逻辑:如果数组大小和深度相同,则存入一个空的一维数组
        if (result.size() == depth) result.push_back(vector<int>());
        //往空的一维数组存值
        result[depth].push_back(cur->val);
        
        //3、下一层递归
        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;
    }
};

107.二叉树的层次遍历 II

image.png

思路

相对于102.二叉树的层序遍历,就是最后把result数组反转一下就可以了。

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        //定义一个二维数组,用于存放结果
        vector<vector<int>> res;
        //定义一个类型为TreeNode的队列
        queue<TreeNode*> q;
        //如果树不为空,则将根节点放入队列中
        if(root!=nullptr) q.push(root);
        //当队列不为空时
        while(q.empty()==false){
            //定义一个size,记录每层节点数,也是for循环的次数
            int size = q.size();
            //定义一个一维数组,存放每层节点的数值
            vector<int> vec;
            //循环遍历每层的节点
            for(int i=0;i<size;i++){
                //获取队列首部节点
                TreeNode* node = q.front();
                //将队列首部节点的值存入vec
                vec.push_back(node->val);
                //弹出队列首部节点
                q.pop();
                //将下一层的节点存入队列中
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            //将本层存入res
            res.push_back(vec);
        }
        //反转数组
        reverse(res.begin(),res.end());
        return res;
    }
};

199.二叉树的右视图

image.png

思路

层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> res;
        //定义一个类型为TreeNode的队列
        queue<TreeNode*> q;
        //如果树不为空,则将根节点放入队列中
        if(root!=nullptr) q.push(root);
        //当队列不为空时
        while(q.empty()==false){
            //定义一个size,记录每层节点数,也是for循环的次数
            int size = q.size();
            //循环遍历每层的节点
            for(int i=0;i<size;i++){
                //获取队列首部节点
                TreeNode* node = q.front();
                //弹出队列首部节点
                q.pop();
                // 将每一层的最后元素放入res数组中
                if (i == (size - 1)) res.push_back(node->val); 
                //将下一层的节点存入队列中
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
        }
        return res;
    }
};

637.二叉树的层平均值

image.png

思路

定义sum存储每层的节点值的和,之后用sum除以该层节点数大小size即可。

class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        vector<double> res;
        //定义一个类型为TreeNode的队列
        queue<TreeNode*> q;
        //如果树不为空,则将根节点放入队列中
        if(root!=nullptr) q.push(root);
        //当队列不为空时
        while(q.empty()==false){
            //定义一个size,记录每层节点数,也是for循环的次数
            int size = q.size();
            double sum = 0;//记录每层节点值的和
            //循环遍历每层的节点
            for(int i=0;i<size;i++){
                //获取队列首部节点
                TreeNode* node = q.front();
                //弹出队列首部节点
                q.pop();
                sum+=(node->val);
                //将下一层的节点存入队列中
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            sum = sum/size;
            res.push_back(sum);
        }
        return res;
    }
};

429.N叉树的层序遍历

image.png

思路

相比之前的多了查询节点所有子树并加入队列。

/*
// 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:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> res;
        queue<Node*> q;
        if(root!=NULL) q.push(root);
        while(q.empty()==false){
            int size = q.size();
            vector<int> vec;
            for(int i=0;i<size;i++){
                Node* node = q.front();
                vec.push_back(node->val);
                q.pop();
                //查询该节点所有子树并加入队列
                for(int j=0;j<node->children.size();j++){
                    q.push(node->children[j]);
                }
            }
            res.push_back(vec);
        }
        return res;
    }
};

515.在每个树行中找最大值

image.png

思路

class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int> res;
        //定义一个类型为TreeNode的队列
        queue<TreeNode*> q;
        //如果树不为空,则将根节点放入队列中
        if(root!=nullptr) q.push(root);
        //当队列不为空时
        while(q.empty()==false){
            //定义一个size,记录每层节点数,也是for循环的次数
            int size = q.size();
            int maxnum = INT32_MIN; //记录每层节点值最大值
            //循环遍历每层的节点
            for(int i=0;i<size;i++){
                //获取队列首部节点
                TreeNode* node = q.front();
                //弹出队列首部节点
                q.pop();
                maxnum=max(maxnum,node->val);
                //将下一层的节点存入队列中
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            res.push_back(maxnum);
        }
        return res;
    }
};

116.填充每个节点的下一个右侧节点指针

image.png

思路

在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了。

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if (root != NULL) que.push(root);
        while (!que.empty()) {
            int size = que.size();
            Node* nodePre;
            Node* node;
            for (int i = 0; i < size; i++) {
                //如果是当前层的第一个节点,用nodePre记录一下这个节点
                if (i == 0) {
                    nodePre = que.front(); // 取出一层的头结点
                    que.pop();
                    node = nodePre;
                }
                //不是第一个节点:让上一个节点的next指向本节点
                else {
                    node = que.front(); //取出本节点node
                    que.pop();
                    nodePre->next = node; // 本层前一个节点的next指向本节点
                    nodePre = nodePre->next;
                }
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            nodePre->next = NULL; // 本层最后一个节点指向NULL
        }
        return root;

    }
};

117.填充每个节点的下一个右侧节点指针II

image.png

思路

和116一样。

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if (root != NULL) que.push(root);
        while (!que.empty()) {
            int size = que.size();
            vector<int> vec;
            Node* nodePre;
            Node* node;
            for (int i = 0; i < size; i++) {
                if (i == 0) {
                    nodePre = que.front(); // 取出一层的头结点
                    que.pop();
                    node = nodePre;
                } 
                else {
                    node = que.front();
                    que.pop();
                    nodePre->next = node; // 本层前一个节点next指向本节点
                    nodePre = nodePre->next;
                }
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            nodePre->next = NULL; // 本层最后一个节点指向NULL
        }
        return root;
    }
};

104.二叉树的最大深度

image.png

思路

class Solution {
public:
    int maxDepth(TreeNode* root) {
        int depth = 0;//记录深度
        queue<TreeNode*> q;
        if(root!=nullptr) q.push(root);
        while(q.empty()==false){
            int size = q.size();
            depth++;//每遍历一层,depth++
            for(int i=0;i<size;i++){
                TreeNode* node = q.front();
                q.pop();
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
        }
        return depth;
    }
};

111.二叉树的最小深度

image.png

思路

注意:只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点

class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == NULL) return 0;
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        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;
    }
};

226. 翻转二叉树

image.png

递归法

翻转二叉树.gif

递归三部曲:

  1. 确定递归函数的参数和返回值:
  • 参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。
  • 返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode*
  1. 确定终止条件
  • 当前节点为空的时候,就返回
  1. 确定单层递归的逻辑
  • 因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 左
        invertTree(root->right);        // 右
        return root;
    }
};

前序遍历法

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        stack<TreeNode*> st;
        st.push(root);
        while(!st.empty()) {
            TreeNode* node = st.top();              // 中
            st.pop();
            swap(node->left, node->right);
            if(node->right) st.push(node->right);   // 左:因为是用栈模拟,所以先存右,后面pop在后
            if(node->left) st.push(node->left);     // 右:因为是用栈模拟,所以后存左,后面pop在前
        }
        return root;
    }
};

层次遍历法

class Solution {
public:
    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. 对称二叉树

image.png

递归法

image.png

  1. 确定递归函数的参数和返回值
  • 因为要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。返回值是bool类型。
  1. 确定终止条件
  • 要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

  • 节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点

    • 左节点为空,右节点不为空,不对称,return false
    • 左不为空,右为空,不对称 return false
    • 左右都为空,对称,返回true
  • 此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

    • 左右都不为空,比较节点数值,不相同就return false
  1. 确定单层递归的逻辑
  • 进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

    • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子
    • 比较内测是否对称,传入左节点的右孩子,右节点的左孩子
    • 如果左右都对称就返回true ,有一侧不对称就返回false 。
class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right) {
        // 首先排除空节点的情况
        //一边为空,一边不为空,直接false
        if (left == NULL && right != NULL) return false;
        else if (left != NULL && right == NULL) return false;
        //两边都为空,对称,true
        else if (left == NULL && right == NULL) return true;
        // 排除了空节点,再排除数值不相同的情况
        else if (left->val != right->val) return false;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);   // 外层——》左子树:左、 右子树:右
        bool inside = compare(left->right, right->left);    // 内层——》左子树:右、 右子树:左
        bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)
        return isSame;
    }
    bool isSymmetric(TreeNode* root) {
        //树为空
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }
};