Leetcode 513.找树左下角的值
题目链接:513 找树左下角的值
题干:给定一个二叉树的根节点
root
,请找出该二叉树的最底层最左边节点的值。假设二叉树中至少有一个节点。
思考一:递归法。关键点:不要误以为一直向左遍历的就是最底层的节点,最底层的叶子节点有可能在右子树中。考虑到要求节点的深度,采用前序遍历,这样首个遍历的最深的叶子节点就是目标节点。终止条件:空节点直接返回,出现比当前记录更深的叶子节点替换结果value值。单层循环逻辑:处理左右子树,深度加一。
代码:
class Solution {
public:
int maxDepth;
void traversal(TreeNode* node, int depth, int& value) {
if (!node) return;
if (!node->left && !node->right && depth > maxDepth) { //叶子节点层次判断 中
value = node->val;
maxDepth = depth;
}
traversal(node->left, depth + 1, value); //左
traversal(node->right, depth + 1, value); //右
}
int findBottomLeftValue(TreeNode* root) {
maxDepth = 0;
int value;
traversal(root, 1, value);
return value;
}
};
思考二:迭代法。采用层序遍历,每遍历一层将第一个节点的值赋值给结果result
代码:
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
que.push(root);
int result;
while (!que.empty()) {
int size = que.size();
TreeNode* cur = que.front();
result = cur->val;
for (int i = 0; i < size; i++) {
cur = que.front();
que.pop();
if (cur->left) que.push(cur->left); //左
if (cur->right) que.push(cur->right); //右
}
}
return result;
}
};
Leetcode 112. 路径总和
题目链接:112 路径总和
题干:给你二叉树的根节点
root
和一个表示目标和的整数targetSum
。判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和targetSum
。如果存在,返回true
;否则,返回false
。
- 叶子节点:是指没有子节点的节点。
思考一:递归法。终止条件:遍历到空节点则说明此条路径不对,遍历到叶子节点进行一次判断。
单层递归逻辑:处理左右子树,将两结果进行或运算后返回。
代码:
class Solution {
public:
bool traversal(TreeNode* node, int sum, int targetSum) {
if (!node) return false;
sum += node->val;
if (!node->left && !node->right && sum == targetSum) //找到路径
return true;
bool left = traversal(node->left, sum, targetSum); //左
bool right = traversal(node->right, sum, targetSum); //右
return left || right; //中
}
bool hasPathSum(TreeNode* root, int targetSum) {
int sum = 0;
return traversal(root, sum ,targetSum);
}
};
思考二:递归法。参数count:当前路径之和与目标值的差值。终止条件:遇到叶子节点判断count是否为0表示是否查询到路径。单层递归逻辑:将非空孩子节点进行处理,参数count先减去孩子节点的value值,回溯时加上value值。
代码:
class Solution {
private:
bool traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) return true; //遇到叶子节点,并且计数为0
if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回
if (cur->left) { // 左
count -= cur->left->val; // 递归,处理节点;
if (traversal(cur->left, count)) return true;
count += cur->left->val; // 回溯,撤销处理结果
}
if (cur->right) { // 右
count -= cur->right->val; // 递归,处理节点;
if (traversal(cur->right, count)) return true;
count += cur->right->val; // 回溯,撤销处理结果
}
return false;
}
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL) return false;
return traversal(root, sum - root->val);
}
};
思考三:后续遍历的迭代写法。唯一的区别:栈中存储元素类型改为键值对,不仅存储节点还要存储根节点到记录节点的路径之和。出口:遍历到叶节点判断记录的路径之和是否满足目标值。
代码:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (!root) return false;
stack<pair<TreeNode*,int>> st;
st.push(pair<TreeNode*,int>(root, root->val));
while (!st.empty()) {
TreeNode* cur = st.top().first;
int count = st.top().second;
st.pop();
//当前节点为叶子节点且路径总和长度为sum则查询成功 中
if (!cur->left && !cur->right && count == targetSum)
return true;
if (cur->right) //右
st.push(pair<TreeNode*,int>(cur->right, count + cur->right->val));
if (cur->left) //左
st.push(pair<TreeNode*,int>(cur->left, count + cur->left->val));
}
return false;
}
};
Leetcode 113.路径总和ii
题目链接:113 路径总和ii
题干:给你二叉树的根节点
root
和一个整数目标和targetSum
,找出所有从根节点到叶子节点路径总和等于给定目标和的路径。
- 叶子节点:是指没有子节点的节点
思考:递归法。和上题思考二的区别:函数参数多个路径path;单层递归逻辑:处理非空左右孩子节点时路径要更新。当然要记得主函数调用前传的参数path要加上根节点val值,参数count要用目标值减去根节点val值。
代码:
class Solution {
public:
vector<vector<int>> result;
void traversal(TreeNode* node, int count, vector<int> path) {
if (!node->left && !node->right && count == 0) { //叶子节点路径之和满足
result.push_back(path);
return;
}
if (!node->left && !node->right) return; //叶节点路径之和不满足
if (node->left) { //左
count -= node->left->val;
path.push_back(node->left->val);
traversal(node->left, count, path);
count += node->left->val;
path.pop_back();
}
if (node->right) { //右
count -= node->right->val;
path.push_back(node->right->val);
traversal(node->right, count, path);
count += node->right->val;
path.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
if (!root) return result;
vector<int> path;
path.push_back(root->val);
traversal(root, targetSum - root->val, path);
return result;
}
};
Leetcode 106.从中序与后序遍历序列构造二叉树
题目链接:106 从中序与后序遍历序列构造二叉树
题干:给定两个整数数组
inorder
和postorder
,其中inorder
是二叉树的中序遍历,postorder
是同一棵树的后序遍历,请你构造并返回这颗二叉树 。
思考:理论知识:以 后序遍历数组的最后一个元素为切割点,先切中序遍历数组,根据中序遍历数组,反过来再切后序遍历数组。一层一层切下去,每次后序遍历数组最后一个元素就是节点元素。
递归处理逻辑:
-
第一步:如果数组大小为零的话,说明是空节点。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组(按切割点切割)
-
第五步:切割后序数组,切成后序左数组和后序右数组(按切割后的中序数组个数切割;理由:中序数组大小一定是和后序数组的大小相同的)
-
第六步:递归处理左区间和右区间
代码:
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0) return nullptr;
//后序数组最后一个元素就是当前的中间节点
int rootValue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorder.size() == 1) return root; //叶子节点
int pivotpos; //在中序数组中寻找切割点
for (pivotpos = 0; pivotpos < inorder.size(); pivotpos++)
if (inorder[pivotpos] == rootValue) break;
//按切割点 切割中序数组 左闭右开
vector<int> leftInorder(inorder.begin(), inorder.begin() + pivotpos);
vector<int> rightInorder(inorder.begin() + pivotpos + 1, inorder.end());
postorder.resize(postorder.size() - 1); //后序数组去除最后一个元素
//按切割后的中序数组个数 切割后序数组 左闭右开
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0 || inorder.size() == 0) return nullptr;
return traversal(inorder, postorder);
}
};
Leetcode 105.从前序与中序遍历序列构造二叉树
题目链接:105 从前序与中序遍历序列构造二叉树
题干:给定两个整数数组
preorder
和inorder
,其中preorder
是二叉树的先序遍历,inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
思考:和上题区别:前序数组的第一个元素就是根节点,作为切割点。
代码:
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, vector<int>& preorder) {
if (preorder.size() == 0) return nullptr;
//前序数组第一个元素就是当前的中间节点
int rootValue = preorder[0];
TreeNode* root = new TreeNode(rootValue);
if (preorder.size() == 1) return root; //叶子节点
int pivotpos; //在中序数组中寻找切割点
for (pivotpos = 0; pivotpos < inorder.size(); pivotpos++)
if (inorder[pivotpos] == rootValue) break;
//按切割点 切割中序数组 左闭右开
vector<int> leftInorder(inorder.begin(), inorder.begin() + pivotpos);
vector<int> rightInorder(inorder.begin() + pivotpos + 1, inorder.end());
//按切割后的中序数组个数 切割前序数组 左闭右开
vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + leftInorder.size() + 1);
vector<int> rightPreorder(preorder.begin() + leftInorder.size() + 1, preorder.end());
root->left = traversal(leftInorder, leftPreorder);
root->right = traversal(rightInorder, rightPreorder);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() == 0 || inorder.size() == 0) return nullptr;
return traversal(inorder, preorder);
}
};
自我总结:
- 了解切割二叉树的方式以及传参方式
- 知识盲点:中序数组大小一定是和后序数组的大小相同的