Lazy loaded image
技术分享
Leetcode刷题——11月
00 分钟
2024-11-15
2024-11-29
type
status
date
slug
summary
tags
category
icon
password
comment
💡
2024.11.30 十一月,完结撒花🎉🎉🎉!!!

每日一题

2024.11.01 T3259. 超级饮料的最大强化能量

  1. 问题描述
    1. 给定两个整数数组 energyDrinkAenergyDrinkB,它们的长度均为 n,分别表示每小时饮用 A、B 两种不同能量饮料所能提供的强化能量。你需要在每个小时选择饮用其中一种饮料,以最大化n 小时内的总强化能量。
      限制条件
      • 如果从一种饮料切换到另一种饮料,需要等待一个小时来适应,这个小时将不提供任何强化能量。
      • 可以选择从任意一种能量饮料开始饮用。
  1. 解决方案
    1. 为了解决这个问题,可以使用动态规划来计算在每个小时选择不同饮料类型所能获得的最大强化能量。关键在于合理地记录和更新不同情况下的选择:是否切换饮料,或者保持不变。
  1. 实现思路
    1. 状态定义
        • 定义 dp[0][i] 表示在第 i 小时选择饮用能量饮料 A 的情况下,能够获得的最大强化能量。
        • 定义 dp[1][i] 表示在第 i 小时选择饮用能量饮料 B 的情况下,能够获得的最大强化能量。
    2. 状态转移
        • 在第 i 小时选择饮料 A,有两种情况:
          • 在第 i-1 小时选择了饮料 A,则 dp[0][i] = dp[0][i-1] + energyDrinkA[i-1]
          • 在第 i-2 小时选择了饮料 B,则 dp[0][i] = dp[1][i-2] + energyDrinkA[i-1]
          • 因此,dp[0][i] = max(dp[0][i-1] + energyDrinkA[i-1], dp[1][i-2] + energyDrinkA[i-1])
        • 同理,在第 i 小时选择饮料 B:
          • 在第 i-1 小时选择了饮料 B,则 dp[1][i] = dp[1][i-1] + energyDrinkB[i-1]
          • 在第 i-2 小时选择了饮料 A,则 dp[1][i] = dp[0][i-2] + energyDrinkB[i-1]
          • 因此,dp[1][i] = max(dp[1][i-1] + energyDrinkB[i-1], dp[0][i-2] + energyDrinkB[i-1])
    3. 初始条件
        • dp[0][1] = energyDrinkA[0]:如果从第一个小时选择饮料 A,获得的能量为 energyDrinkA[0]
        • dp[1][1] = energyDrinkB[0]:如果从第一个小时选择饮料 B,获得的能量为 energyDrinkB[0]
    4. 结果
        • 计算完 n 小时的最大强化能量后,结果为 max(dp[0][n], dp[1][n])
  1. 代码实现细节
    1. 初始化状态
        • 初始化 dp[0][1]dp[1][1] 表示在第一小时选择饮料 A 或饮料 B 时的能量值。
    2. 状态转移方程
        • dp[0][i]dp[1][i] 的值通过前面两种可能的选择转移而来,即维持相同饮料或切换饮料。
    3. 结果返回
        • 最后返回 max(dp[0][n], dp[1][n]),表示在 n 小时结束时,选择任意饮料的最大强化能量。
  1. 代码实现
  1. 复杂度分析
      • 时间复杂度,因为需要遍历每个小时 次,并在每次遍历时进行常数操作。
      • 空间复杂度,使用了一个大小为 的二维数组存储每小时的状态。
 

