• [英雄星球六月集训LeetCode解题日报] 第29日 分治


    日报

    题目

    一、 654. 最大二叉树

    链接: 654. 最大二叉树

    1. 题目描述

    在这里插入图片描述

    2. 思路分析

    按照题意模拟即可。

    3. 代码实现

    class Solution:
        def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
            n = len(nums)
            def dfs(i,l,r):
                root = TreeNode(nums[i])
                m = -1
                mi = -1
                for j in range(l,i):
                    if nums[j]>m:
                        m = nums[j]
                        mi = j   
                if i > l:                         
                    root.left = dfs(mi,l,i-1)
                if i<r:
                    m = -1
                    for j in range(i+1,r+1):
                        if nums[j]>m:
                            m = nums[j]
                            mi = j                            
                    root.right = dfs(mi,i+1,r)
                return root
    
            m = -1
            mi = -1
            for j in range(0,n):
                if nums[j]>m:
                    m = nums[j]
                    mi = j  
            return dfs(mi,0,n-1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    二、889. 根据前序和后序遍历构造二叉树

    链接: 889. 根据前序和后序遍历构造二叉树

    1. 题目描述

    在这里插入图片描述

    2. 思路分析

    • 我们处理一棵树的时候将这颗树分为三部分:根节点、左子树,右子树。
    • 对preoder来说,构成为[根、左子树、右子树];对postorder来说,构成为[左子树、右子树、根]。
    • 显然,这三部分在preorder和postorder里都是连续的,则有:
    • 一颗树的根节点显然是preorder[0]、postorder[-1];
    • 接下来找左右子树,左子树的根节点显然是preorder[1],然后在postorder里找到这个节点的下标i。
    • 上边我们提到左子树在postorder里是连续的,且是第一部分,那么左子树长度width就是i+1。
    • 显然剩下一部分就是右子树。
    • 创建根节点,然后dfs分别构建左右子树即可。

    3. 代码实现

    class Solution:
        def constructFromPrePost(self, preorder: List[int], postorder: List[int]) -> TreeNode:
            
            def dfs(preorder,postorder):
                if not preorder:
                    return None
                root = TreeNode(preorder[0])
                n = len(preorder)
                if n == 1:
                    return root
    
                width = postorder.index(preorder[1])+1   # 左子树长度
                root.left = dfs(preorder[1:1+width], postorder[:width])
                root.right = dfs(preorder[width+1:], postorder[width:n-1])
                return root
    
            return dfs(preorder,postorder)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    三、 1569. 将子数组重新排序得到同一个二叉查找树的方案数

    链接: 1569. 将子数组重新排序得到同一个二叉查找树的方案数

    1. 题目描述

    在这里插入图片描述

    2. 思路分析

    • BST的性质是:这棵树所有子树满足:左子树左右节点<根节点<右子树所有节点。
    • 这题要求按数组顺序插入时,能构建同一颗BST。
    • 观察数组样例,会发现第一个插入的一定是根节点,后续插入大的就去右边,小的就去左边;
    • 大的和小的去到不同地方,那么大、小互相之间不会有影响,他们的相对顺序就是我们可以调整的。
    • 如果两个都是小于根节点的值,他们俩的插入顺序变化,左子树就会发生变化,因为先插入的是长辈;同理右子树一样。
    • 因此我们处理完左右子树后,合并的算法如下:
    • 设左子树的节点数量为a,右子树的节点数量为b,求a个小数和b个小数排列到一起的方案数,其中a、b内部的顺序不能变。
    • 这个点想了半天,最后发现其实就是C(a+b,a),即为:一共a+b个位置,选a个放左子树的东西。剩下的放b。
    • 然后就到了喜闻乐见的组合数取模问题,查了很多资料,有好几种算法计算C(n,r):
      • 当n,r<1e3,可以用n^2算法,杨辉三角帕斯卡恒等式:C(n,r) = C(n-1,r-1)+C(n-1,r). r==0时,C(n,0) = 1
        • 证明:从n中选一个基准数k,如果r里包含这个k,则是从剩下n-1个数取r-1个C(n-1,r);如果不包含,就是从剩下n-1个取r个C(n-1,r-1)。
      • 当n,r<1e5,这时n^2算法不能用了。需要用线性递推,公式大概是C(n,r) = C(n,r-1)*(n-r+1)/r ,但是这个里包含除法,需要求从乘法逆元,这里不展开了。
      • 当n,r<1e9,MOD<1e5,这时需要用卢卡斯Lucas定理,时间复杂度是plgN,但是可以用O§预先处理p以内的逆元阶乘,然后单次查询就是lgN。
      • 当n,r<1e9,MOD<1e9,那不会做了。
    • 研究半天,发现python的int自带大数,不管多少位都不溢出。而且自带组合数计算。
    • 组合数:comb(n,r)
    • 排列数:perm(n,r)
    • 阶乘:factorial(n)
    • 幂:2**65
    • 以上都不会溢出,最后直接%MOD即可。
    • Life is short,I use Python。
      有时间应该整理一下取模的模板。

    3. 代码实现

    class Solution:
        def numOfWays(self, nums: List[int]) -> int:
            MOD=10**9+7
            def dfs(nums):
                if not nums:
                    return 1
                l,r = [],[]
                for i in range(1,len(nums)):
                    if nums[i] < nums[0]:
                        l.append(nums[i])
                    else:
                        r.append(nums[i])
                return comb(len(l)+len(r),len(r)) * dfs(l) *dfs(r) % MOD
            return (dfs(nums) + MOD - 1)%MOD
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    mysql内部存储代码的优势
    代码学习记录48---单调栈
    Docker Compose快速入门及实战
    Asp.Net Core&Jaeger实现链路追踪
    代码随想录算法训练营之JAVA|第四十三天|139. 单词拆分
    数组排序,实现中间数字最大,向两边越来越小的排序
    shell 初探
    代码随想录 Day - 48|#198 打家劫舍|#213 打家劫舍 II|#337 打家劫舍 III
    InfiniteScroll 无限滚动组件第一次进来就执行load方法怎么办
    不使用脚手架构建vue项目
  • 原文地址:https://blog.csdn.net/liuliangcan/article/details/125529565