代码随想录第16天|leetcode513.找树左下角的值、leetcodee112.路径总和、leetocde106.从中序与后序遍历序列构造二叉树

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

2.113. 路径总和 II - 力扣(LeetCode)

要遍历整个树,找到所有路径,所以递归函数不要返回值!这道题有上道题基础之后,就容易多了。回溯&&递归不分家,有回溯就有递归。

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

⚠️:遇到这种题目的时候,也要学会打日志来调试(如何打日志有时候也是个技术活),不要脑动模拟,脑动模拟很容易越想越乱

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值