1.513. 找树左下角的值 - 力扣(LeetCode)
请找出该二叉树的 最底层最左边节点的值。(!!!记得读题!!!)
思路1:迭代法,找到是最左下角的值,也即是最后一层的第一个元素的值。
int findBottomLeftValue(TreeNode *root)
{
queue<TreeNode *> q;
if (root != nullptr)
q.push(root);
int res = 0;
while (!q.empty())
{
int size = q.size();
for (int i = 0; i < size; i++)
{
TreeNode *node = q.front();
q.pop();
if (i == 0)
res = node->val;
if (node->left)
q.push(node->left);
if (node->right)
q.push(node->right);
}
}
return res;
}
思路2:递归法,首先设定一个阈值max_depth。边界条件是:达到叶子结点并且深度最大。注意首先遍历root->left,所以首先到达左下。即前序遍历
int result;
int max_depth = INT_MIN;
void traversal(TreeNode *root, int depth)
{
if (root->left == nullptr && root->right == nullptr)
{
if (depth > max_depth)
{
max_depth = depth;
result = root->val;
}
}
if (root->left)
traversal(root->left, depth + 1);
if (root->right)
traversal(root->right, depth + 1);
}
int findBottomLeftValue(TreeNode *root)
{
if (root == nullptr)
return 0;
traversal(root, 0);
return result;
}
2.112. 路径总和 - 力扣(LeetCode)
本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回,
思路1:前序遍历+递归法
缺点:使用了全局变量,coding的时候就在想如何判断true之后,后面就算遍历到false的话,也要是true。所以设置了个全局变量。这段代码有很多不足,比如剪枝、比如全局变量等
bool flag = false;
bool getsum(TreeNode *root, int targetSum, int &res)
{
if (flag){
return true;
}
if (root != nullptr)
res += root->val;
if (root->left == nullptr && root->right == nullptr)
{ // 叶子节点
if (res == targetSum)
{
flag = true;
return true;
}
return false;
}
if (root->left)
{
if (getsum(root->left, targetSum, res))
{
return true;
}
res -= root->left->val;
}
if (root->right)
{
if (getsum(root->right, targetSum, res))
{
return true;
}
res -= root->right->val;
}
return false;
}
bool hasPathSum(TreeNode *root, int targetSum)
{
if (root == nullptr)
return false;
int res = 0;
return getsum(root, targetSum, res);
}
思路2:也就是让代码更加简洁,原代码要判断res与targetsum的大小,所以我们可以减少一个变量的引入,即二者直接做差。
bool getsum(TreeNode *root, int count)
{
if (root->left == nullptr && root->right == nullptr && count == 0) // 遇到叶子节点且count==0
return true;
if (root->left == nullptr && root->right == nullptr)
return false; // 遇到叶子节点但count不为0
if (root->left)
{
count -= root->left->val;
if (getsum(root->left, count))
return true;
count += root->left->val; // 回溯
}
if (root->right)
{
count -= root->right->val;
if (getsum(root->right, count))
return true;
count += root->right->val; // 回溯
}
return false;
}
bool hasPathSum(TreeNode *root, int targetSum)
{
if (root == nullptr)
return false;
return getsum(root, targetSum - root->val);
}
思路3:在上述基础之上接着化简
注意⚠️,这里是值传递,不是引用传递,同时引用不能绑定临时对象。
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
if (!root->left && !root->right && sum == root->val) {
return true;
}
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
要遍历整个树,找到所有路径,所以递归函数不要返回值!这道题有上道题基础之后,就容易多了。回溯&&递归不分家,有回溯就有递归。
vector<vector<int>> res; // 结果
vector<int> path; // 路径
void dfs(TreeNode *root, int sum)
{
if (root == nullptr)
return;
if (root->left == nullptr && root->right == nullptr && sum == 0)
{
res.push_back(path); // 叶子结点,且和为sum
return;
}
if (!root->left && !root->right)
return; // 叶子结点,且和不为sum
if (root->left)
{
path.push_back(root->left->val);
sum -= root->left->val;
dfs(root->left, sum);
sum += root->left->val;
path.pop_back();
}
if (root->right)
{
path.push_back(root->right->val);
sum -= root->right->val;
dfs(root->right, sum);
sum += root->right->val;
path.pop_back();
}
return;
}
vector<vector<int>> pathSum(TreeNode *root, int sum)
{
if (root == nullptr)
return res;
path.push_back(root->val);
dfs(root, sum - root->val);
return res;
}
3.106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
-
如果数组大小为零的话,说明是空节点了。
-
如果不为空,那么取后序数组最后一个元素作为节点元素。
-
找到后序数组最后一个元素在中序数组的位置,作为切割点
-
切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
切割后序数组,切成后序左数组和后序右数组
-
递归处理左区间和右区间
在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭右闭,必然乱套!好难....没写出来...
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
//第一步,如果数组大小为0,说明是空节点了
if(postorder.size()==0) return nullptr;
//第二步,如果不为空,后序最后一个元素为根节点(中间节点)
int rootValue = postorder[postorder.size() - 1];
TreeNode *root = new TreeNode(rootValue);
//叶子节点
if(postorder.size()==1) return root;
// 第三步,寻找切割点
int index;
for (index = 0; index < inorder.size();index++){
if(inorder[index]==rootValue) break;
}
// 第四步,切割中序数组,得到中序左数组和中序右数组
vector<int>leftInorder(inorder.begin(),inorder.begin()+index);//截取元素,左闭右开
vector<int> rightInorder(inorder.begin() + index + 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 = buildTree(leftInorder, leftPostorder);
root->right = buildTree(rightInorder, rightPostorder);
return root;
}
3.105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
思路与上面的题类似,不再赘述~上面的不会,下面的就ac了哈哈哈😂
TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder)
{
// 第一步
if (inorder.size() == 0)
return nullptr;
// 第二部,找根节点
int rootValue = preorder[0];
TreeNode *root = new TreeNode(rootValue);
// 叶子节点
if (inorder.size() == 1)
return root;
// 第三步,寻找割点
int ind;
for (ind = 0; ind < inorder.size(); ind++)
{
if (inorder[ind] == rootValue)
break;
}
// 第四步,切割中序数组,左闭右开
// 为什么要inorder.begin()+ind+1 因为是左闭右开.如9,13,15,20,7.要切割成[9],[15,20,7],中间的13为要剔除。
vector<int> leftInorder(inorder.begin(), inorder.begin() + ind);
vector<int> rightInorder(inorder.begin() + ind + 1, inorder.end());
// 第五步,切割前序数组,左闭右开
preorder.erase(preorder.begin());
vector<int> leftPreorder(preorder.begin(), preorder.begin() + leftInorder.size());
vector<int> rightPreorder(preorder.begin() + leftInorder.size(), preorder.end());
// 第六步,递归
root->left = buildTree(leftPreorder, leftInorder);
root->right = buildTree(rightPreorder, rightInorder);
return root;
}
⚠️:遇到这种题目的时候,也要学会打日志来调试(如何打日志有时候也是个技术活),不要脑动模拟,脑动模拟很容易越想越乱。