二叉树与递归问题2

目录

一:左叶子之和

二:求叶子到根节点的最短路径 

三:二叉树所有路径

         回溯的知识 

四:任意起点和任意终点的路径和 

五:在bst上求两个结点的最近公共祖先 


一:左叶子之和

 注意审题:求的是左叶子(左孩子的叶子结点


思路一:

int sumOfLeftLeaves(TreeNode* root) {
       if(root==NULL) return 0;
       int res=0;
       if(root->left&&!root->left->right&&!root->left->left)
           res+=root->left->val;
       res+=sumOfLeftLeaves(root->left)+sumOfLeftLeaves(root->right);
       return res;
    }
  1. 根节点为空,直接return 0;定义变量res计算左孩子值
  2. 定义接收结果的条件----左孩子+叶子(左孩子的左右孩子均为空)
  3. 递归相加---传入左右孩子
  4. 别忘了return res

法二:

int sumOfLeftLeaves(TreeNode* root) {
      if(root==NULL) return 0;
      //遍历
       int res=0;
      //左子树
      if(root->left){
          if(!root->left->left&&!root->left->right)
                res+=root->left->val;//左右孩子为空返回该结点值
          else  res+=sumOfLeftLeaves(root->left);
      }
      //右子树
      if(root->right){
          res+=sumOfLeftLeaves(root->right);
      }
      return res;
    }

深度遍历------代码符合dfs的感觉-----前序遍历

  • 左子树-----判断左孩子是否为叶子,递归左孩子
  • 右子树-----把该节点传入递归

法三

void dfs(TreeNode* root,bool isLeft,int &res){
       if(root==NULL) return ;
       if(!root->left&&!root->right&&isLeft){
            res+=root->val;
       }
       dfs(root->left,true,res);
       dfs(root->right,false,res);
    }
    int sumOfLeftLeaves(TreeNode* root) {
        int res=0;
        dfs(root,false,res);
        return res;
    }

这里更体现了“深度优先遍历”,传入所求和该节点自身是不是左孩子去判断。

 void dfs(TreeNode* root,bool isLeft,int *res){
       if(root==NULL) return ;
       if(!root->left&&!root->right&&isLeft){
            *res+=root->val;
       }
       dfs(root->left,true,res);
       dfs(root->right,false,res);
    }
    int sumOfLeftLeaves(TreeNode* root) {
        int res=0;
        dfs(root,false,&res);
        return res;
    }

或者传入指针:

*res+=root->val; dfs(root,false,&res);

二:求叶子到根节点的最短路径 

 分析:

这道题思路不难,注意情况的分析,容易出错。

首先:

如果该节点有左子树和右子树,那么最短路径是左右子树最小值 

如果该节点只有单子树,那么路径一定是该单子树的最短路径

法一:

int minDepth(TreeNode* root) {
         if(root==NULL) return 0;
         if(!root->left&&!root->right) return 1;
         //递归调用
         int leftDeep=minDepth(root->left);
         int rightDeep=minDepth(root->right);
         //情况1---只有左子树或右子树
         if(root->left&&root->right) return 1+min(leftDeep,rightDeep);
         //情况2---左右子树都有
         return 1+leftDeep+rightDeep;
    }

难点在于情况的包含:根节点为空--return 0,根节点是叶子结点--return 1

如果有左右子树-----可以定义两个变量计算路径

情况:左右子树都存在,左右子树只存在一颗,这时候最好先判断都存在。


法二--广度优先遍历

int minDepth(TreeNode* root) {
        //广度优先遍历
        if(root==NULL) return 0;
        queue<TreeNode*> que;
        que.push(root);
        int deep=1;
        while(!que.empty()){
            int size=que.size();
            for(int i=0;i<size;i++){
            TreeNode* tmp=que.front();
            que.pop();
            if(!tmp->left&&!tmp->right) return deep;
            if(tmp->left)  que.push(tmp->left);
            if(tmp->right)  que.push(tmp->right);
            }//for
            deep++;
        }
        return deep;
    }

需要记录每次的队列元素,以判断当前所在层数

必须定义size变量,否则发生覆盖

int maxDepth(TreeNode* root) {
          //广度优先遍历
        if(root==NULL)  return 0;
        queue<TreeNode*> que;
        que.push(root);
        int deep=0;
        while(!que.empty()){
            int size=que.size();
            for(int i=0;i<size;i++){
                TreeNode* tmp=que.front();
                que.pop();
                if(tmp->left) que.push(tmp->left);
                if(tmp->right) que.push(tmp->right);
            }
            deep++;
        }
         return deep;
    }

 稍微一改就是求最大深度

  • 把deep定义为0
  • 去掉判断没有左右子树的语句

三:二叉树所有路径


法一:

vector<string> binaryTreePaths(TreeNode* root) {
          vector<string> res;
          if(root==NULL)  return res;
          if(!root->left&&!root->right) {//叶子结点返回
              res.push_back(to_string(root->val));}
          /*把左孩子路径并入结点*/
          vector<string> leftPath=binaryTreePaths(root->left);
          for(int i=0;i<leftPath.size();i++)
              res.push_back(to_string(root->val)+"->"+leftPath[i]);
          /*把右孩子路径并入结点*/
          vector<string> rightPath=binaryTreePaths(root->right);
          for(int i=0;i<rightPath.size();i++)
              res.push_back(to_string(root->val)+"->"+rightPath[i]);
          return res;
    }

to_string(整形):整形->字符串形


法二:

class Solution {
private:
    vector<string> res;//定义全局变量,用于返回
    void dfs(TreeNode* root,string s){//深度优先遍历
           //递归结束
           if(!root->left&&!root->right) res.push_back(s);
           //左路径
           if(root->left){
               dfs(root->left,s+"->"+to_string(root->left->val));
           }
           //右路径
           if(root->right){
               dfs(root->right,s+"->"+to_string(root->right->val));
           }
    }
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        if(root==NULL) return res;
        dfs(root,to_string(root->val));
        return res;
    }
};

