LeetCode-数据结构(六)-树专题

本文详细解析了四种二叉树相关的算法问题:二叉树的层序遍历、求解最大深度、判断对称性和寻找路径总和。通过递归和广度优先搜索(BFS)策略,介绍了不同的解题思路和代码实现。对于每个问题,不仅提供了基础的解决方案,还探讨了优化和简化的方法,有助于深化对二叉树操作的理解。
摘要由CSDN通过智能技术生成

第十一天

102-二叉树的层序遍历

在这里插入图片描述
经典层序遍历,利用队列模拟,由于输出时是按照层序的二维数组,所以需要记录每层入队的数目

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(!root)
            return {};
        vector<vector<int>> ans;
        queue<TreeNode * > q;
        q.push(root);
        //经典广搜模板
        while(!q.empty())
        {
            int cnt = q.size();//记录入队数量
            vector<int> temp;
            while(cnt--)
            {
                TreeNode * cur = q.front();
                q.pop();
                temp.push_back(cur->val);
                if(cur->left!=nullptr)q.push(cur->left);
                if(cur->right!=nullptr)q.push(cur->right);
            }
            ans.push_back(temp);
            temp.clear();//记得清空
        }
        return ans;
    }
};

104-二叉树的最大深度

在这里插入图片描述
思路1:递归
最朴素的思路就是深搜,每次往下一层,则depth+1,直到到叶子节点,则更新最大depth

class Solution {
public:
    void order(TreeNode * root, int depth, int & ans)
    {
        if(root==nullptr)
            return;
        order(root->left, depth+1, ans = max(ans, depth+1));
        //利用ans记录最大depth
        order(root->right, depth+1, ans = max(ans, depth+1));
    }
    int maxDepth(TreeNode* root) {
        int ans = 0;
        if(root==nullptr)
            return ans;
        order(root, 0, ans);
        return ans;
    }
};

简化版

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root==nullptr)
            return 0;
        //注意这里的递归写法
        return max(maxDepth(root->left), maxDepth(root->right))+1;
    }
};

思路2:广度搜索BFS
对于二叉树的遍历来说,也就是层序遍历,可以天然的记录层次也就是深度

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        queue<TreeNode*> Q;
        Q.push(root);
        int ans = 0;
        while (!Q.empty()) {
            int sz = Q.size();
            while (sz--) {
                TreeNode* node = Q.front();Q.pop();
                if (node->left) Q.push(node->left);
                if (node->right) Q.push(node->right);
            }
            ans += 1;//记录深度
        } 
        return ans;
    }
};

101-对称二叉树

在这里插入图片描述
基础都是DFS——当然BFS也可以做,具体解法见LeetCode官网
思路1:利用flag带回结果
判断一颗二叉树是否对称,主要是两点:

  • 对称节点的值相同
  • 树型结构要对称

所以最朴素的思想就是设定两个指针去分别遍历左子树和右子树,对其结构和节点的值进行判断和比较,则遍历时应该遵循

对于左子树指针left和右子树指针right来说
left->left->val = right->right->val
left->right->val = right->left->val

同样需要注意空指针的特判

如何判断其对称?不妨把这个问题换一下:如何判断其不对称呢?显然从值和结构两个方面:

  • 当对应节点的值不同时,则结构不同
  • 当两个指针仅有一个指空时,说明其结构不同

当没有上述问题时,则说明该二叉树是对称的,这里我们利用flag来标志是否对称

class Solution {
public:
    void Judge(TreeNode * left, TreeNode * right,int&flag)
    {
    	//当左右指针中仅有一个为空,则其结构不对称
        if((left==nullptr&&right!=nullptr)||(left!=nullptr&&right==nullptr))
        {
            flag = 1;
            return;
        }
        //都为空,无法确定其一定对称,直接返回
        else if(left==nullptr&&right==nullptr)
        {
            return;
        }
        //值不同一定不对称
        else if(left->val!=right->val)
        {
            flag = 1;
            return;
        }
        //两个指针按照对称的方向遍历
        Judge(left->left, right->right, flag);
        Judge(left->right, right->left, flag);           
    }
    bool isSymmetric(TreeNode* root) {
    	//特判仅一个节点的情况
        if(root->left==nullptr&&root->right==nullptr)
            return true;
        TreeNode * left = root->left;
        TreeNode * right = root->right;
        int flag = 0;
        //根据flag判断其是否对称
        Judge(left, right, flag);
        if(flag)
            return false;
        else
            return true;
    }
};

