参考链接:代码随想录
思路:这题首先想到层序遍历,直接记录每一层第一个值,最后即为答案。
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> q;
int ans;
if(root){
q.push(root);
}
while(!q.empty()){
int size=q.size();
for(int i=0;i<size;i++){
TreeNode* node=q.front();
q.pop();
if(i==0){
ans=node->val;//第一个
}
if(node->left){
q.push(node->left);
}
if(node->right){
q.push(node->right);
}
}
}
return ans;
}
};
然后是递归方法,难点是达到最后一层。判断方法即为计算深度,深度最大即为最后一层,然后对最左边,使用前序遍历即可,第一个就是最左边。对于计算深度的写法和最大深度那题的前序遍历有点区别,需要判断depth>maxDepth,而不能等于,这样才确保了是深度最大的第一个节点。
class Solution {
public:
int maxDepth=0;//这里和求最大深度那题的前序遍历是一样的
int ans;
void getDepth(TreeNode* root,int depth){
if(depth>maxDepth&&!root->left&&!root->right){//到达最大深度,第一个节点就是
//注意这里判断必须是depth大于之前最大深度,这样才确保第一个
maxDepth=depth;
ans=root->val;
}
if(root->left){
getDepth(root->left,depth+1);
}
if(root->right){
getDepth(root->right,depth+1);
}
return;
}
int findBottomLeftValue(TreeNode* root) {
if(!root){
return 0;
}
getDepth(root,1);//这里必须为1
return ans;
}
};
112.路径总和
113.路径总和ii
思路:本题和求所有路径那题类似,需要采用回溯法,当遇到叶子节点时,判断sum是否为targetSum,如果是则返回true。采用前序遍历。
class Solution {
public:
bool ans=false;
void traversal(TreeNode* root,int sum,int targetSum){
sum+=root->val;
if(!root->left&&!root->right){//到达叶子
if(sum==targetSum){
ans=true;
}
return;
}
if(root->left){
traversal(root->left,sum,targetSum);//不用写--,因为处理完还是之前的sum
}
if(root->right){
traversal(root->right,sum,targetSum);
}
return;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root){
return false;
}
traversal(root,0,targetSum);
return ans;
}
};
对于解析提到的递归函数的返回值问题,我认为对所有二叉树题目,都可以将主体写成一个void traversal
,然后使用全局变量或者引用传递需要计算的result,最后再调用这个函数,这样就不用考虑返回值问题,如果使用返回值更简单,也很容易修改简化代码。
看完标答,可以不需要累加后再判断,直接在递归过程中减少targetSum值,这样比较简单。
标答:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root){
return false;
}
if(!root->left&&!root->right&&root->val==targetSum){//到达叶子
return true;
}
bool leftBool=false;
bool rightBool=false;
if(root->left){
leftBool=hasPathSum(root->left,targetSum-root->val);//减去根节点的值
}
if(root->right){
rightBool=hasPathSum(root->right,targetSum-root->val);
}
return leftBool||rightBool;
}
};
路径总合ii也是和求所有路径那题类似:
class Solution {
public:
void traverse(TreeNode* root,vector<int> path,vector<vector<int>>& ans,int targetSum){
if(!root){
return;
}
path.push_back(root->val);
if(!root->left&&!root->right&&targetSum==root->val){
ans.push_back(path);
return;
}
if(root->left){
traverse(root->left,path,ans,targetSum-root->val);
}
if(root->right){
traverse(root->right,path,ans,targetSum-root->val);
}
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>> ans;
vector<int> path;
traverse(root,path,ans,targetSum);
return ans;
}
};
106.从中序与后序遍历序列构造二叉树
105.从前序与中序遍历序列构造二叉树
思路:本题首先要搞清楚过程,首先是返回条件,数组为空返回空指针,递归结束。然后是根据后序数组的末尾创建根节点,并对中序数组分割成前后数组,然后根据前后中序数组的长度将后序数组去尾后分割成前后数组,然后对前数组和后数组分部递归求节点,并于根节点左右相连。注意左闭右开。
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(postorder.size()==0){//切割成的空数组直接返回
return nullptr;
}
int rootValue=postorder.back();
TreeNode* root=new TreeNode(rootValue);
int midIndex;//切割点
for(midIndex=0;midIndex<inorder.size();midIndex++){
if(inorder[midIndex]==rootValue){
break;
}
}
vector<int> leftInorder(inorder.begin(),inorder.begin()+midIndex);//左闭右开
vector<int> rightInorder(inorder.begin()+midIndex+1,inorder.end());
//切割后序数组,直接按照大小切割
vector<int> leftPostorder(postorder.begin(),postorder.begin()+midIndex);
vector<int> rightPostorder(postorder.begin()+midIndex,postorder.end()-1);
root->left=buildTree(leftInorder,leftPostorder);
root->right=buildTree(rightInorder,rightPostorder);
return root;
}
};
使用前序和中序的方法类似:
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty()){
return nullptr;
}
int rootVal=preorder.front();
TreeNode* root=new TreeNode(rootVal);
int midIndex;
for(midIndex=0;midIndex<inorder.size();midIndex++){
if(inorder[midIndex]==rootVal){
break;
}
}
vector<int> leftInorder(inorder.begin(),inorder.begin()+midIndex);
vector<int> rightInorder(inorder.begin()+midIndex+1,inorder.end());
vector<int> leftPreorder(preorder.begin()+1,preorder.begin()+1+midIndex);
vector<int> rightPreorder(preorder.begin()+1+midIndex,preorder.end());
root->left=buildTree(leftPreorder,leftInorder);
root->right=buildTree(rightPreorder,rightInorder);
return root;
}
};
看完解析,发现还可以不用每次建立新的vector,而直接使用之前的原nums,参数传递切割后数组开始结尾位置,可以节约空间。
标答:
class Solution {
private:
// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
if (postorderBegin == postorderEnd) return NULL;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割后序数组
// 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
// 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
// 左闭右开的原则
return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};