Leetcode hot100
二叉树
1.路径总和 III
路径总和 III
思路:我们访问每一个节点 node,检测以 node 为起始节点且向下延深的路径有多少种。递归遍历每一个节点的所有可能的路径,然后将这些路径数目加起来即为返回结果。
综上:递归是比较合适的方法。
当前层需要干什么:求出以当前 root 为节点向下遍历的和 == targetsum 的个数
rootSum(TreeNode* root, int targetSum)
以当前节点 p 为目标路径的起点递归向下进行搜索。假设当前的节点 p 的值为 val,我们对左子树和右子树进行递归搜索,对节点 p 的左孩子节点 pl 求出 rootSum(pl, targetSum−val)
以及对右孩子节点 pr 求出 rootSum(pr, targetSum−val)
。节点 p 的 rootSum(p, targetSum)
即等于 rootSum(pl,targetSum−val) + rootSum(pr,targetSum−val)
,同时我们还需要判断一下当前节点 p 的值是否刚好等于 targetSum。
/**
* Definition for a binary tree node.
* 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 {
public:
//以各个节点为根节点
//root,root->left,root->right,root->left->left,root->left->right......
int pathSum(TreeNode* root, int targetSum) {
if (root == nullptr) return 0;
return rootSum(root, targetSum) + pathSum(root->left, targetSum)
+ pathSum(root->right, targetSum);
}
//以当前节点为根节点,找到从当前节点向下遍历,节点相加和为target的次数
int rootSum(TreeNode* root, long targetSum) {
if (root == nullptr) return 0;
int ret = 0;
if (root->val == targetSum) ret++;
ret += rootSum(root->left, targetSum - root->val);
ret += rootSum(root->right, targetSum - root->val);
return ret;
}
};
2.路径总和 II
路径总和 II
虽然这道题不在hot100里,但是和上一道题很类似,该题是求所有 从根节点到叶子节点 路径总和等于给定目标和的路径。显然还是用递归的方法更合适。
我们可以递归遍历记录所有的路径,同时计算路径和,如果等于那么添加到最终的vector<vector< int >>中。
函数 dfs(TreeNode* root, int targetSum)
:
递推参数: 当前节点 root ,当前目标值 targetSum。
终止条件: 若节点 root 为空,则直接返回。
递推工作:
路径更新: 将当前节点值 root->val 加入路径 vector<int>tmp
。
目标值更新: targetSum = targetSum - root->val (即目标值 targetSum 从 targetSum 减至 0 )。
路径记录: 当 (1) root 为叶节点(左右子树均为空) 且 (2) 路径和等于目标值(targetSum == 0),则将此路径 tmp 加入 ans 。
先序遍历: 递归左 / 右子节点。
路径恢复: 向上回溯前,需要将当前节点从路径 tmp 中删除,即执行 tmp.pop_back()
。
/**
* Definition for a binary tree node.
* 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 {
public:
vector<vector<int>> ans;
vector<int> tmp;
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
if (root == nullptr) return ans;
dfs(root, targetSum);
return ans;
}
void dfs(TreeNode* root, int targetSum) {
if (root == nullptr) return;
tmp.push_back(root->val);
targetSum -= root->val;
if (root->left == nullptr && root->right == nullptr && targetSum == 0) {
ans.push_back(tmp);
}
dfs(root->left, targetSum);
dfs(root->right, targetSum);
tmp.pop_back();
}
};
3.二叉树的所有路径
二叉树的所有路径
再来看一道相似的题目,只需要记录从根节点到叶子节点的路径即可
/**
* Definition for a binary tree node.
* 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 {
public:
vector<string> ans;
vector<string> binaryTreePaths(TreeNode* root) {
if (root == nullptr) return ans;
string tmp;
dfs(root, tmp);
return ans;
}
void dfs(TreeNode* root, string tmp) {
if (root == nullptr) return;
tmp += to_string(root->val);
if (root->left == nullptr && root->right == nullptr) {
ans.push_back(tmp);
} else {
tmp += "->";
dfs(root->left, tmp);
dfs(root->right, tmp);
}
}
};
4.二叉树的最近公共祖先
二叉树的最近公共祖先
终止条件:
- 当 root 等于 p,q,则直接返回 root;
递推工作:
- 开启递归左子节点,返回值记为 left;
- 开启递归右子节点,返回值记为 right;
返回值:
根据 left 和 right,可展开为四种情况;
- 当 left 和 right 同时为空 :说明 root 的左 / 右子树中都不包含 p,q,返回 null;
- 当 left 和 right 同时不为空 :说明 p,q 分列在 root 的 异侧 (分别在 左 / 右子树),因此 root
为最近公共祖先,返回 root ; - 当 left 为空 ,right 不为空 :p,q 都不在 root 的左子树中,直接返回 right。
具体可分为两种情况:- p,q 其中 p 在 root 的 右子树 中,此时 right 指向 p(假设为 p );
- p,q 两节点都在 root 的 右子树 中,此时的 right 指向 最近公共祖先节点 ;
- 当 left 不为空 , right 为空 :与情况 3. 同理;
参考
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//找到p,找到q,或者已经到叶子节点还没找到
if (root == nullptr || root == p || root == q) return root;
//遍历左子树
TreeNode* left = lowestCommonAncestor(root->left, p, q);
//遍历右子树
TreeNode* right = lowestCommonAncestor(root->right, p, q);
//左右子树点都没找到
if (left == nullptr && right == nullptr) return nullptr;
//右边没有p或者q,left要么是p/q,或者是p/q的公共祖先
if (left != nullptr && right == nullptr) return left;
if (right != nullptr && left == nullptr) return right;
return root;
}
};
回溯
1.电话号码的字母组合
电话号码的字母组合
电话号码的组合就是输入字符对应字符串的全排列,全排列的问题应该用回溯解决。
遍历输入的字符串;
找出对应字符映射的字母;
遍历映射字母;
添加到tmp string中,进行递归;
tmp string 剪枝。
class Solution {
public:
unordered_map<char, string> mp = {
{'2', {"abc"}},
{'3', {"def"}},
{'4', {"ghi"}},
{'5', {"jkl"}},
{'6', {"mno"}},
{'7', {"pqrs"}},
{'8', {"tuv"}},
{'9', {"wxyz"}}
};
vector<string> ans;
vector<string> letterCombinations(string digits) {
if (digits.empty()) return ans;
string tmp;
dfs(digits, 0, tmp);
return ans;
}
void dfs(string digits, int index, string& tmp) {
if (index == digits.length()) {
ans.push_back(tmp);
} else {
//遍历输入的数字
char digit = digits[index];
//找到数字对应的string
string letters = mp[digit];
//遍历数字对应的char
for (char c: letters) {
//添加到string
tmp.push_back(c);
//递归
dfs(digits, index + 1, tmp);
tmp.pop_back();
}
}
}
};