代码随想录C++算法训练,二叉树(day16)
找树左下角的值
题目链接:513. 找树左下角的值 - 力扣(LeetCode)
题目描述:
给定一个二叉树的 根节点 root
,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例 1:
输入: root = [2,1,3] 输出: 1
示例 2:
输入: [1,2,3,4,null,5,6,null,null,7] 输出: 7
题解:
/**
* 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:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if(root != nullptr) que.push(root);
int result = 0;
while(!que.empty()){
int size = que.size();
for(int i = 0; i < size; i++){
TreeNode* node = que.front();
que.pop();
//记录最后一行的第一个元素
if(i == 0) result = node->val;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return result;
}
};
总结与思考:
- 此题我们使用迭代层序遍历来解决。依旧使用层序遍历的模板来解题
- 在遍历过程中只需要记录最后一行第一个节点的数值就可以了。
路径总和
题目链接:112. 路径总和 - 力扣(LeetCode)
题目描述:
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22 输出:true 解释:等于目标和的根节点到叶节点路径如上图所示。
示例 2:
输入:root = [1,2,3], targetSum = 5 输出:false 解释:树中存在两条根节点到叶子节点的路径: (1 --> 2): 和为 3 (1 --> 3): 和为 4 不存在 sum = 5 的根节点到叶子节点的路径。
示例 3:
输入:root = [], targetSum = 0 输出:false 解释:由于树是空的,所以不存在根节点到叶子节点的路径。
题解:
/**
* 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:
bool traversal(TreeNode* cur, int count){
//当遍历到叶子并且count == 0 返回True
if(!cur->left && !cur->right && count == 0) return true;
//遍历到叶子节点则返回
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;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == nullptr) return false;
return traversal(root, targetSum - root->val);
}
};
总结与思考:
- 根据题意我们此题使用深度优先遍历的方式,递归回溯来解决此题
- 确定递归三要素:
- 确定递归函数的参数和返回值:我们需要二叉树的根节点,并且需要一个计数器,计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。同时因为递归的路线不需要整棵树,而且递归函数需要返回值,因此我们将返回值类型设置为bool型
- 递归函数的结束条件:用递减的方式记录,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。如果遍历到了叶子节点,count不为0,就是没找到。
- 单层递归时的逻辑处理:因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归。又因为递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。
从中序与后序遍历序列构建二叉树
题目链接:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
题目描述:
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3] 输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1] 输出:[-1]
题解:
/**
* 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 {
private:
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
//1.后序数组为空时,返回空节点
if(postorder.size() == 0) return nullptr;
//2.寻找后序遍历数组的最后一个元素,就是当前的中间节点,令其成为节点元素
int rootValue = postorder[postorder.size() - 1];
//创建子树的根节点
TreeNode* root = new TreeNode(rootValue);
//找到叶子节点后,直接返回,防止性能浪费
if(postorder.size() == 1) return root;
//3.找后序数组最后一个元素在中序数组中的位置作为切割点
//定义分界指针delimiterindex
int delimiterIndex = 0;
for(delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++){
if(inorder[delimiterIndex] == rootValue) break;
}
//4.切割中序数组,令其成为中序左数组和中序右数组
//统一左闭右开
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());
//5.切割后序数组,令其成为后序左数组和后序右数组
//统一左闭右开
//调整后序数组,去除了后序数组最后一个元素(作为中间节点,已经使用过了)
postorder.resize(postorder.size() - 1);
//使用中序左数组的大小作为切割后序数组的切割点
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
//6.递归处理左区间和右区间,递归建立左右子树
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
//返回当前的根节点
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size() == 0 || postorder.size() == 0) return nullptr;
return traversal(inorder, postorder);
}
};
总结与思考:
- 解决此题,我们需要回忆根据中序遍历以及后序遍历数组的结果来构建二叉树的过程,此题需要一层一层切割,因此使用递归的方法做题解
- 以后序数组的最后一个元素作为切割点,先切割中序数组,然后根据切割后的中序数组切割后序数组,一层一层的切下去,每次后序数组的最后一个元素就是节点元素
- 分治法处理递归过程:
- 判断数组的大小,特殊情况,数组大小为零时,说明此树为空树,则返回空节点
- 不为空时,取后序遍历数组的最后一个元素作为节点元素
- 找到后序数组中最后一个元素在中序数组中的位置,作为切割点开始切割
- 开始切割中序数组,将其切割成中序左数组和中序右数组(统一左闭右开)
- 随后继续切割后序数组,将其切割成后序左数组和后序右数组(根据中序左数组的大小作为合理切割的条件)(统一左闭右开)
- 递归处理左区间和右区间
- 此题逻辑的实现难度并不大,但需要思考的细节和重点较多,需要清晰每个功能实现的逻辑