算法学习记录| 2023.X.XX| 章节DayX| 题目号.题目标题 & 题目号.题目标题
前言
404.左叶子之和
题目链接
思路1:层序遍历
正常层序遍历,遍历时如果左子节点为叶子节点则是左叶子,存储即可
代码
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
queue<TreeNode*> que;
int sum = 0;
if ( root == NULL)
return sum;
que.push(root);
while (!que.empty()){
int size = que.size();
for(int i = 0; i < size; i++){
TreeNode* node = que.front();
que.pop();
if (node -> left)
que.push(node -> left);
if (node -> right)
que.push(node -> right);
//左叶子节点
if (node -> left && node -> left ->left == NULL && node -> left -> right == NULL)
sum += node ->left -> val;
}
}
return sum;
}
};
思路2:递归
- 确定递归函数的参数和返回值
根节点与数值之和,所以是树节点和int - 确定终止条件
如果遍历到空节点,那么左叶子值一定是0。
此外,只有当前遍历的节点是父节点,才能判断其子节点是不是左叶子。 所以如果当前遍历的节点是叶子节点,那其左叶子也必定是0,那么终止条件为:
if (root == NULL) return 0;
//其实这个也可以不写,如果不写不影响结果,但就会让递归多进行了一层。
if (root->left == NULL && root->right== NULL) return 0;
- 确定单层递归的逻辑
当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和
代码
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (root == NULL) return 0;
if (root->left == NULL && root->right== NULL) return 0;
int leftValue = sumOfLeftLeaves(root->left); // 左
if (root->left && !root->left->left && !root->left->right) { // 左子树就是一个左叶子的情况
leftValue = root->left->val;
}
int rightValue = sumOfLeftLeaves(root->right); // 右
int sum = leftValue + rightValue; // 中
return sum;
}
};
总结
不能判断当前节点是否为叶子节点时,可以考虑利用他的父节点进行判断。
之前都是习惯了通过节点的左右孩子判断本节点的属性,此题打开了一定思路。
513.找树左下角的值
题目链接
思路:层序遍历
看到最后一层以及最左,直觉反应就是通过层序遍历,设一个vector存放每一层最左边的值,这样最后只需要返回最后的元素就是题目要求的数了
代码
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
que.push(root);
int result;
while(!que.empty()){
result= que.front() -> val; //只需要记录每一层第一个数即可
int size = que.size();
for (int i = 0; i < size; i++){
TreeNode* node = que.front();
que.pop();
if (node -> left)
que.push(node -> left);
if (node -> right)
que.push(node -> right);
}
}
return result; //每一层都记录且不断覆盖,则最后一个数就是最后一层的最左节点值
}
总结
下次可以再研究下递归解法
112. 路径总和
题目链接
思路1:递归
同样是需要回溯的题目
- 确定递归函数的参数和返回类型
参数:需要二叉树的根节点、一个记录到自己之前的路径和的计数器和目标总和
返回值:因为需要找到一条符合条件的路径,不需要遍历整棵树,因此递归函数需要返回值来及时返回,bool型
bool traversal(TreeNode* cur, int count, int sum)
- 确定终止条件
遍历到叶子节点时如果路径和等于目标值则true,不等于则false
if (!cur -> left && !cur -> right && count == sum) //遍历到叶子节点且总和为目标值
return true;
if (!cur -> left && !cur -> right && count != sum) //遍历到叶子节点但总和不等于目标值
return false;
- 确定单层递归的逻辑
因为count只记录当前节点之前路径的和,所以应该首先将当前节点的值加进去。
只对非空节点进行递归。
由于递归函数是有返回值的,如果递归函数返回true,说明找到了符合要求的路径,应该立刻返回
count += cur -> val; //每遍历到一个新的节点,就用之前节点的总和count加上现在节点的数值
if (cur -> left)
if (traversal(cur -> left, count, sum))
return true;
if (cur -> right)
if (traversal(cur -> right, count, sum))
return true;
代码
class Solution {
public:
bool traversal(TreeNode* cur, int count, int sum){
count += cur -> val; //每遍历到一个新的节点,就用之前节点的总和count加上现在节点的数值
if (!cur -> left && !cur -> right && count == sum) //遍历到叶子节点且总和为目标值
return true;
if (!cur -> left && !cur -> right && count != sum) //遍历到叶子节点但总和不等于目标值
return false;
if (cur -> left)
if (traversal(cur -> left, count, sum))
return true;
if (cur -> right)
if (traversal(cur -> right, count, sum))
return true;
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL)
return false;
int count = 0; //记录根节点之前的总和,因为没有所以是0
return traversal(root, count, targetSum);
}
};
思路2:迭代(栈)
如果使用栈来模拟递归,怎么做回溯呢?
此时栈里的一个元素不仅要记录该节点的指针,还需要记录到当前节点的路径总和
因此使用pair结构
pair<TreeNode*, int>
pair<节点指针,路径和>
代码:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if ( root == NULL )
return false;
stack<pair<TreeNode*, int>> st; //保存节点与到该节点(包含)的路径总和
st.push(pair<TreeNode*, int>(root, root -> val));
while (!st.empty()){
pair<TreeNode*, int> node = st.top();
st.pop();
//叶子结点且总和为sum时符合于要求
if (!node.first -> left && !node.first -> right && targetSum == node.second)
return true;
if (node.first -> left)
st.push(pair<TreeNode*, int>(node.first -> left, node.second + node.first -> left -> val));
if (node.first -> right)
st.push(pair<TreeNode*, int>(node.first -> right, node.second + node.first -> right -> val));
}
return false;
}
};
总结
113. 路径总和ii
题目链接
思路:递归
其实相当于 112. 路径总和 和 257. 二叉树的所有路径 两道题目的混合体。
主要注意在递归的时候想清楚如何回溯
代码
class Solution {
public:
vector<vector<int>> result; //全局变量
vector<int> path;
void recPath(TreeNode* cur, int count, int targetSum){ //count是该节点前所有路径总和
count += cur -> val;
path.push_back(cur -> val);
if (!cur -> left && !cur -> right && count == targetSum){
vector<int> tempPath;
for(int i = 0; i < path.size(); i++){
tempPath.push_back(path[i]);
}
result.push_back(tempPath);
}
if (cur -> left){
recPath(cur -> left, count, targetSum);
path.pop_back(); //回溯
}
if (cur -> right){
recPath(cur -> right, count, targetSum);
path.pop_back(); //回溯
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
if (root == NULL)
return result;
recPath(root, 0, targetSum);
return result;
}
};