代码随想录第十五天 二叉树层序遍历 226、101

二叉树的层序遍历

层序遍历相比较迭代和递归来说,简单很多。使用额外容器queue来存放结点,将一层的元素存入到队列中,再按顺序输出。因为队列是先进先出,所以按照左右的顺序正常压入就行。代码如下:

vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        vector<vector<int>> ans;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size(); //que的size表示这次需要处理几个结点
            vector<int> vec;
            while(size--){
                TreeNode* cur = que.front(); //当前需要处理的结点
                que.pop();
                vec.push_back(cur->val);
                //弹出后需要将其左右孩子压入队列,在下一次循环中遍历
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            ans.push_back(vec);
            vec.clear();
        }
        return ans;
    }

因为这里输出的是二维数组,所以在while循环中需要初始化一个储存当前层的元素。

LeetCode 637. 二叉树的层平均值

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

本题需要注意的就是sum需要是double类型。另外因为需要求平均值,所以不能使用while(size--)循环,改为for(int i = 0; i < size ; i++)

LeetCode 429. N 叉树的层序遍历

这题其实没什么东西,很简单。只不过是在处理当前节点的子节点时,从左右孩子变成了遍历一个结点数组。

vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> ans;
        queue<Node*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            vector<int> vec;
            while(size--){
                Node* cur = que.front();
                vec.push_back(cur->val);
                que.pop();
                for(auto it = cur->children.begin(); it != cur->children.end(); it++){
                    que.push(*it);
                }
            }
            ans.push_back(vec);
        }
        return ans;
    }

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

之前做过类似找最大最小值的题目,这里令对比对象初始化为INT_MIN

vector<int> largestValues(TreeNode* root) {
        vector<int> ans;
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            int temp = INT_MIN;
            while(size--){
                TreeNode* node = que.front();
                temp = node->val > temp ? node->val : temp;
                que.pop();
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            ans.push_back(temp);
        }
        return ans;
    }

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

本题就是在遍历时加一个判断,如果为本层最后一个结点,使其指向NULL即可。

Node* connect(Node* root) {
        queue<Node*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            while(size--){
                Node* cur = que.front();
                que.pop();
                if(size != 0){
                    cur->next = que.front();
                }else{
                    cur->next = NULL;
                }
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
        }
        return root;
    }

LeetCode 104. 二叉树的最大深度

这里要知道二叉树深度的概念,以及高度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
二叉树的高度是表示当前结点所在的位置。比如二叉树最下方的叶子结点的高度为1,根节点的高度与深度相同。
采用层序遍历,在一层结束后,depth++即可。

int maxDepth(TreeNode* root) {
        int ans = 0;
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            while(size--){
                TreeNode* cur = que.front();
                que.pop();
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            ans++;
        }
        return ans;
    }

111. 二叉树的最小深度

当左右孩子都为空的时候,才说明遍历到最低点了,如果其中一个孩子为空则不是最低点。所以当找到左右孩子都为空的结点时,直接返回结果即可。

int minDepth(TreeNode* root) {
        int min = 0;
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            min++;
            while(size--){
                TreeNode* cur = que.front();
                que.pop();
                //找到即返回
                if(cur->left == NULL && cur->right == NULL) return min;
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
        }
        return min;
    }

LeetCode 226. 翻转二叉树

本题也是有很多解法,递归迭代都可以实现。

  • 递归
    递归上来还是三部曲:

    1. 输入就是当前需要反转的结点,输出就是反转过后的结点。
    2. 终止条件是空结点时,结束本层递归
    3. 递归函数体就是反转左右结点

    代码如下:

    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 左
        invertTree(root->right);        // 右
        return root;
    }
    

    后序递归的话只需要把反转逻辑放在最后即可。
    如果是中序遍历,会将一个结点反转两次,所以不推荐中序,中序需要把代码写成这样:

    	
        invertTree(root->left);         // 左
        swap(root->left, root->right);  // 中
        invertTree(root->left);        // 右
    

    我需要反转的右结点实际上是经过反转后的左节点,所以反转两次left。

  • 迭代
    迭代其实也很简单,在遍历的过程中添加一个swap完成反转,代码如下:

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

    这里只给出前序迭代的代码,后序类似。

    迭代的统一写法(前序):

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

    还是一样,在处理中间结点的时候swap一下。

  • 层序
    最后是层序,层序这题也可以做,相同的思路,一样的味道。
    代码如下:

    TreeNode* invertTree(TreeNode* root) {
        queue<TreeNode*> que;
        if(root == NULL) return root;
        que.push(root);
        while(!que.empty()){
            int size = que.size();
            while(size--){
                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;
    }
    

LeetCode 101. 对称二叉树

这题leetcode上给出的是简单难度,实际上思路和代码复杂度上怎么说也得是个中等难度。
首先需要搞清楚几种情况:

  • 左节点为空,右节点不为空,不对称
  • 左不为空,右为空,不对称
  • 左右都为空,对称
  • 左右都不为空且值不等,不对称
  • 左右都不为空且值相等,对称

然后要清楚对比的是二叉树两侧的结点,分为内侧和外侧。下面给出卡哥的图:
在这里插入图片描述
这样看比较清晰。
最后看一颗二叉树是否是对称二叉树,实际上是对比这颗树的左右子树,也就是对两棵子树同时进行遍历。
接下来就是考虑使用的遍历方式和遍历顺序,首先是递归遍历。

  • 递归
    递归的三要素:

    1. 输入输出:输入应该是两侧的两个结点left和right进行对比,所以输入是两个参数,输出即是否对称,为bool
    2. 终止条件:不对称即false,直接退出递归
    3. 递归函数体

    递归代码如下:

    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 outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
    	bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
    	bool isSame = outside && inside;                    // 左子树:中、 右子树:中(逻辑处理)
    	return isSame;
    }
    

    先把外侧的结点处理完成,再处理内侧结点,也就是代码中的outsideinsideleft->left就是左子树继续往外走,right->right就是右子树继续往外走。最后返回值必须两者都是true才说明是对称。
    其次注意if和else if语句中的顺序,首先需要把有可能出现空结点的情况讨论完,再去讨论不是空结点的情况,否则会出现访问空结点的错误。

  • 迭代
    迭代遍历首先还是要模拟一下入栈和出栈的过程,只有把过程搞明白了,才能写出正确的代码。直接看代码以及注释:

    bool isSymmetric(TreeNode* root) {
        queue<TreeNode*> que;
        //因为是同时遍历左右子树,所以要先把根节点的左右孩子压入
        que.push(root->left);
        que.push(root->right);
        while(!que.empty()){
        	//取出结点进行对比。先压入再取出处理也是迭代中必须的手法
            TreeNode* l = que.front();
            que.pop();
            TreeNode* r = que.front();
            que.pop();
            if(l == NULL && r == NULL) continue; //首先处理都为空的情况,直接进入下一次循环
            //再处理不对称的情况
            if(l == NULL && r != NULL) return false;
            else if(l != NULL && r == NULL) return false;
            else if(l->val != r->val) return false;
            //最后按照顺序将当前结点的子结点压入栈中
            que.push(l->left);
            que.push(r->right);
            que.push(l->right);
            que.push(r->left);
        }
        return true;
    }
    

    迭代相对于递归的写法更复杂一些,我认为将结点成对的入栈,再成对的取出进行对比是本题的核心思想。

最后

因为二叉树是面试中常考的数据结构,对二叉树的处理也是手撕代码中出现频率较高的,需要熟练掌握,所以要经常回来复习一下!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值