代码随想录算法训练营day18

题目:513.找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树

参考链接:代码随想录

513.找树左下角的值

思路:这题首先想到层序遍历,直接记录每一层第一个值,最后即为答案。

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());
    }
};

你可能感兴趣的:(算法,数据结构)