[英雄星球六月集训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)
二、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)
三、 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,那不会做了。
- 当n,r<1e3,可以用n^2算法,杨辉三角、帕斯卡恒等式:C(n,r) = C(n-1,r-1)+C(n-1,r). r==0时,C(n,0) = 1
- 研究半天,发现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