代码随想录算法训练营第18天|● 513.找树左下角的值● 112. 路径总和 113.路径总和ii● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

513.找树左下角的值

思路(递归):题意是找树的最后一行,最左边的值,所以不一定该节点是左孩子,也有可能是右孩子。本题不需要中的处理过程,所以前中后序遍历都可以,因为只需要左右的遍历顺序即可。用一个全局变量MaxDepth记录最大的深度,depth记录当前遍历的层数(所以需要回溯过程)。因为最先递归遍历左边,所以如果当第一次最大深度出现节点,则result一定记录的是最左值,再之后遍历如果最大深度改变则记录替换之前的值。

代码:

int MaxDepth = INT_MIN;//记录树的最大深度
    int result;//存放最终节点的值
    void traversal(TreeNode* node,int depth){//depth记录当前遍历到的深度
        if(node->left==nullptr && node->right==nullptr){//终止条件
            if(MaxDepth < depth){
                MaxDepth = depth;
                result = node->val;
            }
            return;//如果当前不是最大深度但又是叶节点则会返回上一个节点
        }
        if(node->left){//左
            depth++;
            traversal(node->left,depth);
            depth--;//回溯深度
        }
        if(node->right){//右
            depth++;
            traversal(node->right,depth);
            depth--;
        }
        return;
    }
    int findBottomLeftValue(TreeNode* root) {
        traversal(root,0);
        return result;
    }

思路(迭代法):本题用层序遍历是最简单的方法,只需要每次记录每一行的第一个节点值即可.

代码:

int findBottomLeftValue(TreeNode* root) {
        queue 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();//该行最左的第一个节点
                if(i == 0) result = node->val;//i=0,则这一行刚开头,记录下结果
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
                que.pop();
            }
        }
        return result;
    }

112.路径总和

思路(递归法):首先遍历顺序由于没有需要中节点的处理逻辑,所以中为空,遍历顺序左右都可以。

正常思路都是把每遍历的节点进行累加,若等于目标值且到了叶子节点则符合目标路径。这种方法需要count值设为0,但是还需要传目标值,会比较麻烦。下面的方法是count值直接为目标值,每遍历一个节点就减去该节点的目标val值。最后遍历到叶子节点且count为0则为目标路径。

注1:返回true和false的条件多,这题只需要找到其中一条满足条件的路径,所以如果找到目标路径一直往上返回true,所以需要返回值.若只是遍历的题目,不需要返回值。

注2:需要用到回溯,有递归就有回溯,这题和上题的思路相似。

代码:

bool traversal(TreeNode* node,int count){//count是目标值,每遍历一个节点减去节点值,最后为0且叶子节点则是目标路径
        //终止条件1,遍历到叶子节点且count已经减到0,则是目标路径
        if(node->left==nullptr && node->right==nullptr && count==0)
            return true;
        //终止条件2,遍历到叶子节点但count不为0,则不是目标路径
        if(node->left==nullptr && node->right==nullptr && count!=0)
            return false;
        //左右遍历顺序
        if(node->left){//左
            count-=node->left->val;
            if(traversal(node->left,count))//如果返回结果是真,则找到了目标路径,所以不需要进行下一步回溯,直接一层层返回true
                return true;
            count+=node->left->val;//若上面条件不满足,说明到了叶子节点但是不满足条件,所以要回溯
        }
        if(node->right){//右
            count-=node->right->val;
            if(traversal(node->right,count))
                return true;
            count+=node->right->val;//回溯
        }
        return false;//从根出发经历了上面的遍历,都没有返回true说明没有符合条件的路径
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root==nullptr)
            return false;
        return traversal(root,targetSum-root->val);
    }

113.路径总和ii

思路(递归法):这题思路112题和257.二叉树的所有路径结合,找符合目标值的路径112题,把路径加入结果集257题。

代码:

vector> result;//存放结果
    vector path;//存放当前路径
     void traversal(TreeNode* node,int count){
        //终止条件1,符合目标路径,把当前路径放入结果集
        if(node->left==nullptr && node->right==nullptr && count==0){
            result.push_back(path);
            return;
        }
        //终止条件2,遍历到叶节点但不符合目标路径
        if(node->left==nullptr && node->right==nullptr && count!=0)
            return;

        if(node->left){//左
            count-=node->left->val;
            path.push_back(node->left->val);
            traversal(node->left,count);
            count+=node->left->val;//回溯
            path.pop_back();//回溯
        }
        if(node->right){//右
            count-=node->right->val;
            path.push_back(node->right->val);
            traversal(node->right,count);
            count+=node->right->val;//回溯
            path.pop_back();//回溯
        }
        return;
    }
 
    vector> pathSum(TreeNode* root, int targetSum) {
        result.clear();
        path.clear();
        if(root==nullptr)
            return result;
        path.push_back(root->val);
        traversal(root,targetSum-root->val);
        return result;
    }

