二叉树_题目13:113. 路径总和 II

69 阅读3分钟

题目13:113. 路径总和 II

113. 路径总和 II

同类题目

前置知识

题目描述

  1. 给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

    叶子节点 是指没有子节点的节点。

    示例 1:

     输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
     输出:[[5,4,11,2],[5,8,4,5]]
    

    示例 2:

     输入:root = [1,2,3], targetSum = 5
     输出:[]
    

    示例 3:

     输入:root = [1,2], targetSum = 0
     输出:[]
    

    提示:

    • 树中节点总数在范围 [0, 5000]
    • -1000 <= Node.val <= 1000
    • -1000 <= targetSum <= 1000

思路分析

先说结论:前序遍历 + 回溯

本题中要获取所有满足路径和为 targetSum 的从根到叶子节点的路径,需要有全局变量记录从根到叶子的路径,当路径之和等于targetSum 时,将其加入到结果列表中。

回溯: 这里有个不算窍门的窍门,问完成一件事情的所有解决方案,一般采用回溯算法(深度优先遍历)完成。

什么时候回溯?

从调用下一层结束后,进行回溯。

程序代码

方法一:使用一个全局变量记录经过的路径

 //https://leetcode.cn/problems/path-sum-ii/description/
 //113. 路径总和 II
 #include<iostream>
 #include<vector>
 ​
 using namespace std;
 struct TreeNode {
     int val;
     TreeNode *left;
     TreeNode *right;
     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 };
 ​
 class Solution {
 private:
     vector<vector<int>> res;
     vector<int> num;
 public:
     void travel(TreeNode* root, int targetSum,int curSum)
     {
         if(root == nullptr)
         {
             return;
         }
         
         num.push_back(root->val);
         curSum += root->val;
         // 到达叶子节点,然后进行判断
         if(root->left == nullptr && root->right == nullptr)
         {       
             if(curSum == targetSum)
             {
                 res.push_back(num);
             }
         }
 ​
         travel(root->left,targetSum,curSum);    
         travel(root->right,targetSum,curSum);
         // 回溯,curSum 的回溯可以不写,因为不算全局变量也不是传递引用,这一轮的值改变,不会带回上一轮
         // curSum -= root->val;
         num.pop_back();
 ​
     }
     vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
         travel(root,targetSum,0);
         return res;
     }
 };

上述代码的优化版本,不需要curSum这个变量,直接从targetSum 中减去 root->val ,到叶子节点后判断是否等于0

 class Solution {
 private:
     vector<vector<int>> res;
     vector<int> num;
 public:
     void travel(TreeNode* root, int targetSum)
     {
         if(root == nullptr)
         {
             return;
         }
         // 添加路径
         num.push_back(root->val);
         // 目标值减添加后的值
         targetSum -= root->val;
         // 到达叶子节点,然后进行判断
         if(root->left == nullptr && root->right == nullptr)
         {       
             if(0 == targetSum)
             {
                 res.push_back(num);
             }
         }
 ​
         travel(root->left,targetSum);    
         travel(root->right,targetSum);
         
         // 回溯,恢复这一层的路径和targetSum
         // targetSum 这个可以不写,这是C++ 语言特性,因为传参是值复制
         targetSum += root->val;
         num.pop_back();
 ​
     }
     vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
         travel(root,targetSum);
         return res;
     }
 };

隐藏回溯

 class Solution {
 private:
     vector<vector<int>> res;
     vector<int> num;
 public:
     void travel(TreeNode* root, int targetSum)
     {
         if(root == nullptr)
         {
             return;
         }
         
         num.push_back(root->val);
        //targetSum -= root->val;
         // 到达叶子节点,然后进行判断
         if(root->left == nullptr && root->right == nullptr)
         {       
             if(0 == targetSum -  root->val)
             {
                 res.push_back(num);
             }
         }
         // 隐藏回溯到变量中,为什么不需要回溯?因为在当前的这一层中没有改变targetSum 的值
         travel(root->left,targetSum - root->val);    
         travel(root->right,targetSum - root->val);        
         num.pop_back();
     }
     
     vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
         travel(root,targetSum);
         return res;
     }
 };

复杂度分析

时间复杂度:O(n),其中 n 是二叉树中的节点个数。每个节点都会遍历一次。

空间复杂度:O(n),其中 n 是二叉树中的节点个数。递归栈的开销,极限情况树是链状的高度为O(N)。

题目感悟

回溯的位置:我们只要在递归之前做出选择,在递归之后撤销刚才的选择

更多相关知识:github.com/pingguo1987…