2024.11.02 T3226. 使两个整数相等的位更改次数

  1. 问题描述
    1. 给定两个正整数 nk。可以选择 n 的二进制表示中任意一个值为 1 的位,将其改为 0。返回将 n 变成 k 所需要的最少更改次数。如果无法实现,返回 -1
  1. 问题分析
    1. 判断是否可以实现
        • 要将 n 变为 kk 的每一位 1 只能来自 n 中已有的 1。换句话说,k 中的所有 1 位必须出现在 n 中的 1 位位置上。
        • 通过位运算 (n | k) == n 可以判断是否可以实现:
          • 如果 n 的每个 1 位能完全覆盖 k 中的所有 1 位,则 (n | k) == n。否则,返回 1
    2. 计算更改次数
        • 如果 (n | k) == n,说明可以将 n 变为 k
        • 计算 nk 的不同位位置,即 nk 的异或结果 xorResult = n ^ k
        • xorResult 的每一位检查:每个 1 位表示 n 中一个需要更改的 1 位。
        • 统计 xorResult 中的 1 位数量即可得到最小更改次数。
  1. 代码实现细节
    1. 判断是否可以实现
        • 使用 (n | k) != n 判断是否可以实现,如果 kn 无法覆盖的 1 位,则直接返回 1
    2. 异或运算计算不同位
        • xorResult = n ^ knk 不同的位标记为 1,即需要更改的位。
        • 逐位检查 xorResult 中的 1,计数得到需要更改的位数。
    3. 右移检查每位
        • 使用 while (xorResult > 0) 循环,将 xorResult 每次右移一位,并检查当前位是否为 1,若是则计数。
  1. 代码实现
    1. 复杂度分析
    • 时间复杂度,因为整数 n 的二进制表示长度为 ,逐位检查异或结果的时间复杂度为
    • 空间复杂度,只使用了常数个额外变量。
     

    2024.11.03 T638 . 大礼包

    638. 大礼包 - 力扣(LeetCode)
    638. 大礼包 - 在 LeetCode 商店中, 有 n 件在售的物品。每件物品都有对应的价格。然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。 给你一个整数数组 price 表示物品价格,其中 price[i] 是第 i 件物品的价格。另有一个整数数组 needs 表示购物清单,其中 needs[i] 是需要购买第 i 件物品的数量。 还有一个数组 special 表示大礼包,special[i] 的长度为 n + 1 ,其中 special[i][j] 表示第 i 个大礼包中内含第 j 件物品的数量,且 special[i][n] (也就是数组中的最后一个整数)为第 i 个大礼包的价格。 返回 确切 满足购物清单所需花费的最低价格,你可以充分利用大礼包的优惠活动。你不能购买超出购物清单指定数量的物品,即使那样会降低整体价格。任意大礼包可无限次购买。   示例 1: 输入:price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2] 输出:14 解释:有 A 和 B 两种物品,价格分别为 ¥2 和 ¥5 。 大礼包 1 ,你可以以 ¥5 的价格购买 3A 和 0B 。 大礼包 2 ,你可以以 ¥10 的价格购买 1A 和 2B 。 需要购买 3 个 A 和 2 个 B , 所以付 ¥10 购买 1A 和 2B(大礼包 2),以及 ¥4 购买 2A 。 示例 2: 输入:price = [2,3,4], special = [[1,1,0,4],[2,2,1,9]], needs = [1,2,1] 输出:11 解释:A ,B ,C 的价格分别为 ¥2 ,¥3 ,¥4 。 可以用 ¥4 购买 1A 和 1B ,也可以用 ¥9 购买 2A ,2B 和 1C 。 需要买 1A ,2B 和 1C ,所以付 ¥4 买 1A 和 1B(大礼包 1),以及 ¥3 购买 1B , ¥4 购买 1C 。 不可以购买超出待购清单的物品,尽管购买大礼包 2 更加便宜。   提示: * n == price.length == needs.length * 1 <= n <= 6 * 0 <= price[i], needs[i] <= 10 * 1 <= special.length <= 100 * special[i].length == n + 1 * 0 <= special[i][j] <= 50 * 生成的输入对于 0 <= j <= n - 1 至少有一个 special[i][j] 非零。
    638. 大礼包 - 力扣(LeetCode)
    1. 问题描述
      1. 在 LeetCode 商店中,有 n 件商品,每件商品都有对应的价格。同时,有一些大礼包以优惠价格捆绑销售一组商品。给定商品的单价数组 price、购物清单数组 needs 和大礼包数组 special,需要返回满足购物清单所需的最低花费,允许无限次使用大礼包。
    1. 解决思路
      1. 状态定义
          • needs 数组表示当前需要购买的商品数量。
          • currentCost 表示当前已花费的金额。
      2. 递归与深度优先搜索
          • 使用深度优先搜索(DFS)来探索所有可能的购买组合。
          • 每次递归时,首先检查当前花费是否已经超过已知的最低花费,若超过则剪枝。
          • 计算单独购买剩余商品的花费,并更新最低花费。
      3. 考虑礼包购买
          • 对于每个大礼包,减少 needs 中对应商品的数量,并增加礼包的价格到 currentCost,然后进行递归。
          • 递归结束后恢复 needscurrentCost,以便尝试其他礼包。
      4. 剪枝优化
          • 如果在计算过程中发现某种商品的需求量小于 0,则直接返回,避免无效的递归。
          • 在每次递归时检查总花费是否变化,若没有变化则返回,减少不必要的计算。
    1. 代码实现细节
      1. 状态管理
          • 使用 std::vector<int> singleItemPrices 存储商品价格。
          • 使用 std::vector<std::vector<int>> bundleOffers 存储大礼包的组成和价格。
      2. DFS 函数
          • dfs(int currentCost, std::vector<int>& needs) 实现了深度优先搜索逻辑。
          • 首先判断当前花费是否超过最低花费,进行剪枝。
          • 计算当前 needs 数组中所有未满足需求的商品的花费,并更新最低花费。
      3. 礼包处理
          • 通过递归尝试每种礼包的购买组合。
          • 使用循环遍历每个礼包,更新需求并计算新的总花费,递归调用 dfs
      4. 剪枝优化
          • 如果在某次递归中发现 needs 中某个商品的数量为负,则直接返回以避免继续无效计算。
          • 计算总花费后,如果未发生变化,则可以返回,进一步减少不必要的递归深度。
    1. 代码实现
      1. 复杂度分析
          • 时间复杂度:最坏情况下,DFS 会遍历所有组合,复杂度为 ,其中 m 是礼包的数量,n 是商品的数量。实际运行时会由于剪枝显著降低复杂度。
          • 空间复杂度:递归栈的深度为 ,需要存储每次递归的 needs 状态和当前花费,额外的空间复杂度为

      2024.11.04 T633 . 平方数之和

      1. 问题描述
        1. 给定一个非负整数 c,需要判断是否存在两个非负整数 ab,使得
      1. 解决思路
        1. 双指针方法
            • 设定两个指针 lr,分别从 0 和 开始。
            • 通过计算 c 的比较来调整指针:
              • 如果 ,说明 r 需要减小,以减小总和。
              • 如果 ,说明 l 需要增大,以增大总和。
              • 如果 ,则找到了满足条件的 ab
        2. 结束条件
            • l 超过 r 时,遍历结束。
      1. 代码实现
        1. 代码详解
            • 数据类型:使用 long long 类型来避免在计算平方时溢出。
            • 指针初始化
              • l 从 0 开始,r 开始。
            • 主循环:使用 while 循环,检查指针是否相交。
            • 条件判断
              • 计算 sum = l * l + r * r
              • 根据 sumc 的比较,调整指针位置。
        1. 复杂度分析
            • 时间复杂度,因为 lr 的最大值均为
            • 空间复杂度,只使用了常量空间来存储指针和中间变量。
         

        2024.11.05 T3222. 求出硬币游戏的赢家

        1. 问题描述
          1. 给定两个正整数 xy,分别表示价值为 75 和 10 的硬币的数量。Alice 和 Bob 玩一个游戏,每轮游戏中玩家需要拿出硬币,总价值为 115。如果玩家无法做到这一点,他们将输掉游戏。Alice 先手,Bob 后手。两名玩家都采取最优策略,要求判断游戏的赢家。
        1. 思路分析
          1. 游戏逻辑
              • 每轮游戏中,玩家需要组合出总价值为 115 的硬币。可以用硬币的组合如下:
                • 1 个 75 的硬币和 4 个 10 的硬币。
              • 玩家需要在每轮消耗 1 个 75 的硬币和 4 个 10 的硬币。
          2. 胜负判断
              • 游戏进行到某一轮时,如果当前玩家无法拿出总价值为 115 的硬币,则该玩家输掉游戏。
              • Alice 先手,Bob 后手,轮流进行游戏。
          3. 最优策略
              • 两个玩家都采取最优策略,即尽可能多次使用组合 (1 * 75 + 4 * 10) 直到无法进行时。
        1. 实现思路
          1. 计数循环
              • 初始化一个标记 tag,用于跟踪当前轮到的玩家。
              • 每轮消耗 xy,即 x--y -= 4
              • 轮流切换玩家,直到其中一个玩家无法满足条件(x < 0y < 0)。
          2. 返回结果
              • 如果 tagtrue,表示最后一轮是 Bob 操作时无法进行,返回 "Bob" 输掉游戏。
              • 如果 tagfalse,表示 Alice 操作时无法进行,返回 "Alice" 输掉游戏。
        1. 代码实现
          1. 代码实现细节
            1. 变量初始化
                • bool tag = 0,表示当前是 Alice 的回合。
            2. 游戏循环
                • 每次循环中,消耗 1 个 75 的硬币和 4 个 10 的硬币,并切换玩家。
                • 循环条件是 x >= 1 && y >= 4,因为每轮需要满足该条件才能继续。
            3. 判断输赢
                • 当循环结束时,根据 tag 的值返回输家。
          1. 复杂度分析
              • 时间复杂度,每次循环减少一个 75 的硬币和四个 10 的硬币,因此时间复杂度是线性的。
              • 空间复杂度,只使用了常数空间。
           

          2024.11.06 T3254. 长度为 K 的子数组的能量值 I

          2024.11.07 T3255. 长度为 K 的子数组的能量值 II

          1. 问题描述
            1. 给定一个整数数组 nums 和一个正整数 k,你需要计算所有长度为 k 的子数组的能量值。子数组的能量值定义如下:
              • 如果该子数组中的元素是连续且严格递增的,那么能量值为该子数组中的最大元素。
              • 如果不是严格递增的,则能量值为 -1。
              最终需要返回一个包含所有长度为 k 子数组的能量值的数组。
          1. 解决思路
            1. 遍历每个子数组:对于每个长度为 k 的子数组,我们需要判断它是否是严格递增的。如果是递增的,返回该子数组中的最大元素;如果不是递增的,返回 -1。
            2. 递增判断:为了判断一个子数组是否严格递增,可以遍历该子数组并确保每个元素都比前一个元素大。如果存在任何元素不满足这一条件,则认为该子数组不是递增的。
            3. 效率考虑:遍历每个子数组并检查是否递增,时间复杂度是 O(k),而有 n - k + 1 个子数组需要检查。因此,总体的时间复杂度是 O(k * (n - k + 1)),即 O(n * k)
            4. 返回结果:对于每个子数组,满足条件的情况下,返回该子数组的最大值,否则返回 -1。
          1. 代码实现
            1. 代码解释
              1. 遍历所有长度为 k 的子数组
                  • 从索引 i = 0i = n - k,依次考虑每个长度为 k 的子数组。
              2. 递增判断
                  • 对于每个子数组,我们检查从 nums[i]nums[i + k - 1] 是否是严格递增的。
                  • 如果发现任意一对相邻元素 nums[j]nums[j+1] 不是递增的(即 nums[j] + 1 != nums[j+1]),则设置 isIncreasingfalse,并跳出循环。
              3. 计算能量值
                  • 如果子数组是递增的,能量值是该子数组的最大值 nums[i + k - 1](即子数组的最后一个元素,因为它是最大元素)。
                  • 如果子数组不是递增的,能量值为 -1。
              4. 返回结果
                  • 最终返回一个包含所有子数组能量值的数组 res
            1. 复杂度分析
                • 时间复杂度,即对于每个长度为 k 的子数组,我们需要检查其是否递增,且此操作在每个子数组上需要 O(k) 的时间。因此总的时间复杂度是
                • 空间复杂度,即需要一个数组 res 来存储所有子数组的能量值,长度为

            2024.11.08 T3235. 判断矩形的两个角落是否可达

            3235. 判断矩形的两个角落是否可达 - 力扣(LeetCode)
            3235. 判断矩形的两个角落是否可达 - 给你两个正整数 xCorner 和 yCorner 和一个二维整数数组 circles ,其中 circles[i] = [xi, yi, ri] 表示一个圆心在 (xi, yi) 半径为 ri 的圆。 坐标平面内有一个左下角在原点,右上角在 (xCorner, yCorner) 的矩形。你需要判断是否存在一条从左下角到右上角的路径满足:路径 完全 在矩形内部,不会 触碰或者经过 任何 圆的内部和边界,同时 只 在起点和终点接触到矩形。 如果存在这样的路径,请你返回 true ,否则返回 false 。   示例 1: 输入:X = 3, Y = 4, circles = [[2,1,1]] 输出:true 解释: [https://assets.leetcode.com/uploads/2024/05/18/example2circle1.png] 黑色曲线表示一条从 (0, 0) 到 (3, 4) 的路径。 示例 2: 输入:X = 3, Y = 3, circles = [[1,1,2]] 输出:false 解释: [https://assets.leetcode.com/uploads/2024/05/18/example1circle.png] 不存在从 (0, 0) 到 (3, 3) 的路径。 示例 3: 输入:X = 3, Y = 3, circles = [[2,1,1],[1,2,1]] 输出:false 解释: [https://assets.leetcode.com/uploads/2024/05/18/example0circle.png] 不存在从 (0, 0) 到 (3, 3) 的路径。 示例 4: 输入:X = 4, Y = 4, circles = [[5,5,1]] 输出:true 解释: [https://assets.leetcode.com/uploads/2024/08/04/rectangles.png]   提示: * 3 <= xCorner, yCorner <= 109 * 1 <= circles.length <= 1000 * circles[i].length == 3 * 1 <= xi, yi, ri <= 109
            3235. 判断矩形的两个角落是否可达 - 力扣(LeetCode)
            1. 问题描述
              1. 给定一个二维矩形,其左下角在原点 (0, 0),右上角在 (xCorner, yCorner),并且有多个圆,定义在 circles 数组中,每个圆的表示为 [xi, yi, ri],表示圆心在 (xi, yi),半径为 ri。需要判断是否存在一条从矩形的左下角到右上角的路径,这条路径完全在矩形内部,不会触碰或经过任何圆的内部和边界,仅在起点 (0, 0) 和终点 (xCorner, yCorner) 接触矩形边界。
                如果存在这样的路径,则返回 true,否则返回 false
            1. 解决思路
              1. 本问题要求在考虑多个圆障碍的情况下,确定是否可以找到一条从矩形左下角到右上角的路径。可以将此问题视为路径查找和碰撞检测问题。需要确保路径不接触或穿越任何圆的边界或内部,同时满足到达终点的条件。
            1. 具体步骤
              1. 判断矩形边界与圆的碰撞
                  • 首先检查每个圆是否覆盖了矩形的左下角 (0, 0) 或右上角 (xCorner, yCorner),如果有任何圆覆盖了这些点,则说明从起点或终点出发就不可行,直接返回 false
                  • 然后检查每个圆是否接触到矩形的左边界、下边界、上边界或右边界,以确定它们是否阻挡了路径。
              2. 深度优先搜索 (DFS) 检查圆的连接性
                  • 使用 DFS 检查是否存在连接到矩形内部的圆群。
                  • 对于每一个圆,如果它连接到另一个圆,检查其距离和路径是否相交,如果相连,则继续进行递归。
                  • 如果在递归过程中发现某个圆接触到矩形边界或矩形的内部,则路径被阻断。
              3. 路径搜索的剪枝
                  • 若当前圆无法从一个圆直接穿越到矩形的另一边,则返回 false
                  • DFS 过程中,若某个圆已经被访问过,则跳过,以减少不必要的重复计算。
            1. 代码实现
              1. 代码解释
                1. 辅助函数 isPointInCircle
                    • 判断一个点 (x, y) 是否位于圆 (centerX, centerY, radius) 的内部或边界上。
                2. DFS 函数 dfs
                    • 递归地检查圆与圆之间的连接性。
                    • 判断当前圆是否接触到矩形的边界或内部。如果接触则返回 true,表示路径被阻断。
                3. 主循环
                    • 遍历每个圆,检查它是否覆盖了矩形的起点或终点。如果覆盖,则直接返回 false
                    • 使用 DFS 检查当前圆是否与其他圆连接并穿越矩形的内部。如果存在这样的一组圆,则返回 false
              1. 复杂度分析
                  • 时间复杂度,其中 n 是圆的数量。DFS 中对于每个圆需要检查其他圆,导致平方级别的时间复杂度。
                  • 空间复杂度,递归深度和访问数组的存储空间。
               

              2024.11.09 T3242. 设计相邻元素求和服务

              3242. 设计相邻元素求和服务 - 力扣(LeetCode)
              3242. 设计相邻元素求和服务 - 给你一个 n x n 的二维数组 grid,它包含范围 [0, n2 - 1] 内的不重复元素。 实现 neighborSum 类: * neighborSum(int [][]grid) 初始化对象。 * int adjacentSum(int value) 返回在 grid 中与 value 相邻的元素之和,相邻指的是与 value 在上、左、右或下的元素。 * int diagonalSum(int value) 返回在 grid 中与 value 对角线相邻的元素之和,对角线相邻指的是与 value 在左上、右上、左下或右下的元素。 [https://assets.leetcode.com/uploads/2024/06/24/design.png]   示例 1: 输入: ["neighborSum", "adjacentSum", "adjacentSum", "diagonalSum", "diagonalSum"] [[[[0, 1, 2], [3, 4, 5], [6, 7, 8]]], [1], [4], [4], [8]] 输出: [null, 6, 16, 16, 4] 解释: [https://assets.leetcode.com/uploads/2024/06/24/designexample0.png] * 1 的相邻元素是 0、2 和 4。 * 4 的相邻元素是 1、3、5 和 7。 * 4 的对角线相邻元素是 0、2、6 和 8。 * 8 的对角线相邻元素是 4。 示例 2: 输入: ["neighborSum", "adjacentSum", "diagonalSum"] [[[[1, 2, 0, 3], [4, 7, 15, 6], [8, 9, 10, 11], [12, 13, 14, 5]]], [15], [9]] 输出: [null, 23, 45] 解释: [https://assets.leetcode.com/uploads/2024/06/24/designexample2.png] * 15 的相邻元素是 0、10、7 和 6。 * 9 的对角线相邻元素是 4、12、14 和 15。   提示: * 3 <= n == grid.length == grid[0].length <= 10 * 0 <= grid[i][j] <= n2 - 1 * 所有 grid[i][j] 值均不重复。 * adjacentSum 和 diagonalSum 中的 value 均在范围 [0, n2 - 1] 内。 * 最多会调用 adjacentSum 和 diagonalSum 总共 2 * n2 次。
              3242. 设计相邻元素求和服务 - 力扣(LeetCode)
              1. 问题描述
                1. 给定一个 n x n 的二维数组 grid,其中包含从 0n^2 - 1 的不重复整数。要求实现 NeighborSum 类,包括以下两个功能:
                  • int adjacentSum(int value):返回在 grid 中与 value 上、下、左、右相邻的元素之和。
                  • int diagonalSum(int value):返回在 grid 中与 value 在对角线位置(左上、右上、左下、右下)相邻的元素之和。
              1. 解决思路
                1. 方向数组
                    • 定义一个方向数组 dirs,表示四个正交相邻方向和四个对角线相邻方向:
                      • 正交方向(上、下、左、右):{-1, 0}, {1, 0}, {0, -1}, {0, 1}
                      • 对角线方向(左上、右上、左下、右下):{-1, -1}, {1, 1}, {-1, 1}, {1, -1}
                2. 预处理
                    • 使用一个一维数组 s 来存储每个 value 的相邻元素和。
                    • 遍历 grid 中的每个元素,根据其 valuedirs 数组,计算其四个正交相邻方向的和(存储在 s[value][0])以及四个对角线相邻方向的和(存储在 s[value][1])。
                3. 查询
                    • adjacentSum(int value) 直接返回 s[value][0]
                    • diagonalSum(int value) 直接返回 s[value][1]
              1. 代码实现
                1. 代码解释
                  1. 构造函数 NeighborSum
                      • 使用方向数组 dirs 遍历 grid 中的每个元素,并计算其正交相邻和对角线相邻元素之和。
                      • s[v][0] 保存值 v 的正交相邻之和,s[v][1] 保存值 v 的对角线相邻之和。
                  2. adjacentSumdiagonalSum
                      • 直接从 s 数组中读取已计算的结果,分别返回正交相邻之和和对角线相邻之和。
                1. 复杂度分析
                • 时间复杂度。构造函数遍历整个 n x ngrid 并计算相邻元素之和,复杂度为
                • 空间复杂度。存储每个元素的正交相邻和对角线相邻元素之和,使用了 s 数组,空间复杂度为
                 

                2024.11.10 T540 . 有序数组中的单一元素

                1. 问题描述
                  1. 给定一个仅由整数组成的有序数组 nums,其中每个元素都会出现两次,唯有一个数只会出现一次。要求找出并返回只出现一次的那个数。设计的解决方案需要满足 时间复杂度和 空间复杂度。
                1. 思路分析
                  1. 由于数组是有序的且每个元素都出现两次(除了那个唯一的元素),可以利用二分查找来达到 的时间复杂度。以下是具体的分析步骤:
                  2. 特征分析
                      • 假设 nums 中的元素除了唯一的单个元素外,都成对出现。
                      • 每对元素的第一个出现的位置是偶数索引,第二个出现的位置是奇数索引。
                      • 在唯一元素之前的索引对会满足该特性,而在唯一元素之后的索引对会被错位(即成对元素的第一个出现的位置为奇数,第二个位置为偶数)。
                  3. 二分查找
                      • 通过二分查找的思想来缩小范围,使用中点 mid 来划分数组。
                      • 如果 midmid ^ 1 相等(异或操作:将 mid 的最后一位从偶数变为奇数或从奇数变为偶数),则说明唯一元素在 mid 之后,可以移动左指针 l = mid + 1
                      • 如果 midmid ^ 1 不相等,则说明唯一元素在 mid 之前,可以移动右指针 r = mid
                  4. 终止条件
                      • l == r 时,找到唯一的元素,返回 nums[l]
                3. 代码实现
                1. 代码详解
                    • 指针初始化
                      • l = 0r = n - 1,用于二分查找范围。
                    • 二分查找循环
                      • 计算 mid 为当前查找范围的中点。
                      • 使用 mid ^ 1 进行奇偶位置的判断。
                      • 根据结果更新 lr,缩小查找范围。
                    • 返回结果
                      • 最后 l == r 时,返回 nums[l],即为唯一的非重复元素。
                1. 复杂度分析
                    • 时间复杂度,二分查找每次将范围缩小一半。
                    • 空间复杂度,没有使用额外空间。
                 

                2024.11.11 T1547. 切棍子的最小成本

                1547. 切棍子的最小成本 - 力扣(LeetCode)
                1547. 切棍子的最小成本 - 有一根长度为 n 个单位的木棍,棍上从 0 到 n 标记了若干位置。例如,长度为 6 的棍子可以标记如下: [https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/08/09/statement.jpg] 给你一个整数数组 cuts ,其中 cuts[i] 表示你需要将棍子切开的位置。 你可以按顺序完成切割,也可以根据需要更改切割的顺序。 每次切割的成本都是当前要切割的棍子的长度,切棍子的总成本是历次切割成本的总和。对棍子进行切割将会把一根木棍分成两根较小的木棍(这两根木棍的长度和就是切割前木棍的长度)。请参阅第一个示例以获得更直观的解释。 返回切棍子的 最小总成本 。   示例 1: [https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/08/09/e1.jpg] 输入:n = 7, cuts = [1,3,4,5] 输出:16 解释:按 [1, 3, 4, 5] 的顺序切割的情况如下所示: [https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/08/09/e11.jpg] 第一次切割长度为 7 的棍子,成本为 7 。第二次切割长度为 6 的棍子(即第一次切割得到的第二根棍子),第三次切割为长度 4 的棍子,最后切割长度为 3 的棍子。总成本为 7 + 6 + 4 + 3 = 20 。 而将切割顺序重新排列为 [3, 5, 1, 4] 后,总成本 = 16(如示例图中 7 + 4 + 3 + 2 = 16)。 示例 2: 输入:n = 9, cuts = [5,6,1,4,2] 输出:22 解释:如果按给定的顺序切割,则总成本为 25 。总成本 <= 25 的切割顺序很多,例如,[4, 6, 5, 2, 1] 的总成本 = 22,是所有可能方案中成本最小的。   提示: * 2 <= n <= 10^6 * 1 <= cuts.length <= min(n - 1, 100) * 1 <= cuts[i] <= n - 1 * cuts 数组中的所有整数都 互不相同
                1547. 切棍子的最小成本 - 力扣(LeetCode)
                1. 问题描述
                  1. 给定一根长度为 n 的木棍和一个整数数组 cuts,表示需要切割的位置,要求最小化木棍的总切割成本。每次切割的成本是当前木棍的长度。切割后,木棍会分成两段,接下来的切割成本依赖于剩余的木棍长度。返回最小总成本。
                1. 问题解析
                  1. 核心思想是通过合理的选择切割顺序来最小化总成本。
                    关键点:
                  2. 排序切割点
                      • 将切割点排序是因为每次切割的成本与木棍的长度有关,木棍的长度不断变化,因此我们需要知道每一段木棍的长度。
                  3. 动态规划数组
                      • 定义 dp[i][j] 为在 cuts[i]cuts[j] 之间切割的最小成本。
                  4. 状态转移
                      • 对于每一段区间 [i, j],我们尝试选择一个中间点 k 作为切割点,将问题分解为 dp[i][k]dp[k][j] 两个子问题,最终的成本是:dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + (cuts[j] - cuts[i]))
                  5. 边界条件
                      • 当区间的长度为 2(即没有中间切割点时),dp[i][i+1] = 0,因为不需要再切割了。
                1. 动态规划的实现
                  1. 初始化
                      • 将所有切割点(包括木棍的两端)保存在 positions 数组中,positions[0] 为木棍的起点,positions[numCuts+1] 为木棍的终点。
                  2. 状态转移
                      • 遍历所有的区间长度,从 len = 3len = numCuts + 2,对于每个区间 [i, j],尝试在区间内选择一个 k,计算最小切割成本。
                  3. 最终结果
                      • 最终的最小成本存储在 dp[0][numCuts + 1] 中。
                1. 代码实现
                  1. 代码解释
                    1. 初始化和排序
                        • positions[0] = 0positions[numCuts + 1] = n,分别表示木棍的起始和结束位置。
                        • cuts 数组排序,然后将每个切割点填入 positions 数组。
                    2. 动态规划填表
                        • 对于每个长度为 len 的子区间 [i, j],尝试所有的切割点 k,更新 dp[i][j] 为最小成本。
                        • dp[i][j] 表示从 positions[i]positions[j] 区间的最小切割成本。我们计算所有可能的 k(中间的切割点),选择最小的切割成本。
                    3. 返回最小成本
                        • 最终答案为 dp[0][numCuts + 1],即从 0n 范围内最小的切割成本。
                  1. 复杂度分析
                      • 时间复杂度
                        • ,因为我们有 的区间需要处理,对于每个区间,我们需要检查 个切割点。
                      • 空间复杂度
                        • ,存储 dp 数组和 positions 数组。
                   

                  2024.11.12 T3258. 统计满足 K 约束的子字符串数量 I

                  1. 问题描述
                  给定一个二进制字符串 s 和一个整数 k。我们需要计算 s 中所有满足以下条件的子字符串数量:
                  • 子字符串中 0 的数量不超过 k
                  • 子字符串中 1 的数量不超过 k
                  返回符合条件的子字符串数量。
                  1. 解决思路
                    1. 这是一个滑动窗口问题,目的是找出满足特定条件的子字符串的数量。通过维护一个窗口并在满足约束时扩大窗口,不满足时缩小窗口,可以高效地计算出满足条件的子字符串数量。
                    2. 滑动窗口初始化
                        • 维护两个计数器 count0count1,分别记录窗口内 01 的数量。
                        • 设定左边界 left 和结果计数器 result
                    3. 滑动窗口右移
                        • 遍历字符串的每个字符,并将其加入窗口中。如果字符为 0,增加 count0;如果为 1,增加 count1
                    4. 调整窗口
                        • 如果 count0count1 都大于 k,则当前窗口不满足 k 约束。移动左边界 left,逐步缩小窗口,直到满足 k 约束。
                    5. 计算子字符串数量
                        • 对于每个右边界 right,满足条件的子字符串数量等于当前窗口的长度 (right - left + 1)
                        • 累积到 result 中。
                    6. 返回结果
                        • 最终返回所有满足条件的子字符串数量。
                  1. 代码实现
                    1. 代码详解
                      1. 窗口更新
                          • 通过 count0++count1++ 来更新窗口内的 01 的数量。
                      2. 窗口收缩
                          • 如果窗口内的 01 的数量都大于 k,则收缩窗口(即增大 left),同时减少相应的计数器。
                      3. 子字符串计数
                          • 对于每个右边界 right,窗口的长度为 (right - left + 1),表示所有从 leftright 的子字符串都满足 k 约束。
                    1. 复杂度分析
                        • 时间复杂度O(n),每个字符最多只会被左右指针访问一次。
                        • 空间复杂度O(1),只需几个额外的变量用于计数和索引。
                     

                    2024.11.13 T3261. 统计满足 K 约束的子字符串数量 II

                    1. 问题分析
                      1. 给定一个二进制字符串 s 和一个整数 k,我们需要计算 s 中满足以下条件的子字符串数量:
                        • 子字符串中 0 的数量不超过 k
                        • 子字符串中 1 的数量不超过 k
                        对于一系列查询,每个查询给定一个区间 [li, ri],要求计算该区间内所有满足 k 约束的子字符串数量。
                    1. 解题思路
                      1. 这是一个滑动窗口问题,通过使用前缀和数组和维护一个右边界数组来快速计算满足条件的子字符串数量。具体思路如下:
                      2. 滑动窗口处理
                          • 我们通过滑动窗口来遍历字符串,维护一个窗口内的 01 的数量。
                          • 使用两个指针 ij 来表示窗口的左右边界,确保窗口内的 01 的数量都不超过 k
                      3. 前缀和计算
                          • prefix[j] 表示从位置 0 到位置 j 的子字符串数量,所有满足 k 约束的子字符串数量。
                          • 使用 right[i] 数组来记录每个位置 i 的右边界,即最大的位置 j,使得从位置 ij 之间的子字符串满足约束。
                      4. 计算查询结果
                          • 对于每个查询 [li, ri],我们可以通过 prefix 数组快速计算该区间的子字符串数量。通过计算区间内满足条件的子字符串数量,我们利用 right 数组来确定每个位置可以扩展到的最远位置。
                    1. 代码实现
                      1. 代码解释
                        1. 前缀和数组
                            • prefix[j + 1] = prefix[j] + (j - i + 1) 计算从 ij 的子字符串数量并加到 prefix 数组中。
                        2. 右边界计算
                            • right[i] = j 表示从位置 i 到位置 j 之间的子字符串满足 k 约束。在每次扩展窗口时,维护这个右边界信息。
                        3. 处理查询
                            • 对每个查询 [l, r],我们可以通过 prefix 数组和 right 数组来高效地计算该区间的符合条件的子字符串数量。
                            • part1part2 分别表示从 li 的子字符串数量和从 ir 的子字符串数量。
                        4. 返回结果
                            • 最终返回所有查询的结果。
                      1. 复杂度分析
                          • 时间复杂度:所以总的时间复杂度是 ,其中 q 是查询的数量。
                          • 空间复杂度:prefixright 数组的大小是 ,因此空间复杂度是
                       

                      2024.11.14 T3249. 统计好节点的数目

                      1. 问题分析
                        1. 给定一棵树,树中有 n 个节点,节点的编号为从 0 到 n - 1,根节点是节点 0。树的结构通过一个长度为 n - 1 的二维整数数组 edges 表示,其中 edges[i] = [ai, bi] 表示节点 ai 和节点 bi 之间存在一条无向边。
                          我们需要判断树中哪些节点是“好节点”。如果一个节点的所有子节点为根的子树包含的节点数相同,那么该节点被认为是一个“好节点”。
                      1. 解题思路
                        1. 要找到“好节点”的数量,我们需要计算树中每个节点的子树的大小,并检查每个节点的所有子树大小是否相同。
                          关键步骤
                        2. 树的表示:使用邻接表表示树的结构,其中每个节点存储它相邻的节点。
                        3. DFS遍历:从根节点开始进行深度优先遍历 (DFS),在遍历过程中计算每个节点的子树大小,并检查该节点的所有子树是否大小相同。
                        4. 判断好节点:如果某个节点的所有子树大小相同,则该节点是好节点。
                      1. 具体步骤
                        1. 构建树的邻接表:将 edges 数组转换为树的邻接表。
                        2. 深度优先搜索 (DFS):从根节点开始遍历树,对于每个节点,递归计算其子树的大小,并检查该节点的所有子树大小是否一致。
                        3. 统计好节点:如果当前节点的所有子树大小一致,增加好节点的计数。
                      1. 代码实现
                        1. 代码解析
                          1. 初始化
                              • goodNodesCount 用于记录“好节点”的数量。
                              • graph 是一个邻接表,用于存储树的结构。
                          2. 构建邻接表
                              • 遍历 edges 数组,将每一条边 edges[i] = [ai, bi] 转换为邻接表的形式,表示节点 aibi 之间有一条边。
                          3. DFS遍历
                              • dfs 函数采用递归方式,计算每个节点的子树大小,并检查该节点的所有子树是否大小一致。
                              • firstSubtreeSize 用于记录第一个子树的大小,如果后续的子树大小与第一个子树不同,则说明当前节点不是“好节点”。
                              • 递归计算时,如果发现当前节点的所有子树大小一致,则将 goodNodesCount 加 1。
                          4. 返回结果
                              • 最终返回 goodNodesCount,即所有“好节点”的数量。
                        1. 复杂度分析
                        • 时间复杂度:
                          • 邻接表构建O(n),其中 n 是树的节点数,遍历 edges 数组构建邻接表需要 的时间。
                          • DFS遍历O(n),因为树中有 n 个节点,DFS 遍历每个节点一次,时间复杂度是
                          • 因此,总时间复杂度是
                        • 空间复杂度
                          • 邻接表,需要存储树的结构。
                          • DFS递归栈:最深递归的深度为树的高度,最坏情况下高度为
                          • 因此,总空间复杂度是
                         

                        2024.11.15 T3239. 最少翻转次数使二进制矩阵回文 I

                        1. 问题分析
                          1. 给定一个 m x n 的二进制矩阵 grid,我们需要使矩阵的所有行或者所有列成为回文。回文是指某一行或列从前往后读和从后往前读是相同的。为了达到这个目标,我们可以翻转任意一个格子的值,将其从 0 变为 1 或者从 1 变为 0,求最少翻转次数。
                            问题的要求是:返回使矩阵中的所有行或者所有列成为回文所需的最少翻转次数。
                        1. 解题思路
                          1. 我们需要通过两种方式来解决这个问题,分别是:
                          2. 行翻转处理
                              • 对于每一行,检查其是否是回文。如果不是回文,我们需要计算最小的翻转次数,使其成为回文。
                              • 计算所有行需要翻转的总次数。
                          3. 列翻转处理
                              • 对于每一列,检查其是否是回文。如果不是回文,同样需要计算最小的翻转次数。
                              • 计算所有列需要翻转的总次数。
                          4. 选择最少翻转次数
                              • 最终,返回行翻转次数和列翻转次数的最小值,即为答案。
                            为了检查一行或者一列是否为回文,我们使用双指针方法。通过比较左端和右端的元素,如果不同,则需要进行翻转。
                        1. 代码实现
                          1. 代码解释
                            1. minFlipsToMakePalindrome
                                • 这个函数用于判断一个数组(即一行)是否是回文,并计算将其变为回文所需的最小翻转次数。
                                • 使用两个指针 leftright,分别从两端向中间遍历,如果 arr[left] != arr[right],则需要一次翻转。
                                • 返回需要的翻转次数。
                            2. minFlipsToMakeColumnPalindrome
                                • 这个函数用于判断一列是否是回文,并计算将其变为回文所需的最小翻转次数。
                                • 同样使用双指针 topbottom 来遍历列中的元素,判断是否需要翻转。
                                • 返回需要的翻转次数。
                            3. minFlips
                                • 主函数 minFlips 计算所有行和列的翻转次数,分别调用 minFlipsToMakePalindrome 处理每一行和 minFlipsToMakeColumnPalindrome 处理每一列。
                                • 最终返回行翻转和列翻转次数中的最小值,即为结果。
                          1. 复杂度分析
                              • 时间复杂度
                                • 对每一行,我们需要遍历整个行的元素,计算最小翻转次数,时间复杂度为 ,其中 m 是行数,n 是列数。
                                • 对每一列,我们同样需要遍历该列的元素,时间复杂度为
                                • 总的时间复杂度为
                              • 空间复杂度
                                • 主要使用了额外的空间来存储结果,空间复杂度为 ,因为没有额外开辟大的存储空间,除了输入矩阵外。
                           

                          2024.11.16 T3240. 最少翻转次数使二进制矩阵回文 II

                          1. 问题分析
                          我们需要处理一个 m x n 的二进制矩阵 grid,目标是通过最少的翻转次数满足以下两个条件:
                          1. 行和列为回文
                              • 每一行和每一列从前往后与从后往前读是一样的。
                          1. 1 的数量能被 4 整除
                              • 矩阵中所有 1 的个数最终必须是 4 的倍数。
                          为了达成目标,需要合理安排翻转策略,优先处理矩阵的对称性,同时关注 1 的数量调整。
                          2. 解题思路
                          解决问题可以分为以下几步:
                          1. 处理矩阵的对称性
                              • 将矩阵分为四分之一区域,每次处理对称的四个格子,即 (r, c)(r, n-1-c)(m-1-r, c)(m-1-r, n-1-c)
                              • 对于每组对称的四个格子,计算 1 的数量 oneCount,翻转最少的格子即可满足回文。
                          1. 处理中间行和列
                              • 如果行数或列数是奇数,需要单独处理矩阵的中间行或列,确保其回文性。
                          1. 处理矩阵中心点
                              • 如果矩阵行列均为奇数,需要处理矩阵的正中心点,决定是否翻转它以满足目标条件。
                          1. 调整 1 的数量
                              • 在满足矩阵对称性后,根据 1 的总数量是否是 4 的倍数,进行进一步调整:
                                • 如果 1 的数量多了,需要通过适当翻转减少 1
                                • 如果 1 的数量少了,需要通过适当翻转增加 1
                          3. 代码实现
                          4. 代码解释
                          1. 矩阵对称性处理
                              • 遍历矩阵的左上四分之一部分,通过四个对称位置计算 1 的数量 oneCount
                              • 如果 oneCount > 2,需要翻转 4 - oneCount 个格子;否则翻转 oneCount 个格子。
                          1. 中间行和列的处理
                              • 通过 (rows + 1) / 2(cols + 1) / 2,有效处理了奇数行和奇数列的情况。
                          1. 调整 1 的总数量
                              • 在翻转的基础上,检查 1 的数量是否是 4 的倍数。
                              • 如果不是,进一步翻转使其满足条件。
                          5. 复杂度分析
                          1. 时间复杂度
                              • 遍历矩阵的四分之一部分,每次操作最多涉及 4 个元素,时间复杂度为
                          1. 空间复杂度
                              • 只使用了常数额外空间,空间复杂度为
                           

                          2024.11.17 T825 . 适龄的朋友

                          1. 问题分析
                          我们需要计算一个社交媒体平台上用户间发送好友请求的总数。好友请求的规则是:
                          1. 用户 x 不会向用户 y 发送请求,若满足以下条件之一:
                          1. 用户 x 不会向自己发送请求。
                          目标:返回符合条件的好友请求总数。
                          2. 解题思路
                          为高效计算好友请求,我们采用如下方法:
                          1. 排序数组
                              • 将数组 ages 按照年龄从小到大排序,便于使用双指针计算满足条件的用户对。
                          1. 双指针遍历
                              • 对于每个用户 i,使用双指针维护范围 [l, r],表示能接收来自用户 i 的好友请求的用户范围。
                              • 左边界 l:用户 ages[l] 必须满足
                              • 右边界 r:用户 ages[r] 必须满足
                          1. 计算好友请求
                              • 对于每个用户 i,满足条件的好友请求数量为 r - l
                              • 排除自己发送给自己的情况,因此不加 i
                          3. 代码实现
                          4. 代码解释
                          1. 排序数组
                              • 使用 sort(ages.begin(), ages.end()); 将年龄从小到大排序。
                          1. 双指针范围更新
                              • l:维护左边界,保证好友请求的接收者满足
                              • r:维护右边界,保证好友请求的接收者满足
                          1. 计算好友请求
                              • ans += r - l:在每个用户 i 发送好友请求时,范围 [l, r] 内的用户是有效的接收者。
                          1. 特殊处理
                              • 如果用户年龄小于 15,跳过该用户,因为其不会发送好友请求。
                          5. 复杂度分析
                          1. 时间复杂度
                              • 排序的时间复杂度为
                              • 双指针遍历整个数组一次,时间复杂度为
                              • 总时间复杂度为
                          1. 空间复杂度
                              • 排序操作为原地操作,不使用额外空间,空间复杂度为
                           

                          2024.11.18 T661 . 图片平滑器

                          661. 图片平滑器 - 力扣(LeetCode)
                          661. 图片平滑器 - 图像平滑器 是大小为 3 x 3 的过滤器,用于对图像的每个单元格平滑处理,平滑处理后单元格的值为该单元格的平均灰度。 每个单元格的  平均灰度 定义为:该单元格自身及其周围的 8 个单元格的平均值,结果需向下取整。(即,需要计算蓝色平滑器中 9 个单元格的平均值)。 如果一个单元格周围存在单元格缺失的情况,则计算平均灰度时不考虑缺失的单元格(即,需要计算红色平滑器中 4 个单元格的平均值)。 [https://assets.leetcode.com/uploads/2021/05/03/smoother-grid.jpg] 给你一个表示图像灰度的 m x n 整数矩阵 img ,返回对图像的每个单元格平滑处理后的图像 。   示例 1: [https://assets.leetcode.com/uploads/2021/05/03/smooth-grid.jpg] 输入:img = [[1,1,1],[1,0,1],[1,1,1]] 输出:[[0, 0, 0],[0, 0, 0], [0, 0, 0]] 解释: 对于点 (0,0), (0,2), (2,0), (2,2): 平均(3/4) = 平均(0.75) = 0 对于点 (0,1), (1,0), (1,2), (2,1): 平均(5/6) = 平均(0.83333333) = 0 对于点 (1,1): 平均(8/9) = 平均(0.88888889) = 0 示例 2: [https://assets.leetcode.com/uploads/2021/05/03/smooth2-grid.jpg] 输入: img = [[100,200,100],[200,50,200],[100,200,100]] 输出: [[137,141,137],[141,138,141],[137,141,137]] 解释: 对于点 (0,0), (0,2), (2,0), (2,2): floor((100+200+200+50)/4) = floor(137.5) = 137 对于点 (0,1), (1,0), (1,2), (2,1): floor((200+200+50+200+100+100)/6) = floor(141.666667) = 141 对于点 (1,1): floor((50+200+200+200+200+100+100+100+100)/9) = floor(138.888889) = 138   提示: * m == img.length * n == img[i].length * 1 <= m, n <= 200 * 0 <= img[i][j] <= 255
                          661. 图片平滑器 - 力扣(LeetCode)
                          1. 问题分析
                          给定一个表示图像灰度的 矩阵 img,需要对矩阵的每个单元格进行平滑处理。每个单元格的新值为该单元格及其周围最多 8 个单元格的灰度平均值(向下取整)。
                          • 如果某个单元格位于边界或角落,缺失的邻域单元格不参与计算。
                          • 需要返回经过平滑处理后的矩阵。
                          2. 解题思路
                          为实现图像平滑处理,我们可以采用以下步骤:
                          1. 偏移量数组
                              • 用一个固定的方向数组来表示当前单元格的 3x3 邻域,包括 8 个方向及自身的位置。
                          1. 遍历每个单元格
                              • 使用双重循环依次遍历矩阵中的每个单元格。
                          1. 处理每个单元格的邻域
                              • 使用方向数组,根据当前单元格的位置,确定其 3x3 邻域的有效单元格。
                              • 对有效的邻域单元格,累加灰度值,同时统计邻域单元格的数量。
                          1. 更新结果矩阵
                              • 对于每个单元格,计算灰度平均值,向下取整,并存储在结果矩阵中。
                          1. 返回结果矩阵
                              • 最终返回包含平滑处理后值的结果矩阵。
                          3. 代码实现
                          4. 代码解释
                          1. 方向数组
                              • 定义了 9 个方向的偏移量(包括当前单元格本身),用于定位邻域单元格。
                          1. 遍历矩阵
                              • 外层循环遍历每一行,内层循环遍历每一列,从而遍历整个矩阵。
                          1. 处理邻域单元格
                              • 对于每个单元格,计算其周围最多 8 个邻域单元格的灰度值总和。
                              • 检查邻域单元格是否在矩阵范围内,确保不越界。
                          1. 更新结果矩阵
                              • 将灰度平均值向下取整后存储到结果矩阵中。
                          1. 返回结果
                              • 返回完整处理后的矩阵。
                          5. 复杂度分析
                          1. 时间复杂度
                              • 遍历每个单元格(外层 ,内层 )。
                              • 对于每个单元格,检查其 3x3 邻域的 9 个方向(常数次操作)。
                              • 总时间复杂度为
                          1. 空间复杂度
                              • 结果矩阵占用 的额外空间。
                              • 方向数组为常量,额外空间复杂度为
                           

                          2024.11.19 T3243. 新增道路查询后的最短距离 I

                          3243. 新增道路查询后的最短距离 I - 力扣(LeetCode)
                          3243. 新增道路查询后的最短距离 I - 给你一个整数 n 和一个二维整数数组 queries。 有 n 个城市,编号从 0 到 n - 1。初始时,每个城市 i 都有一条单向道路通往城市 i + 1( 0 <= i < n - 1)。 queries[i] = [ui, vi] 表示新建一条从城市 ui 到城市 vi 的单向道路。每次查询后,你需要找到从城市 0 到城市 n - 1 的最短路径的长度。 返回一个数组 answer,对于范围 [0, queries.length - 1] 中的每个 i,answer[i] 是处理完前 i + 1 个查询后,从城市 0 到城市 n - 1 的最短路径的长度。   示例 1: 输入: n = 5, queries = [[2, 4], [0, 2], [0, 4]] 输出: [3, 2, 1] 解释: [https://assets.leetcode.com/uploads/2024/06/28/image8.jpg] 新增一条从 2 到 4 的道路后,从 0 到 4 的最短路径长度为 3。 [https://assets.leetcode.com/uploads/2024/06/28/image9.jpg] 新增一条从 0 到 2 的道路后,从 0 到 4 的最短路径长度为 2。 [https://assets.leetcode.com/uploads/2024/06/28/image10.jpg] 新增一条从 0 到 4 的道路后,从 0 到 4 的最短路径长度为 1。 示例 2: 输入: n = 4, queries = [[0, 3], [0, 2]] 输出: [1, 1] 解释: [https://assets.leetcode.com/uploads/2024/06/28/image11.jpg] 新增一条从 0 到 3 的道路后,从 0 到 3 的最短路径长度为 1。 [https://assets.leetcode.com/uploads/2024/06/28/image12.jpg] 新增一条从 0 到 2 的道路后,从 0 到 3 的最短路径长度仍为 1。   提示: * 3 <= n <= 500 * 1 <= queries.length <= 500 * queries[i].length == 2 * 0 <= queries[i][0] < queries[i][1] < n * 1 < queries[i][1] - queries[i][0] * 查询中没有重复的道路。
                          3243. 新增道路查询后的最短距离 I - 力扣(LeetCode)
                          1. 问题分析
                          给定一个有 城市的图,初始时每个城市 都有一条单向道路通往城市 。通过若干查询 queries[i] = [],动态地在图中增加从城市 到城市 的单向道路。目标是在每次查询后,找到从城市 0 到城市 的最短路径长度。
                          返回一个数组 answer,其中 answer[i] 表示处理完第 个查询后,从城市 0 到城市 的最短路径长度。
                          2. 解题思路
                          1. 初始化图和路径
                              • 初始情况下,每个城市 到城市 有一条单向道路,路径长度为
                              • 用数组 记录从城市 到每个城市的最短路径长度,初始化为
                              • 用邻接表 记录每个城市可以通过哪些前驱城市到达。
                          1. 处理查询
                              • 对于每个查询 queries[i] = [u, v],添加从城市 u 到城市 v 的单向道路。
                              • 更新从 0 到 的最短路径长度。
                              • 遍历所有可能到达的城市,使用前驱城市的最短路径动态更新当前城市的最短路径。
                          1. 动态更新最短路径
                              • 每次查询添加一条新道路后,利用邻接表和 dp 数组更新所有受影响的节点的最短路径长度。
                          1. 返回结果
                              • 每次查询结束后,将从 0 到 n-1 的最短路径长度记录到结果数组中。
                          3. 代码实现
                          4. 代码解释
                          1. 初始化
                              • prev[i] 用于存储城市 i 的前驱城市。
                              • dp[i] 表示从城市 0 到城市 i 的最短路径长度。
                              • 初始情况下,城市 i 只能通过 i-1 到达,路径长度为 i 。
                          1. 查询处理
                              • 每次新增一条 的道路后,更新 的值。
                              • 遍历从 v 开始的所有城市,利用前驱城市动态更新最短路径长度。
                          1. 结果存储
                              • 每次查询结束后,将 的值加入结果数组 res
                          5. 复杂度分析
                          1. 时间复杂度
                          • 初始构造 的时间为
                            • 每次查询中,更新从 v 开始的所有节点,最坏情况遍历 个节点,每个节点访问其所有前驱城市,时间为 ,其中 m 是前驱城市的平均数量。
                              • 总时间复杂度为 ,其中 q 是查询的数量。
                          1. 空间复杂度
                              • dpprev 的空间复杂度为
                              • 总空间复杂度为
                           

                          2024.11.20 T3244. 新增道路查询后的最短距离 II

                          3244. 新增道路查询后的最短距离 II - 力扣(LeetCode)
                          3244. 新增道路查询后的最短距离 II - 给你一个整数 n 和一个二维整数数组 queries。 有 n 个城市,编号从 0 到 n - 1。初始时,每个城市 i 都有一条单向道路通往城市 i + 1( 0 <= i < n - 1)。 queries[i] = [ui, vi] 表示新建一条从城市 ui 到城市 vi 的单向道路。每次查询后,你需要找到从城市 0 到城市 n - 1 的最短路径的长度。 所有查询中不会存在两个查询都满足 queries[i][0] < queries[j][0] < queries[i][1] < queries[j][1]。 返回一个数组 answer,对于范围 [0, queries.length - 1] 中的每个 i,answer[i] 是处理完前 i + 1 个查询后,从城市 0 到城市 n - 1 的最短路径的长度。   示例 1: 输入: n = 5, queries = [[2, 4], [0, 2], [0, 4]] 输出: [3, 2, 1] 解释: [https://assets.leetcode.com/uploads/2024/06/28/image8.jpg] 新增一条从 2 到 4 的道路后,从 0 到 4 的最短路径长度为 3。 [https://assets.leetcode.com/uploads/2024/06/28/image9.jpg] 新增一条从 0 到 2 的道路后,从 0 到 4 的最短路径长度为 2。 [https://assets.leetcode.com/uploads/2024/06/28/image10.jpg] 新增一条从 0 到 4 的道路后,从 0 到 4 的最短路径长度为 1。 示例 2: 输入: n = 4, queries = [[0, 3], [0, 2]] 输出: [1, 1] 解释: [https://assets.leetcode.com/uploads/2024/06/28/image11.jpg] 新增一条从 0 到 3 的道路后,从 0 到 3 的最短路径长度为 1。 [https://assets.leetcode.com/uploads/2024/06/28/image12.jpg] 新增一条从 0 到 2 的道路后,从 0 到 3 的最短路径长度仍为 1。   提示: * 3 <= n <= 105 * 1 <= queries.length <= 105 * queries[i].length == 2 * 0 <= queries[i][0] < queries[i][1] < n * 1 < queries[i][1] - queries[i][0] * 查询中不存在重复的道路。 * 不存在两个查询都满足 i != j 且 queries[i][0] < queries[j][0] < queries[i][1] < queries[j][1]。
                          3244. 新增道路查询后的最短距离 II - 力扣(LeetCode)
                          1. 问题分析
                          给定 个城市,初始时每个城市 都有一条单向道路连接到 。对于每个查询 ,新增一条从城市 到城市 的单向道路。我们需要在处理每个查询后,计算从城市 0 到城市 n-1 的最短路径长度。
                          特殊约束:查询保证不出现嵌套范围,即不存在
                          2. 解题思路
                          利用并查集处理动态连通性问题:
                          1. 初始化并查集,记录每个节点的父节点,并将每个节点指向自身。
                          1. 对于每个查询 queries[i] = [u, v],将 u 到 v-1 的所有节点合并到同一连通块。
                          1. 使用并查集的 find 操作定位节点的根节点,并通过路径压缩优化合并操作。
                          1. 每次查询完成后,记录连通块的数量作为结果。
                          3. 代码实现
                          4. 代码解释
                          并查集初始化
                          fa[i] 用于记录节点 i 的父节点,初始时每个节点的父节点为自身。
                          find 函数
                          实现路径压缩功能,快速找到节点的根节点,减少树的高度。
                          查询处理
                          对于查询 queries[i] = [u, v],合并 u 到 v-1 范围内的所有节点到 v-1 的连通块。
                          结果记录
                          每次查询后,将当前连通块数量保存到结果数组中。
                          5. 复杂度分析
                          时间复杂度
                          • 并查集操作的均摊时间复杂度为 ,其中 是反阿克曼函数,几乎为常数。
                          • 每次查询遍历并合并范围内的节点,最坏情况为
                          • 总时间复杂度为 ,其中 q 是查询数量。
                          空间复杂度
                          • 并查集数组 fa 和结果数组 ans 的空间复杂度为
                          • 总空间复杂度为
                           

                          2024.11.21 T3248. 矩阵中的蛇

                          1. 问题分析
                          在一个大小为 的矩阵中,蛇从位置 0(即左上角)开始,根据一系列命令移动。矩阵中的每个单元格由编号 唯一标识。
                          移动的规则如下:
                          • "UP":向上移动一行。
                          • "DOWN":向下移动一行。
                          • "LEFT":向左移动一列。
                          • "RIGHT":向右移动一列。
                          题目保证蛇在整个移动过程中不会超出矩阵的边界,因此不需要额外处理越界情况。
                          目标:根据所有的命令,返回蛇最终所在单元格的位置。
                          2. 解题思路
                          1. 初始化蛇的位置:
                              • 初始位置为左上角,即 (0, 0)。
                          1. 遍历命令数组:
                              • 根据命令调整当前的位置 ():
                                • "UP":行数减 1。
                                • "DOWN":行数加 1。
                                • "LEFT":列数减 1。
                                • "RIGHT":列数加 1。
                          1. 计算最终位置:
                              • 根据矩阵的编号规则,最终位置为
                          3. 代码实现
                          4. 代码解释
                          1. 初始化:
                              • rowcol 分别表示蛇在矩阵中的行号和列号,初始为 0。
                          1. 遍历命令:
                              • 对于每个命令,根据其类型调整行号或列号。
                          1. 计算结果:
                              • 根据 规则,计算蛇最终所在单元格的位置。
                          5. 复杂度分析
                          时间复杂度
                          • 遍历所有命令,时间复杂度为 ,其中 是命令的数量。
                          空间复杂度
                          • 常量空间用于存储行号和列号,空间复杂度为
                           

                          2024.11.22 T3233. 统计不是特殊数字的数字数量

                          1. 问题分析
                          我们需要找出区间 不是特殊数字的数字数量。
                          特殊数字的定义
                          • 一个数字是特殊数字,如果它的真因数只有两个。换句话说,特殊数字是一个质数的平方
                          问题可以转化为:
                          1. 计算区间内的特殊数字数量。
                          1. 用区间总数减去特殊数字的数量,得到非特殊数字的数量。
                          2. 解题思路
                          • 特殊数字的平方根必须是质数,因此需要一个函数 isPrime(n) 来判断一个数字是否是质数。
                          3. 代码实现
                          1. 复杂度分析
                              • 时间复杂度
                                • 检查质数的复杂度为 ,因为对于每个可能的质数 p ,质数检测的时间复杂度为
                                • 遍历的范围限制在 ,所以总体时间复杂度近似为
                              • 空间复杂度
                                • 算法仅使用了常量级的额外空间,没有使用动态分配的数组或其他数据结构。
                                • 因此,空间复杂度为
                           

                          2024.11.23 T3238. 求出胜利玩家的数目

                          1. 问题分析
                            1. 给定一个整数 n 表示玩家的数量,以及一个二维数组 pick,其中 表示玩家 获得了一个颜色为 的球。
                              对于每个玩家 i
                              • 如果玩家获得了某种颜色球的数量严格大于 i 的话,那么称玩家 i胜利玩家
                              • 例如:
                                • 玩家 0 只要获得了任意球就是胜利玩家。
                                • 玩家 1 需要获得至少 2 个相同颜色的球才能成为胜利玩家。
                                • 玩家 i 需要获得至少 i + 1 个相同颜色的球才能成为胜利玩家。
                              目标:计算游戏中胜利玩家的数量。
                          2. 解题思路
                          1. 使用哈希表统计每个玩家的球数量
                              • 使用一个大小为 n 的数组 colorCount,每个元素是一个哈希表,用于存储每个玩家各颜色球的数量。
                          1. 遍历 pick 数组,统计球数量
                              • 遍历 pick,对每个玩家的球颜色进行计数。
                          1. 判断胜利玩家
                              • 遍历每个玩家,检查每种颜色的球数量是否满足条件,如果满足即认为是胜利玩家。
                          1. 计数胜利玩家
                              • 统计满足条件的玩家数量。
                          3. 代码实现
                          4. 代码解释
                          1. 数据结构选择
                              • 使用一个大小为 n 的数组 colorCount,其中每个元素是一个 unordered_map,用于记录每个玩家的各颜色球的数量。
                          1. 遍历 pick 数组
                              • 对于每个 pick[i],将相应的玩家和球的颜色存储到 colorCount 中,更新计数。
                          1. 判断胜利玩家
                              • 对每个玩家 i,遍历其所有颜色的球数量,如果某种颜色的球数量严格大于 i,则该玩家为胜利玩家,直接跳出检查。
                          1. 返回结果
                              • 统计所有满足条件的玩家数量并返回。
                          5. 复杂度分析
                          • 时间复杂度
                            • 遍历 pick 数组的时间复杂度为 ,其中 m 是 pick 数组的长度。
                            • 遍历每个玩家及其颜色球的哈希表的时间复杂度在最坏情况下接近
                            • 总时间复杂度为
                          • 空间复杂度
                            • 使用了大小为 n 的数组,每个元素是一个哈希表,哈希表的大小取决于 pick 中颜色的种类。
                            • 最坏情况下,空间复杂度为
                           

                          2024.11.24 T632 . 最小区间

                          1. 问题分析
                          给定 个非递减排列的整数列表,需要找到一个最小区间,使得 个列表中的每个列表至少有一个数包含在区间内。
                          规则
                          1. 区间的大小由 决定。
                          1. 如果多个区间大小相同,则选择起点较小的区间。
                          2. 解题思路
                          1. 展开和排序
                              • 将所有列表的元素展开到一个数组中,记录每个元素的值和它所属的列表编号。
                              • 对这个数组按值排序,以便后续使用滑动窗口处理。
                          1. 滑动窗口
                              • 使用双指针(即滑动窗口)遍历排序后的数组。
                              • 窗口的目标是覆盖 个列表中的所有列表。
                              • 每次窗口包含所有列表时,计算当前区间的大小并更新结果。
                          1. 窗口收缩
                              • 当窗口满足条件时,尝试收缩窗口以寻找更小的区间。
                              • 移动左指针,同时维护窗口内包含的列表数量。
                          3. 代码实现
                          4. 代码解释
                          1. 展开和排序
                              • 将所有列表的元素连同其所属的组号存入数组 ordered,并按值排序。这样可以按照数值大小处理所有元素,同时追踪其来源。
                          1. 滑动窗口
                              • 使用 groupCount 记录窗口中每个列表的覆盖情况。
                              • 每当窗口覆盖了 个列表后,尝试更新最小区间。
                          1. 区间更新条件
                              • 优先选择更小的区间(currentRange 更小)。
                              • 如果区间大小相同,选择起点较小的区间。
                          1. 窗口收缩
                              • 左指针右移,移出窗口的组若在窗口内不再出现,减少覆盖的列表数量。
                          5. 复杂度分析
                          • 时间复杂度
                              1. 展开和排序:,其中 是所有列表元素的总数。
                              1. 滑动窗口:每个元素至多进出窗口一次,时间复杂度为
                              1. 总时间复杂度:
                          • 空间复杂度
                            • 使用了额外的数组 ordered 和哈希表 groupCount,空间复杂度为 ,其中 是列表的数量。
                           

                          2024.11.25 T743 . 网络延迟时间

                          1. 问题分析
                          我们需要找到从某个节点 发出的信号传播到所有其他节点所需的最短时间。如果某些节点无法收到信号,则返回 -1。
                          输入
                          • times:表示有向边的传递时间,格式为 ,其中:
                            • 是源节点。
                            • 是目标节点。
                            • 是传递时间。
                          • :节点的总数。
                          • :信号发出的起始节点。
                          输出
                          • 返回信号传播到所有节点的最短时间,如果无法到达所有节点,返回 -1。
                          2. 解题思路
                          1. 构建图的邻接表
                              • 将有向边存储为邻接表结构,便于快速查询每个节点的邻居和边权。
                          1. Dijkstra 算法求最短路径
                              • 使用优先队列(最小堆)维护未访问节点的最短路径。
                              • 每次从优先队列中取出当前距离最短的节点进行松弛操作,更新其邻居的最短路径。
                          1. 检查结果
                              • 如果最终某些节点的最短距离仍为无穷大,表示这些节点无法到达,返回 -1。
                              • 否则,返回所有节点中最远的最短路径。
                          3. 代码实现
                          4. 代码解释
                          1. 构建邻接表
                              • 将输入的边信息存储为邻接表,graph[u] 存储从节点 出发的所有邻居及其边权。
                          1. 初始化
                              • distances 数组存储从起点 到各节点的最短距离,初始值为无穷大,起点 的距离为 0。
                              • 使用最小堆 minHeap 优化 Dijkstra 算法。
                          1. Dijkstra 算法
                              • 每次从堆中取出当前最短距离的节点。
                              • 遍历其邻居,更新未访问节点的最短距离,并将更新后的节点重新加入堆。
                          1. 结果检查
                              • 如果某些节点的距离仍为无穷大,表示它们无法到达,返回 -1。
                              • 否则,返回 distances 数组中的最大值。
                          5. 复杂度分析
                          • 时间复杂度
                            • 构建图的时间复杂度为 ,其中 是边的数量。
                            • Dijkstra 算法的时间复杂度为 ,其中 是节点的数量。
                            • 总时间复杂度为
                          • 空间复杂度
                            • 邻接表的空间复杂度为
                            • distances 数组的空间复杂度为
                            • 优先队列的空间复杂度为
                            • 总空间复杂度为
                           

                          2024.11.26 T3206. 交替组 I

                          2024.11.27 T3208. 交替组 II

                          3208. 交替组 II - 力扣(LeetCode)
                          3208. 交替组 II - 给你一个整数数组 colors 和一个整数 k ,colors表示一个由红色和蓝色瓷砖组成的环,第 i 块瓷砖的颜色为 colors[i] : * colors[i] == 0 表示第 i 块瓷砖的颜色是 红色 。 * colors[i] == 1 表示第 i 块瓷砖的颜色是 蓝色 。 环中连续 k 块瓷砖的颜色如果是 交替 颜色(也就是说除了第一块和最后一块瓷砖以外,中间瓷砖的颜色与它 左边 和 右边 的颜色都不同),那么它被称为一个 交替 组。 请你返回 交替 组的数目。 注意 ,由于 colors 表示一个 环 ,第一块 瓷砖和 最后一块 瓷砖是相邻的。   示例 1: 输入:colors = [0,1,0,1,0], k = 3 输出:3 解释: [https://assets.leetcode.com/uploads/2024/06/19/screenshot-2024-05-28-183519.png] 交替组包括: [https://assets.leetcode.com/uploads/2024/05/28/screenshot-2024-05-28-182448.png][https://assets.leetcode.com/uploads/2024/05/28/screenshot-2024-05-28-182844.png][https://assets.leetcode.com/uploads/2024/05/28/screenshot-2024-05-28-183057.png] 示例 2: 输入:colors = [0,1,0,0,1,0,1], k = 6 输出:2 解释: [https://assets.leetcode.com/uploads/2024/06/19/screenshot-2024-05-28-183907.png] 交替组包括: [https://assets.leetcode.com/uploads/2024/06/19/screenshot-2024-05-28-184128.png][https://assets.leetcode.com/uploads/2024/06/19/screenshot-2024-05-28-184240.png] 示例 3: 输入:colors = [1,1,0,1], k = 4 输出:0 解释: [https://assets.leetcode.com/uploads/2024/06/19/screenshot-2024-05-28-184516.png]   提示: * 3 <= colors.length <= 105 * 0 <= colors[i] <= 1 * 3 <= k <= colors.length
                          3208. 交替组 II - 力扣(LeetCode)
                          两道题一个思路,放在一起说了就
                          1. 问题分析
                            1. 给定一个由红色和蓝色瓷砖组成的环,环中的每一块瓷砖由数组 表示, 为 0 或 1,分别代表红色和蓝色。我们的任务是找到环中长度为 且瓷砖颜色交替的连续子序列的个数。环的特点意味着数组的头尾相连,这为交替组的查找带来了额外的复杂性。
                          1. 解题思路
                            1. 为了解决这个问题,我们需要遍历整个环,找到所有满足条件的交替组。由于数组是环状的,我们可以将数组模拟成长度为 2 * n 的数组,其中 ncolors 的长度,这样可以处理好首尾相连的问题。
                              具体思路如下:
                            2. 通过遍历数组的两倍长度来模拟环的结构。
                            3. 在每次遍历时,检查当前瓷砖是否与前一块的颜色相同。如果相同,重置当前交替组长度计数。
                            4. 如果当前计数大于或等于 k,并且已经遍历到大于等于 n 的位置,则可以增加交替组的数量。
                          1. 代码实现
                            1. 代码解释
                              1. n = colors.size(); - 获取瓷砖的总数量。
                              2. int ans = 0, cnt = 0; - 初始化交替组的数量 ans 和当前计数 cnt
                              3. for (int i = 0; i < n * 2; i++) - 遍历整个环的两倍长度,以处理首尾相连。
                              4. if (i > 0 && colors[i % n] == colors[(i - 1) % n]) - 如果当前瓷砖和前一块颜色相同,重置当前计数。
                              5. cnt++ - 否则增加当前计数。
                              6. ans += i >= n && cnt >= k; - 如果已经遍历到长度 n 以上并且当前计数大于等于 k,增加交替组的数量。
                              7. return ans; - 返回交替组的总数量。
                            1. 复杂度分析
                                • 时间复杂度:,其中 n 为瓷砖的数量。由于我们遍历了 的长度,因此时间复杂度仍然是线性。
                                • 空间复杂度:,只使用了常数个额外的变量,空间复杂度为常数。
                             

                            2024.11.28 T3250. 单调数组对的数目 I

                            2024.11.29 T3251. 单调数组对的数目 II

                            两个题只有数据范围不一样,一起写了
                            1. 问题分析
                              1. 给定一个长度为 n 的正整数数组 nums,要求找到所有满足条件的单调数组对 (arr1, arr2) 的数目。
                                • arr1 是单调非递减数组,即 arr1[0] <= arr1[1] <= ... <= arr1[n - 1]
                                • arr2 是单调非递增数组,即 arr2[0] >= arr2[1] >= ... >= arr2[n - 1]
                                • 对于所有 0 <= i <= n - 1,满足 arr1[i] + arr2[i] == nums[i]
                                由于答案可能很大,最后需要返回答案对 10^9 + 7 取余后的结果。
                            1. 解题思路
                              1. 这道题涉及动态规划和前缀和的技巧。
                              2. 动态规划思路
                                  • 使用一个 dp 数组记录当前能够形成有效解的组合数。
                                  • 初始时,可以通过遍历 nums[0] 来确定初始的可能组合数量。
                              3. 前缀和优化
                                  • 使用前缀和计算方式快速更新 dp 数组,避免逐个累加导致的复杂度过高。
                              4. 滑动窗口
                                  • 每次迭代时,更新 dp 数组中的有效部分,并将无效部分清零,保持 dp 数组的有效性。
                            1. 代码实现
                              1. 代码解释
                                1. 初始化部分
                                    • MOD 是取余数的模数,防止结果溢出。
                                    • maxValue 是数组 nums 中的最大值,用于定义 dp 数组的大小。
                                    • 初始化 dp 数组,使得 dp[i] 表示第 i 个位置上可能的组合数。
                                2. 动态规划迭代
                                    • 对于每一个 nums[i],根据上一个位置的 dp 值来更新当前可能的组合数。
                                    • 使用前缀和的方式来高效地计算新的 dp 值,减少时间复杂度。
                                3. 滑动窗口更新 dp 数组
                                    • 更新 dp 中有效的部分,并将不需要的部分清零,以保证在下一轮迭代时不受干扰。
                                4. 返回结果
                                    • 最终返回 dp 数组中所有元素的和,并对 MOD 取余。
                              1. 复杂度分析
                                  • 时间复杂度:假设 maxValue 为数组中的最大值,时间复杂度约为 O(n * maxValue),其中 n 为数组的长度。代码中主要开销来自于遍历和前缀和计算。
                                  • 空间复杂度:空间复杂度为 O(maxValue),用于存储动态规划的 dp 数组。
                               

                              2024.11.30 T3232. 判断是否可以赢得数字游戏

                              1. 问题分析
                                1. 给定一个正整数数组 nums,Alice 和 Bob 正在玩一个游戏。游戏规则如下:
                                  • Alice 可以从 nums 中选择所有个位数或所有两位数,剩余的数字归 Bob 所有。
                                  • 如果 Alice 所选数字之和严格大于 Bob 的数字之和,则 Alice 获胜。
                                  我们的目标是判断 Alice 是否能赢得这场游戏,如果 Alice 能赢,返回 true,否则返回 false
                              1. 解题思路
                                1. 这道题主要考察如何分配数字,确保 Alice 获胜。
                                2. 分配数字的原则
                                    • 由于 Alice 只能选择所有个位数或所有两位数,而我们希望 Alice 获胜,所以需要尽量让 Alice 获得和更大的数字。
                                3. 思路分析
                                    • 将数组 nums 排序后,Alice 选择所有个位数(即小于 10 的数字),Bob 选择剩余的数字。
                                    • Alice 获胜的条件是 Alice 的选择数字之和严格大于 Bob 的选择数字之和。
                                4. 判断条件
                                    • 如果 Alice 和 Bob 的和不同,则 Alice 赢得比赛;如果 Alice 和 Bob 的和相等,则 Alice 不能获胜。
                              1. 代码实现
                              1. 代码解释
                                1. 排序
                                    • 首先对 nums 数组进行升序排序,目的是方便后续区分 Alice 和 Bob 的选择。
                                2. 遍历数组并分配数字
                                    • 遍历排序后的 nums 数组,对于每个小于 10 的数字,添加到 sum1(Alice 的得分);其余的数字添加到 sum2(Bob 的得分)。
                                3. 判断 Alice 是否获胜
                                    • 最后比较 sum1sum2 的值,如果 sum1 大于 sum2,则 Alice 获胜,返回 true;否则,返回 false
                              1. 复杂度分析
                                  • 时间复杂度,其中 n 为数组 nums 的长度,主要的时间复杂度来源于排序操作。
                                  • 空间复杂度,只使用了常数级的额外空间。
                              上一篇
                              Leetcode刷题——10月
                              下一篇
                              Leetcode刷题——12月

                              评论
                              Loading...