思路2:优化递归条件
当然第一个思路的做法比较麻烦,利用flag进行结果的带回,我们可不可以将其优化一下?
考虑到对称成立的多个条件,我们可以将这些条件进行组合,放在一起进行最终结果的判断

  • 仅从结构上来说,当前节点都存在,则结构对称;当前节点都不存在,则结构对称;当前节点只有一个存在,则结构不对称
  • 仅从值上来说,当前节点值相同,结构对称
    则把上述条件用&&并列,则可以得到最终是否对称的判断条件
class Solution {
public:
    bool check(TreeNode * left, TreeNode * right)
    {
        if(!left&&!right)
            return true;
        if(!left||!right)
            return false;
        //注意这里条件判断的新写法从结构和值上分别进行对称的判断
        return left->val == right->val && check(left->left, right->right) && check(left->right, right->left);
    }
    bool isSymmetric(TreeNode* root) {
        if(root->left==nullptr&&root->right==nullptr)
            return true;
        TreeNode * left = root->left;
        TreeNode * right = root->right;
        return check(left, right);
    }
};

第十二天

226-翻转二叉树

在这里插入图片描述
思路:对二叉树进行翻转,我们同样从深度搜索DFS的角度来考虑

从递归的角度来说,递归的边界是什么?递归的下一层是什么?
二叉树的翻转也就是对于每一个根节点来说将其左右子树进行互换

如果当前结点为NULL,则其没有左右子树,那么递归无法往下走,则该返回,这就是递归的边界条件;
当前结点存在,则其一定有左右子树,递归往其左右子树上走,这就是递归的下一层;
而交换左右子树的工作应该在递归的回溯上,从叶子往上依次进行交换。

理解了上述两点,则不难写出以下代码:

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;
    }
};

112-路径总和

在这里插入图片描述
思路1:DFS
目标是找到一条从根节点到叶子结点的路径,使得路径上的值相加等于targetSum,那么我们需要明确两个问题:

  • 如何判断当前的值和targetSum相等?
  • 如何判断已经走到叶子结点?

同样这是一个多条件的递归问题,我们可以借鉴上面101-对称二叉树的思路,将条件进行并列,由于我们只需要存在一条路径即可,所以条件的并列应该使用(这里和对称二叉树不一样,需要读者仔细思考)
同样从特殊结点的返回开始考虑:

  • 对于空结点:其肯定不是叶子节点,直接返回false
  • 对于叶子节点:如果前面的路径和加上当前叶子结点的值之和等于targetSum,则两个条件都满足,返回true;否则返回false
    则有以下代码:
class Solution {
public:
    bool check(TreeNode * root, int targetSum, int temp)
    {
        if(root==nullptr)//对于空节点
            return false;
        else if(root->left==nullptr&&root->right==nullptr)//叶子结点
        {
            if(targetSum==temp+root->val)
                return true;
            else
                return false;
        }
        //只需要满足存在一条路径即可
        return check(root->left,targetSum, temp+root->val) || check(root->right, targetSum, temp+root->val);//每次向下递归时,需要给temp加上当前结点的值
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root==nullptr)
            return false;
        int cnt = 0;//用于计算当前路径值之和
        return check(root, targetSum, cnt);
    }
};

思路2:BFS
做过BFS相关题目的同学读完题目肯定很熟悉——这不就是变形的单源路径问题嘛?——是的,只是把矩阵换成了二叉树,把求最短变成了求相等。所以我们同样可以利用队列来模拟广度搜索,实现问题的解决。

我们需要两个队列分别存放结点和结点的累加和(也可以说当前路径的长度);
每次出队则判断其是否为叶子节点,是则将其值加上前面路径长度进行判断,相同则直接返回true;
将出队节点(非叶子节点)的左右子树节点入队,同时更新当前路径的长度
直到队列为空

这里直接借鉴leetCode官网的代码进行讲解

class Solution {
public:
    bool hasPathSum(TreeNode *root, int sum) {
        if (root == nullptr) {
            return false;
        }
        queue<TreeNode *> que_node;
        queue<int> que_val;
        que_node.push(root);
        que_val.push(root->val);
        while (!que_node.empty()) {
        	//出队取值
            TreeNode *now = que_node.front();
            int temp = que_val.front();
            que_node.pop();
            que_val.pop();
            //叶子节点判断
            if (now->left == nullptr && now->right == nullptr) {
                if (temp == sum) {
                    return true;
                }
                continue;
            }
            //左右节点入队,路径长度更新
            if (now->left != nullptr) {
                que_node.push(now->left);
                que_val.push(now->left->val + temp);
            }
            if (now->right != nullptr) {
                que_node.push(now->right);
                que_val.push(now->right->val + temp);
            }
        }
        return false;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/path-sum/solution/lu-jing-zong-he-by-leetcode-solution/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国家一级假勤奋研究牲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值