递枕头
LeetCode 2582. 递枕头:数学规律与 O(1) 解法
题目描述
有 n 个人站成一排,编号从 1 到 n。枕头最初在第 1 个人手里。每一秒,枕头会传递给相邻的人。如果枕头到达了队首(第 1 个人)或队尾(第 n 个人),传递方向就会反转。给你 n 和 time,返回 time 秒后枕头在谁手里。
示例:
- 输入:
n = 4, time = 9,输出:4 - 输入:
n = 3, time = 2,输出:3
解题思路
直接模拟传递过程当然可以,但 time 可能很大,模拟会超时。观察传递规律,可以发现它是一个周期性的往返运动。
以 n = 4 为例,枕头的位置随时间变化如下:
1 | 时间: 0 1 2 3 4 5 6 7 8 9 10 11 12 |
可以看到,位置序列在 1 和 4 处会发生“折返”。如果去掉折返时重复的端点,可以把序列拉直成一个更规整的周期:
1 | 1 -> 2 -> 3 -> 4 -> 4 -> 3 -> 2 -> 1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 3 -> 2 -> 1 |
这样每个周期的长度是 2 * n?实际上,从 1 到 4 再回到 1 是一个完整的来回。原始来回包含 2 * (n - 1) + 1 = 2n - 1 个时间点。但为了用数学公式统一计算,我们把转折点(1 和 n)重复一次,使得每个“半周期”的长度为 n。
关键公式
计算需要多少个“半周期”
每个半周期长度是n - 1(因为从1到n需要n - 1步)。
设ch为经过的完整半周期数(即从一端走到另一端的次数),则:1
ch = time / (n - 1)
如果
time不能整除n - 1,说明还需要再走一次半周期才能覆盖time,所以向上取整:1
2
3
4if not ch.is_integer():
ch = int(ch) + 1
else:
ch = int(ch)计算去重后序列的总长度
每经过一个半周期,序列中会增加n个点,但相邻半周期之间会重复一个端点,所以总长度为:1
arry = ch * n - (ch - 1)
根据
ch的奇偶性确定最终位置- 如果
ch是偶数,说明当前正处在“从 n 返回 1”的方向上,位置为arry - time。 - 如果
ch是奇数,说明当前正处在“从 1 走向 n”的方向上,位置为n - (arry - time) + 1。
- 如果
边界情况
当 time < n 时,枕头还没有到达队尾,直接返回 time + 1 即可,因为初始位置是 1,第 time 秒在 time + 1。
代码实现
1 | class Solution: |
示例解析:n = 4, time = 9
time = 9,n = 4,不满足time < n,进入else。ch = 9 / (4 - 1) = 9 / 3 = 3.0,是整数,所以ch = 3。arry = 3 * 4 - (3 - 1) = 12 - 2 = 10。ch是奇数(3),走else分支:return 4 - (10 - 9) + 1 = 4 - 1 + 1 = 4。- 结果正确,第 9 秒枕头在第
4个人手里。
复杂度分析
- 时间复杂度:
O(1),只进行了常数次算术运算。 - 空间复杂度:
O(1),仅使用了几个变量。
总结
这道题的核心是找到传递过程的周期性规律。通过将往返运动“拉直”,并用数学公式计算经过的半周期数,可以避免模拟整个传递过程,实现 O(1) 的高效解法。需要注意边界条件(如 time < n)和浮点数除法的取整处理。
希望这篇题解对你有帮助!如果觉得有用,欢迎点赞收藏。