您的位置:首页 > 其它

leetcode之二叉树类之路径和系列-----112/113/124/257/437 path sum(牵扯附加OJ572和OJ100, 子树和子拓扑)

2017-10-14 20:21 465 查看
OJ112:对路径定义为:从root到leaf的path,求路径和为target的路径是否存在

OJ113:对路径定义为:从root到leaf的path,求路径和为target的路径都有哪些

OJ437:对路径定义为:任意子树(含单节点)形成的路径,求路径和为target的路径有多少个

OJ124:对路径定义为:任意子树(含单节点)形成的路径,求路径和最大是多少

OJ257:对路径定义为:从root到leaf的path,求全部的路径

OJ112和OJ113和OJ257为一类,是单一递归可以解决的问题,并且都是需要做leaf才能够确认是否符合题目需求的情况,所以均用先序遍历;

OJ437和OJ124属于二叉树中的"子树类"问题,其中OJ437需要多层次递归来解决(注意OJ572也是"子树类"问题);

OJ112:只需要返回是否存在,首先路径已经被定义为从root到leaf,那么路径和必须在leaf才能做和target是否相同的判断,那么就做先序遍历,判断当前节点是否为leaf节点,非leaf的上层节点,返回左右子树的返回值的或;

OJ112代码:

class Solution {
public:
bool helper (TreeNode *cur, int sum) {
if (cur) {
if (!cur->left && !cur->right) {
if (sum == cur->val) {
return true;
} else {
return false;
}
} else {
bool l = false, r = false;
if (cur->left) {
l = helper(cur->left, sum - cur->val);
}
if (cur->right) {
r = helper(cur->right, sum - cur->val);
}
return l || r;
}
}
}

bool hasPathSum(TreeNode* root, int sum) {
if (!root) {
return false;
} else if (!root->left && !root->right) {
return (root->val == sum)?true:false;
}

return helper(root, sum);
}
};
OJ113和OJ112唯一差别是,无需返回是否存在,仅需要在leaf节点确认路径和为target时,把这个路径记下来,代码稍作修改即可

OJ113代码:

class Solution {
public:
void helper (TreeNode *cur, int sum, vector<int> v, vector<vector<int>> &res) {
if (cur) {
v.push_back(cur->val);
if (!cur->left && !cur->right) {
if (sum == cur->val) {
res.push_back(v);
}
} else {
if (cur->left) {
helper(cur->left, sum - cur->val, v, res);
}
if (cur->right) {
helper(cur->right, sum - cur->val, v, res);
}
}
}
}

vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>> res;
if (!root) {
return res;
}

vector<int> v;
helper(root, sum, v, res);
return res;
}
};

OJ257,打印从root到每一个leaf的全部路径,同样需要做leaf处才能够得到最终结果,这里的最终结果就是路径,所以和OJ112、OJ113一样也是先序遍历,当判断是leaf节点时,记录当前已走过的路径

OJ257代码:

class Solution {
public:
void helper (TreeNode *cur, string s, vector<string> &res) {
if (cur) {
if (!cur->left && !cur->right) {
s += to_string(cur->val);
res.push_back(s);
} else {
s += to_string(cur->val);
s += "->";
helper(cur->left, s, res);
helper(cur->right, s, res);
}
}
}

vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
if (!root) {
return res;
}

helper(root, "", res);
return res;
}
};


OJ124:对路径定义为从一个任意点到另一个任意点的路径,求路径和最大的路径,而且没有路径值为正负零的约束

这种情况下在任何一个节点都有可能出现路径最大值,这时需要的是每个节点都要计算比较更新最大路径和。一般如果是到leaf节点才能见分晓做判断的都用先序遍历,每一个节点都需要做判断的用后序遍历,如本题。

核心思路点:

1、每一个节点处,都要做最大路径和的判断,判断来源包括:当前节点自己(cur->val)、当前节点左子节点到右子节点的路径和、当前节点到左子节点的路径和、当前节点到右子节点的路径和,取四者最大值和最大路径和max比较,如大于max则更像max;

2、每个节点要返回给上层节点当前层的"到本节点的最大路径和",也就是剔除掉当前节点左子节点到右子节点的路径和,因为当前节点是上层节点的左或右子节点,上层节点需要用本节点的最大路径和作为它的计算1的来源,这个是本题的核心,一定想清楚。即使用当前节点自己(cur->val)、当前节点到左子节点的路径和、当前节点到右子节点的路径和,取三者最大值,作为给上一层的返回值;除空节点返回0外,其他节点均返回计算结果给上一层;

最终结果就是不断更新得到的最大路径和max

OJ124代码:

class Solution {
public:
int helper (TreeNode *cur, int sum, int &max) {
if (cur) {
int left = helper(cur->left, sum + cur->val, max);
int right = helper(cur->right, sum + cur->val, max);

if (max < cur->val) {
max = cur->val;
}
if (max < (cur->val + left)) {
max = cur->val + left;
}
if (max < (cur->val + right)) {
max = cur->val + right;
}
if (max < (cur->val + left + right)) {
max = cur->val + left + right;
}

return std::max(cur->val, std::max(cur->val + left, cur->val + right));
} else {
return 0;
}
}

int maxPathSum(TreeNode* root) {
if (!root) {
return 0;
}

int max = INT_MIN;
helper(root, 0, max);
return max;
}
};