很好的利用了深度优先遍历

  • 把void dfs()和全局变量vectot<string> 定义成私有函数
  • dfs需要两个参数------当前结点指针以及存储的字符串值

回溯的知识 

class Solution {
private:
    vector<string> res;
    void findPathBack(TreeNode* cur,vector<int>& node){
        /*把非空结点的值记录下来*/
        if(cur) node.push_back(cur->val);
        /*递归结束:叶子结点*/
        if(!cur->left&&!cur->right){
            string s;
             //难点:结点1->结点2->...->结点n
             //把“前面的结点和“->”视为整体”,最后并上最后一个结点
             for(int i=0;i<node.size()-1;i++){
                 s+=to_string(node[i])+"->";
             }
             s+=to_string(node[node.size()-1]);//int->string
             //并入结果
             res.push_back(s);
        }
        //递归+回溯
        if(cur->left){
            findPathBack(cur->left,node);
            node.pop_back();//回溯,去掉该节点
        }
        if(cur->right){
            findPathBack(cur->right,node);
            node.pop_back();//回溯,去掉该节点
        }
    }
public:
    vector<string> binaryTreePaths(TreeNode* root) {
       if(root==NULL) return res;
       vector<int> node;
       findPathBack(root,node);
       return res;
    }
};

如果借用,结点+“->”的形式,那么就要存在对结点回溯的语句

注意传参为了改变,加上&

也可以不定义私有变量,传三个参数

