青训营X豆包MarsCode技术训练营第三课 | 豆包MarsCode AI 刷题

39 阅读4分钟

石子移动问题

问题描述

小S正在玩一个关于石子的游戏,给定了一些石子,它们位于一维数轴的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子。

在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。

你需要帮助小S找到可以移动的最大次数。

思路

一、思路概述

该代码主要是要解决与一组石子相关的某种移动次数计算问题。整体思路是先对输入的石子数组进行排序,然后分别计算出最大移动次数和最小移动次数(虽然代码最后返回的是最大移动次数,但计算最小移动次数的过程也是完整的),通过遍历数组并结合特定条件判断来实现这些计算。

二、代码结构

1. solution方法

  • 输入参数:接受一个整数数组 stones,代表一组石子的某种属性值(可能是位置或其他相关量)。
  • 返回值:返回一个整数,表示计算得出的最大移动次数。
  • 内部逻辑步骤: - 首先判断输入数组的长度,如果长度为 1,则直接返回 0,因为只有一个石子不存在移动的概念。 - 接着使用 Arrays.sort(stones) 对输入的石子数组进行排序,以便后续基于排序后的顺序进行计算。
    • 计算最大移动次数:通过 int maxMoves = Math.max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2); 计算。这里是比较数组中最后一个石子与第二个石子的差值,以及倒数第二个石子与第一个石子的差值,取较大值后再减去 (n - 2),其中 n 是数组的长度,这样得出的结果就是最大移动次数的一种计算方式(具体含义可能与石子的排列和移动规则相关)。
    • 计算最小移动次数(使用滑动窗口): - 初始化 minMovesInteger.MAX_VALUE,用于后续不断更新找到最小的移动次数。 - 通过一个外层循环 for (int i = 0; i < n; i++) 遍历排序后的石子数组。 - 在内层有一个 while 循环 while (j < n && stones[j] - stones[i] + 1 <= n),用于控制一个滑动窗口的移动。这个窗口的起始位置由外层循环的 i 确定,结束位置由 j 确定,目的是确保窗口内最多有 n 个石子(通过 stones[j] - stones[i] + 1 <= n 这个条件来保证)。 - 在每次滑动窗口移动后,计算当前窗口内已经在合适位置的石子数量 alreadyInPlace = j - i。 - 如果 alreadyInPlace == n - 1stones[j - 1] - stones[i] + 1 == n - 1,说明遇到了一种特殊情况,此时将 minMoves 更新为 Math.min(minMoves, 2)。 - 否则,将 minMoves 更新为 Math.min(minMoves, n - alreadyInPlace),这是一般情况下计算最小移动次数的方式。
  • 2. main方法 - 在 main 方法中,通过调用 solution 方法并传入不同的测试数组 new int[]{7, 4, 9}new int[]{6, 5, 4, 3, 10}new int[]{1, 2, 3, 4, 5} 等来验证 solution 方法的计算结果是否符合预期,通过 System.out.println(solution(new int[]{7, 4, 9}) == 2) 等类似语句输出验证结果。

总结

首先,处理边界情况,若数组长度为 1 则直接返回 0,因单石子不存在移动概念。

接着对数组排序以便后续计算。计算最大移动次数时,先算出数组最后一个石子与第二个石子的差值、倒数第二个石子与第一个石子的差值,取两者最大值再减去数组长度减 2 的值。

计算最小移动次数采用滑动窗口法。初始化最小移动次数为极大值,通过外层for循环遍历数组确定窗口起始位置,内层while循环依条件控制窗口结束位置以保证窗口内石子数合理。每次移动窗口后,根据窗口内石子数量判断特殊情况并更新最小移动次数,特殊情况按特定规则处理,一般情况则用数组长度减去窗口内石子数量来更新,最终返回最大移动次数。