OJ437:路径被定义为任意一个节点到另一个任意节点,也可以是单节点构成一个路径,找出路径和为target的个数

典型的子树类问题,子树类问题典型的问题描述是:每一个"半截路径"即不是从root开始到leaf结尾的路径,而是任意节点A到任意节点B,A甚至可以和B是同一个节点;而具体对于本题的要求是:节点路径和为target的有多少个。

子树类问题的解决思维是:每一个节点都是root,每一个节点也都是leaf,

而更具体的解决框架是:当前节点、左子树、右子树同时发起递归,递归内部把输入的节点像根节点一样的处理

如root节点为A,左子节点为B右子节点为C,则同时发起A、B、C的递归,B、C进而发起它们各自的左右子节点的递归,即"每个节点都作为root"做处理;然后,递归处理函数中,也要做"每个节点都是leaf"的处理,对于本题就需要对每个路径(含单节点构成的路径)与target做比较,判断是否已经符合要求,同时还要判断加入每层节点后的路径是否又符合要求;

以一个极端例子,某子树节点为10,target就是10,该节点底下N多子节点,value都是0,那么它和全部子节点组成的各种路径都符合要求;

OJ437代码:

class Solution {
public:
int helper (TreeNode *cur, int c, const int sum) {
if (cur) {
c += cur->val;
return (c == sum) + helper(cur->left, c, sum) + helper(cur->right, c, sum);
} else {
return 0;
}
}

int pathSum(TreeNode* root, int sum) {
int res = 0;
if (!root) {
return 0;
}

res = helper(root, 0, sum) + pathSum(root->left, sum) + pathSum(root->right, sum);
return res;
}
};


牵扯到子树,看看另一个子树题,OJ572,判断一个二叉树A是否是另一个二叉树B的子树

注意:子树要求完全一致,不仅节点value和拓扑必须相同,而且不可有不同的子节点,如B的A部分,底下还有其他节点,那不行

先看下"两个树是否为相同的树"(OJ100),看如何做两个二叉树是否相同的判断方法

OJ100代码:

class Solution {
public:
bool helper (TreeNode *p, TreeNode *q) {
if (!p && !q) {
return true;
} else if (!p || !q) {
return false;
} else {
if (p->val == q->val) {
bool l = helper(p->left, q->left);
if (l) {
return helper(p->right, q->right);
} else {
return false;
}
} else {
return false;
}
}
}

bool isSameTree(TreeNode* p, TreeNode* q) {
if (!p && !q) {
return true;
} else if (!p || !q) {
return false;
}

return helper(p, q);
}
};


必须节点个数、拓扑、节点value完全相同才行,对于OJ572,如:

B是如下:

3
/ \
4   5
/ \
1   2
/
0

A是如下:

4
/ \
1   2

A不是B的子树;

但如果B是如下:

3
/ \
4   5
/ \
1   2

A是如下:

4
/ \
1   2

这样A就是B的子树;

所以OJ572是OJ100的扩展,OJ100是A和B是否相同,OJ572是A是否包含有B,子树类问题的典型解决方式:同时发起递归,递归处理函数内全部当作root一样处理

步骤:

1、当前节点、左子树、右子树同时发起递归处理;进而左、右子树下的每个节点都发起递归

2、递归处理函数和OJ100的完全一样

3、主函数只要发现符合要求的子树即返回true结束,没发现就一直递归处理下去

OJ572代码:

class Solution {
public:
bool helper (TreeNode *s, TreeNode *t) {
if (!s && !t) {
return true;
} else if (!s || !t) {
return false;
} else {
if (s->val == t->val) {
return helper(s->left, t->left) && helper(s->right, t->right);
} else {
return false;
}
}
}

bool isSubtree(TreeNode* s, TreeNode* t) {
if (!s && !t) {
return true;
} else if (!s || !t) {
return false;
}

if (s->val == t->val) {
if (helper(s, t)) {
return true;
}
}

return isSubtree(s->left, t) || isSubtree(s->right, t);
}
};


从代码看出,主函数如果判断当前节点的value和待比较树的root的value都不一样,那么就已经不符合了,没必要递归处理了,减少一些陷入递归的case。

从这道题最重要需要掌握的是"子树类"问题的解决思维和解决模型;

另外,可以联想下,"比较A与B,或A与B的子树,不要拓扑完全相同,只要B也有A的结构,也就是说B的A部分还有其他子树也可以"这样的题怎么办,很显然,放宽了条件,不必一定是子树,也就不需要"!p && !q时返回true、!p || !q时返回false"的条件,取而代之的是需要比较A的左右和B的左右只要同时存在且value相同即可;所以,如果是A与B比较,那么形同OJ100的解决方法,如果是A与B的各种子树比较,就形同OJ572的解决方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: