2022-10-20 笔记
779. 第K个语法符号(每日一题)
1. 题目
2. 示例
示例 1:
输入: n = 1, k = 1
输出: 0
解释: 第一行:0
示例 2:
输入: n = 2, k = 1
输出: 0
解释:
第一行: 0
第二行: 01
示例 3:
输入: n = 2, k = 2
输出: 1
解释:
第一行: 0
第二行: 01
3. 注意事项
- 难度:中等题
- 暴力搜索会超出时间限制
- 但是我们可以找出一些规律
- 当前层的字符串可以平均分成 4 部分, s1, s2, s3, s4
- 通过分析可知, s1 = s4, s2 = s3
- 其中 s1 + s2 组成了上一层的字符串
- 当层数小于等于 4 时,直接当作特殊情况,使用一个数组,直接返回
arr[n-1][k-1]
- 当层数大4时,我们进行递归,找到指定位置的字符
- 递推公式:
f(n,k) = f(n-1,k) 1 <= k <= l2
f(n,k) = f(n-1,k + l3 - 2 * l2) l2 < k <= l3
f(n,k) = f(n-1,k - l3 ) l3 < k <= l4
- 其中
l1 = 2^(n - 3), l2 = 2^(n - 2), l3 = l1 + l2, l4 = 2^(n - 1),
- 提示:
- 1 <= n <= 30
- 1 <= k <= 2^(n - 1)
- 来源:力扣(LeetCode)
- 链接:传送门
- 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
4. Python 代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/10/20 10:20
# @Author : David
# @File : 779. 第K个语法符号.py
# @Description : 779. 第K个语法符号
"""
我们构建了一个包含 n 行( 索引从 1 开始 )的表。首先在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。
例如,对于 n = 3 ,第 1 行是 0 ,第 2 行是 01 ,第3行是 0110 。
给定行数 n 和序数 k,返回第 n 行中第 k 个字符。( k 从索引 1 开始)
提示:
1 <= n <= 30
1 <= k <= 2^(n - 1)
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/k-th-symbol-in-grammar
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
"""
import time
class Solution(object):
def kthGrammar(self, n: int, k: int) -> int:
"""
直接找规律
:param n: 行数, 索引从 1 开始
:param k: 列数, 索引从 1 开始
:return: arr[n-1][k-1]
"""
def get_str(x: int, y: int) -> str:
"""
获取指定层数的字符串
分析:
1. 当前层的字符串可以平均分成 4 部分, s1, s2, s3, s4
2. 通过分析可知, s1 = s4, s2 = s3
3. 其中 s1 + s2 组成了上一层的字符串
:return: n-1行 k-1列字符
"""
if x == 4:
s = "01101001"
return s[y - 1]
else:
s2 = 2 ** (x - 2) # 求出字符串的中间下标, 从 1 开始, 例如: n = 5, s2 = 8
s3 = int(s2 + s2 // 2) # 求出字符串的 3/4 下标, 从 1 开始, 例如: n = 5, s3 = 12
if s2 < y <= s3:
y += (s3 - 2 * s2)
elif s3 < y:
# 特殊情况,当 k = s3 时,不能直接使用 k = k - s3, 这样有一些特殊情况会出错
# 例如 n = 30, k = 423336352时就出现了错误。k = s3时,k=k+s3-2*s2
y -= s3
return get_str(x - 1, y)
arr = [
"0",
"01",
"0110"
]
if n < 4:
return int(arr[n - 1][k - 1])
else:
return int(get_str(n, k))
def Main(self):
n = 30
k = 423336352
print(self.kthGrammar(n, k))
if __name__ == '__main__':
start = time.time()
Solution().Main()
end = time.time()
print(f"耗时 {end - start}s")
1339. 分裂二叉树的最大乘积
1. 题目
2. 示例
示例 1:
输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)
示例 2:
输入:root = [1,null,2,3,4,null,null,5,6]
输出:90
解释:移除红色的边,得到 2 棵子树,和分别是 15 和 6 。它们的乘积为 90 (15*6)
示例 3:
输入:root = [2,3,9,10,7,8,6,5,4,11,1]
输出:1025
示例 4:
输入:root = [1,1]
输出:1
3. 注意事项
- 难度:中等题
- 注意循环中间不要使用 mod 不然结果可能有问题
- 小提示:
这题大数据的测试用例有点恶心,如果代码没有逻辑问题,可以试试中间不要做mod操作,只在最后返回结果时mod一次即可,应该能过
- 提示:
- 每棵树最多有 50000 个节点,且至少有 2 个节点。
- 每个节点的值在 [1, 10000] 之间。
- 来源:力扣(LeetCode)
- 链接:题目传送门
- 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
4. python 代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/10/19 19:23
# @Author : David
# @File : 1339. 分裂二叉树的最大乘积.py
# @Description : 1339. 分裂二叉树的最大乘积
from functools import cache
from typing import Optional
"""
给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。
由于答案可能会很大,请你将结果对 10^9 + 7 取模后再返回。
提示:
每棵树最多有 50000 个节点,且至少有 2 个节点。
每个节点的值在 [1, 10000] 之间。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-product-of-splitted-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
"""
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def maxProduct(self, root: Optional[TreeNode]) -> int:
"""
乘积 = 某个节点下所有子节点的和 *(整个树的和 - 某个节点下所有子节点的和)
:param root: 根节点
:return: 两棵子树乘积的最大值
"""
@cache
def dfs(node: Optional[TreeNode]) -> int:
"""
深度优先遍历, 计算当前节点及自己点的和
使用的是后序遍历: 左 -> 右 -> 根
:param node: 当前节点
:return: 当前节点及子节点的和
"""
if node is None:
return 0
else:
# 当前节点的和等于左子树的和 + 右子树的和 + 当前节点值
sum_list.append(dfs(node.left) + dfs(node.right) + node.val)
return sum_list[-1]
sum_list = list() # 所有节点的和值
root_sum = dfs(root) # 计算根节点的和值(整棵树的和)
answer = 0
# 乘积 = 某个节点下所有子节点的和 *(整个树的和 - 某个节点下所有子节点的和)
for x in sum_list:
answer = max(answer, x * (root_sum - x))
return answer % (10 ** 9 + 7)
def Main(self):
root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3, TreeNode(6)))
print(self.maxProduct(root=root))
if __name__ == '__main__':
Solution().Main()
1130. 叶值的最小代价生成树
1. 题目
2. 示例
示例:
输入:arr = [6,2,4]
输出:32
解释:
有两种可能的树,第一种的非叶节点的总和为 36,第二种非叶节点的总和为 32。
24 24
/ \ / \
12 4 6 8
/ \ / \
6 2 2 4
3. 注意事项
- 难度:中等题
- xxx
- 提示:
- 2 <= arr.length <= 40
- 1 <= arr[i] <= 15
- 答案保证是一个 32 位带符号整数,即小于 2^31。
- 来源:力扣(LeetCode)
- 链接:传送门
- 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
4. Python 代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/10/20 21:06
# @Author : David
# @File : 1130. 叶值的最小代价生成树.py
# @Description : 1130. 叶值的最小代价生成树
import sys
from typing import List
"""
给你一个正整数数组 arr,考虑所有满足以下条件的二叉树:
每个节点都有 0 个或是 2 个子节点。
数组 arr 中的值与树的中序遍历中每个叶节点的值一一对应。(知识回顾:如果一个节点有 0 个子节点,那么该节点为叶节点。)
每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。
在所有这样的二叉树中,返回每个非叶节点的值的最小可能总和。这个和的值是一个 32 位整数。
提示:
2 <= arr.length <= 40
1 <= arr[i] <= 15
答案保证是一个 32 位带符号整数,即小于 2^31。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
"""
class Solution(object):
def Main(self):
arr = [6, 2, 4, 3, 5, 1]
print(self.mctFromLeafValues(arr))
def mctFromLeafValues(self, arr: List[int]) -> int:
n = len(arr)
# dp[start,end]: [start,end] 之间的叶子结点,他们所在的二叉树中非叶子结点的值的最小可能总和
dp = [[sys.maxsize] * n for _ in range(n)]
for i in range(n):
# 叶子节点 dp[i][i] 初始化为 0
dp[i][i] = 0
for length in range(1, n):
# length 表示区间 [start,end] 的长度(区间中节点的个数)
for start in range(n - length):
# start 开始节点的位置, 0 <= start < n - length
for mid in range(start, start + length):
# mid 中间节点的位置, start <= mid < start + length
dp[start][start + length] = min(dp[start][start + length], dp[start][mid] + dp[mid + 1][start + length] + max(arr[start:mid + 1]) * max(arr[mid + 1:start + length + 1]))
return dp[0][n - 1]
if __name__ == '__main__':
Solution().Main()
总结
779. 第K个语法符号(每日一题)
关键时找规律,找到了规律就好解决了。题目中每一层的字符串可以拆分成 4 份, 即S(n) = s1 + s2 + s3 + s4
其中s1 = s4, s2 = s3 并且 S(n-1) = s1 + s2
1339. 分裂二叉树的最大乘积
使用dfs
计算树的每个节点的和值,最后通过遍历所有节点的和值,计算出和值乘积的最大值- 动态规划还有待加强,
1130. 叶值的最小代价生成树
完全没有思路,根本没有想到使用动态规划解决 1130. 叶值的最小代价生成树
这题还有一些问题,输入的数组arr = [6, 2, 4, 3, 5, 1]
一直无法构造出对应的二叉树- 主要学习了
树的遍历
,动态规划