class Solution {
private:
    void traversal(TreeNode* cur, vector<int>& valNode, vector<string>& res) {
        //cur---当前结点;val---存放结点值;res---记录返回路径
        if(cur) valNode.push_back(cur->val);
        //达到叶子结点后
        if(!cur->left&&!cur->right){
        string s;
        //拼凑出:结点->。。。。->结点的形式
        for(int i=0;i<valNode.size()-1;i++){
            s+=to_string(valNode[i])+"->";
        }
        s+=to_string(valNode[valNode.size()-1]);
        //返回给res
        res.push_back(s);
        /*递归结束----return*/
        return;
       }
       if(cur->left){
        traversal(cur->left,valNode,res);
        valNode.pop_back();
       }
        if(cur->right){
        traversal(cur->right,valNode,res);
        valNode.pop_back();
       }
    }
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<int> valNode;
        vector<string> res;
        if(root==NULL) return res;
        traversal(root,valNode,res);
        return res;
    }
};

代码更改

class Solution {
private:
    vector<string> res;
    void findPathBack(TreeNode* cur,string s){
        /*把非空结点的值记录下来*/
        if(cur) s+=to_string(cur->val);
        /*递归结束:叶子结点*/
        if(!cur->left&&!cur->right){
             res.push_back(s);
             return;
        }
        //递归
        if(cur->left){
            findPathBack(cur->left,s+"->");
            //node.pop_back();  不回溯
        }
        if(cur->right){
            findPathBack(cur->right,s+"->");
            //node.pop_back();   不回溯
        }
    }
public:
    vector<string> binaryTreePaths(TreeNode* root) {
       if(root==NULL) return res;
       string s;
       findPathBack(root,s);
       return res;
    }
};

       定义的是string path,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。

       回溯就隐藏在traversal(cur->left, path + "->", result);中的 path + "->"。 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。

四:任意起点和任意终点的路径和 


法一:双递归

首先要审题:

  • 结点包含负数(比如“+8”,“-8”也会算上路径)
  • 路径不要求起点和终点,只要求必须自上而下

思想:如果传入结点是路径起点,那么只要递归找到终点即可。

           然后主算法负责更改结点起点。


总结,设计一个函数计算传入结点是路径起点符合数值的路径。主程序把所有结点都过一遍即可

代码

class Solution {
private:
    int nodeIsStart(TreeNode* node,long sum){
        //该函数把传入的node视为路径起点
        //递归查找符合sum值得序列
        if(node==NULL) return 0;//结点为空不存在
        int res=0;
        if(node->val==sum) res+=1;//因为有负数存在,不return
        //递归查找,去掉node结点值路径
        res+=nodeIsStart(node->left,sum-node->val)+nodeIsStart(node->right,sum-node->val);
        return res;
    }
public:
    int pathSum(TreeNode* root, int targetSum) {
       if(root==NULL) return 0;
       int res=nodeIsStart(root,targetSum);
       //递归调用,该点传入结点,但是targetSum不变
       res+=pathSum(root->left,targetSum)+pathSum(root->right,targetSum);
       return res;
    }
};

res+=nodeIsStart(node->left,sum-node->val);

res+=nodeIsStart(node->right,sum-node->val);

class Solution {
private:
    int dfs(TreeNode* node,long sum){
        if(node==NULL) return 0;
        int res=0;
        res+=(node->val==sum)?1:0;
        res+=dfs(node->left,sum-node->val)+dfs(node->right,sum-node->val);
        return res;
    }
public:
    int pathSum(TreeNode* root, int targetSum) {
       if(root==NULL) return 0;
       return dfs(root,targetSum)+pathSum(root->left,targetSum)+pathSum(root->right,targetSum);
    }
};

简化

五:在bst上求两个结点的最近公共祖先 

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL) return NULL;
        if(p->val<root->val && q->val<root->val){
            return lowestCommonAncestor(root->left,p,q);
        }
        if(p->val>root->val && q->val>root->val){
            return lowestCommonAncestor(root->right,p,q);
        }
        //p,q在两个分支
        return root;
    }
};

很简单,p,q在树的两端一定只能是根节点,p,q在一颗子树则不停递归

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值