106.从中序与后序遍历序列构造二叉树

思路:首先要理解构造二叉树的理论知识。整体思路是确定切割点,将其中序数组和后序数组进行切割分为左右子树的集合,再递归切割构造。

以下分步骤解析:

第一步:从中序数组中确定切割点在数组的下标(位置)(后序数组末尾是中间节点也是要在中序数组中找的切割点)

第二步:利用中序切割点的位置把中序数组切割为左中序,右中序数组(不包括切割点)

第三步:把后序数组切割为左后序数组和右后序数组;ps:中序是有已知的切割点当然方便切割,但是切割点也就是中间节点在后序数组是最后一个节点,所以并不能作为后序数组切割的依据。但是从另一个方面来看,第一二步中已经明确划分了左右子树的集合了(左右中序数组)。子树集合大小无论在前中后序中都是不变的,这个可以作为后序数组切割依据。

第四步:递归进行切割构造(分别传入左子树集合包括左中序数组,左后序数组;传入右子树集合包括右中后序数组)

最后在传会其构造节点

注:这题还需要遵守递归的循环不变量原则,即切割的区间。

代码:

TreeNode* traversal(vector& inorder,vector& postorder){//inorder中序数组,postorder后序数组
        //终止条件1,因为主函数有了判断是否为空数组,这个条件是递归循环时遇到传入空数组的终止情况
        if(postorder.size() == 0) return nullptr;
        int rootValue = postorder[postorder.size() - 1];//后序数组的最后一个元素是当前的中间节点
        TreeNode* root = new TreeNode(rootValue);
        //终止条件2,情况1:第一次递归数组只有一个节点,返回当前节点;情况2:递归传入数组只有一个节点,即是叶子节点,所以下面不需要再递归,把当前叶子节点返回
        if(postorder.size() == 1) return root;

        /*第一步.从中序数组确定切割点的位置*/
        int delimiterIndex;
        for(delimiterIndex = 0;delimiterIndex < inorder.size();delimiterIndex++){
            if(inorder[delimiterIndex] == rootValue) break;
        }//遍历结束,delimiterIndex就是中序数组的切割点位置

        /*第二步.切割中序数组使其分为左中序和右中序数组(不包含中节点)*/
        //左区间,当前区间为左闭右开[0,delimiterIndex)
        vector leftInorder(inorder.begin(),inorder.begin() + delimiterIndex);
        //右区间,[delimiter+1,end)
        vector rightInorder(inorder.begin() + delimiterIndex+1,inorder.end());

        /*第三步,切割后序数组使其为左后序和右后序数组,有个注意的点,因为中间节点是在后序数组末尾,所以要舍弃*/
        //左闭右开,切割点无论左右区间的长度都和中序时一样的,所以和中序切割方法不同,用其长度进行切割
        postorder.resize(postorder.size()-1);//舍弃最后一个元素
        //左区间,[0,leftIndex)
        vector leftPostorder(postorder.begin(),postorder.begin() + leftInorder.size());
        //右区间,(leftIndex,end],后序不像中序,后序根节点已经在数组末尾并且被舍弃,所以切割是连续的
        vector rightPostorder(postorder.begin() + leftInorder.size(),postorder.end());

        /*第四步,把左子树集,右子树集进行递归切割*/
        root->left = traversal(leftInorder,leftPostorder);//传入左中序,左后序集合
        root->right = traversal(rightInorder,rightPostorder);//传入右中序,右后序

        return root;
    }
    TreeNode* buildTree(vector& inorder, vector& postorder) {
        if(inorder.size()==0 || postorder.size()==0)    return nullptr;
        return traversal(inorder,postorder);
    }

105.从前序与中序遍历序列构造二叉树

思路:和106思路是一样的,唯一区别是要知道前序的根节点是在数组第一个位置。所以代码在切割部分注意区分即可。

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