须知
- 参数列表的那些参数是上层传到下层的,是下层需要知道的关于上层节点的信息;
- 返回值是下层传回上层的,是上层需要知道的下面所有节点的情况信息(例如,左子树的信息);
- 当参数中有引用时,注意此参数无副本,需要在整个流程中维护此参数的一致性,例如push_back()之后记得在return前pop_back()
1. 体会返回值和参数在递归中 如何使用
1.1 path sum
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
For example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
class Solution {
private:
bool visit(TreeNode *node, int sum, int now) {
now += node->val;
if (node->left == NULL && node->right == NULL)
return sum == now;
bool isleft = false, isright = false;
if (node->left != NULL)
isleft = visit(node->left, sum, now);
if (node->right != NULL)
isright = visit(node->right, sum, now);
return isleft || isright;
}
public:
bool hasPathSum(TreeNode *root, int sum) {
if (root == NULL) return false;
return visit(root, sum, 0);
}
};
此题中, 下层需要知道:上面路径上的节点到底sum是多少了;而上层则需要知道:子树有没有路径和为sum的
这么写也可以:(下面有没有和为sum - root->val的路径?)
class Solution {
private:
bool visit(TreeNode *node, int sum) {
sum -= node->val;
if (node->left == NULL && node->right == NULL)
return sum == 0;
bool isleft = false, isright = false;
if (node->left != NULL)
isleft = visit(node->left, sum);
if (node->right != NULL)
isright = visit(node->right, sum);
return isleft || isright;
}
public:
bool hasPathSum(TreeNode *root, int sum) {
if (root == NULL) return false;
return visit(root, sum);
}
};
1.2 Balanced Binary Tree
返回值: 用来返回给上层调用。可以传递给上
层下层(例如子树)的信息。
有返回值版本
class Solution {
public:
bool isBalanced(TreeNode *root) {
bool isbal = true;
int height = 0;
visit(root, isbal);
return isbal;
}
int visit(TreeNode *node, bool &isbal) {
if (node == NULL) {
isbal = true;
return 0;
}
bool isballeft;
int heightleft = visit(node->left, isballeft);
bool isbalright;
int heightright = visit(node->right, isbalright);
int height = max(heightleft, heightright) + 1;
if (isballeft && isbalright && abs(heightleft - heightright) <= 1)
isbal = true;
else
isbal = false;
return height;
}
};
无返回值版本
class Solution {
public:
bool isBalanced(TreeNode *root) {
bool isbal = true;
int height = 0;
visit(root, isbal, height);
return isbal;
}
void visit(TreeNode *node, bool &isbal, int &height) {
if (node == NULL) {
isbal = true;
height = 0;
return;
}
bool isballeft;
int heightleft;
visit(node->left, isballeft, heightleft);
bool isbalright;
int heightright;
visit(node->right, isbalright, heightright);
height = max(heightleft, heightright) + 1;
if (isballeft && isbalright && abs(heightleft - heightright) <= 1)
isbal = true;
else
isbal = false;
}
};
2. 体会 传引用 在递归中 如何使用
传引用在整个程序运行中只有这一个东西,没有副本,所以它可以用来跨层传递信息。
2.1 Path Sum II
Given a binary tree and a sum, find all root-to-leaf paths where each path’s sum equals the given sum.
For example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
return
[
[5,4,11,2],
[5,8,4,5]
]
本题中,path在整个调用过程中只有一个,没有副本,在return之前都要pop_back()一下,把之前加入的节点去掉,从而维护上层节点path的一致性。另外把中途得到的结果存入rs。因为等程序运行完,path应该是空的。
class Solution {
public:
vector<vector<int> > pathSum(TreeNode *root, int sum) {
vector<int> path;
vector<vector<int>> rs;
if (root == NULL) return rs;
visit(root, sum, path, rs);
return rs;
}
void visit(TreeNode *node, int sum, vector<int> &path, vector<vector<int>> &rs) {
sum -= node->val;
path.push_back(node->val);
if (node->left == NULL && node->right == NULL) {
if (sum == 0) rs.push_back(path);
path.pop_back();
return;
}
if (node->left != NULL)
visit(node->left, sum, path, rs);
if (node->right != NULL)
visit(node->right, sum, path, rs);
path.pop_back();
}
};
或者:
class Solution {
public:
vector<vector<int> > pathSum(TreeNode *root, int sum) {
vector<vector<int>> rs;
if (root == NULL) return rs;
vector<int> tmp;
visit(root, sum, tmp, rs);
return rs;
}
void visit(TreeNode *root, int sum, vector<int> &tmp, vector<vector<int>> &rs)
{
sum -= root->val;
tmp.push_back(root->val);
if (root->left == NULL && root->right == NULL && sum == 0)
{
rs.push_back(tmp);
return;
}
if (root->left)
{
visit(root->left, sum, tmp, rs);
tmp.pop_back();
}
if (root->right)
{
visit(root->right, sum, tmp, rs);
tmp.pop_back();
}
}
};
注意,第一个版本运行完,path是空的;而第二个版本运行完path.size() 是1。因为第一个版本每处理一个node,函数的所有出口都pop_back(); 而第二个版本,只有处理完左子树或右子树的时候才会pop_back()。
个人倾向于第一种写法。
2.2 Subset II (讲DFS的另一篇blog: http://blog.csdn.net/legend0011/article/details/41827697)
用tmp来维护当前path的内容,与2.1同理。
class Solution {
private:
void dfs(vector<int> &S, vector<int> &tmp, vector<vector<int>> &rs, int i) {
rs.push_back(tmp);
for (int k = i; k < S.size(); ++k) {
if (k > i && S[k] == S[k-1]) continue; //去重
tmp.push_back(S[k]);
dfs(S, tmp, rs, k+1);
tmp.pop_back();
}
}
public:
vector<vector<int> > subsetsWithDup(vector<int> &S) {
vector<int> tmp;
vector<vector<int>> rs;
sort(S.begin(), S.end());
dfs(S, tmp, rs, 0);
return rs;
}
};
2.2 Convert Sorted List to Binary Search Tree
递归中 传引用起到的作用 >= 返回值的作用
也就是说返回值可以干的事,通过传引用都可以
中序遍历取list中的元素,每new一个TreeNode就往后挪一个。想想如何靠传 ListNode *&head 来达到如此的效果。体会传引用的作用。
class Solution {
private:
TreeNode *buildHelper(ListNode *&head, int l, int r) { //中序遍历的顺序 来取list中的元素。
if (l > r) return NULL;
int mid = l + (r - l) / 2;
TreeNode *left = buildHelper(head, l, mid - 1);
TreeNode *root = new TreeNode(head->val);
root->left = left;
head = head->next;
root->right = buildHelper(head, mid + 1, r);
return root;
}
public:
TreeNode *sortedListToBST(ListNode *head) {
int n = 0;
for (ListNode *t = head; t != NULL; t = t->next) {
n++;
}
return buildHelper(head, 0, n - 1);
}
};
2.3
3. 子问题的定义方式
子问题的定义方式不只一种!
如 Symmetric Tree
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
For example, this binary tree is symmetric:
1
/ \
2 2
/ \ / \
3 4 4 3
But the following is not:
1
/ \
2 2
\ \
3 3
子问题的定义方式为,左子树的左子树和右子树的右子树是否一致,左子树的右子树和右子树的左子树是否一致:
class Solution {
public:
bool isSymmetric(TreeNode *root) {
if (root == NULL) return true;
return isgood(root->left, root->right);
}
bool isgood(TreeNode *l, TreeNode *r) {
if (l == r) return true; //both are NULL
if (l == NULL && r != NULL) return false;
if (l != NULL && r == NULL) return false;
if (l->val == r->val)
return isgood(l->left, r->right) && isgood(l->right, r->left);
else
return false;
}
};