【LeetCode 刷题笔记 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. 注意事项

  • 难度:中等题
    • 暴力搜索会超出时间限制
    • 但是我们可以找出一些规律
      1. 当前层的字符串可以平均分成 4 部分, s1, s2, s3, s4
      2. 通过分析可知, s1 = s4, s2 = s3
      3. 其中 s1 + s2 组成了上一层的字符串
      4. 当层数小于等于 4 时,直接当作特殊情况,使用一个数组,直接返回 arr[n-1][k-1]
      5. 当层数大4时,我们进行递归,找到指定位置的字符
      6. 递推公式:
        1. f(n,k) = f(n-1,k) 1 <= k <= l2
        2. f(n,k) = f(n-1,k + l3 - 2 * l2) l2 < k <= l3
        3. f(n,k) = f(n-1,k - l3 ) l3 < k <= l4
        4. 其中 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:
示例1

输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)

示例 2:
示例 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] 一直无法构造出对应的二叉树
  • 主要学习了树的遍历动态规划
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值