递枕头

标签: 数学 模拟

难度: Easy

n 个人站成一排,按从 1n 编号。

最初,排在队首的第一个人拿着一个枕头。每秒钟,拿着枕头的人会将枕头传递给队伍中的下一个人。一旦枕头到达队首或队尾,传递方向就会改变,队伍会继续沿相反方向传递枕头。

  • 例如,当枕头到达第 n 个人时,TA 会将枕头传递给第 n - 1 个人,然后传递给第 n - 2 个人,依此类推。

给你两个正整数 ntime ,返回 time 秒后拿着枕头的人的编号。

示例 1:

输入:n = 4, time = 5
输出:2
解释:队伍中枕头的传递情况为:1 -> 2 -> 3 -> 4 -> 3 -> 2 。
5 秒后,枕头传递到第 2 个人手中。

示例 2:

输入:n = 3, time = 2
输出:3
解释:队伍中枕头的传递情况为:1 -> 2 -> 3 。
2 秒后,枕头传递到第 3 个人手中。

提示:

  • 2 <= n <= 1000
  • 1 <= time <= 1000

Submission

运行时间: 14 ms

内存: 16.0 MB

class Solution:
    def passThePillow(self, n: int, time: int) -> int:
        i = 1
        flag = 1
        while time > 0:
            time -= 1
            i += flag
            if i == n or i == 1:
                flag *= -1
        return i

Explain

该题解采用模拟整个过程的方式来解决问题。初始化时,第一个人拿着枕头(i = 1),且枕头的传递方向为正向(flag = 1)。在每个时间单位中,根据当前的方向移动枕头到下一个人。当枕头到达队首或队尾时,枕头的传递方向将反转(通过将flag乘以-1实现)。这个过程持续进行直到达到指定的时间。最后返回枕头所在的人的编号。

时间复杂度: O(time)

空间复杂度: O(1)

# 定义Solution类

class Solution:
    def passThePillow(self, n: int, time: int) -> int:
        i = 1  # 枕头初始位置在第一个人
        flag = 1  # 枕头的传递方向,1为向右(增加),-1为向左(减少)
        while time > 0:  # 持续time秒
            time -= 1  # 每次循环减少1秒
            i += flag  # 根据方向移动枕头
            if i == n or i == 1:  # 如果枕头到达队尾或队首
                flag *= -1  # 改变传递方向
        return i  # 返回time秒后枕头的位置

Explore

模拟方法是解决这个问题的直观方法,适用于time相对较小的情况。然而,如果time非常大,这种方法可能会效率较低,因为它需要逐步模拟每一次枕头的传递。存在更优的方法,比如通过计算而非模拟来直接确定结束时枕头的位置。可以通过数学方法计算出枕头每次达到队尾或队首需要的步数,直接计算出经过time步后枕头的位置,大幅减少必要的计算次数。

在题解中,方向转换是通过检查枕头当前位置是否达到队伍的两端来实现的。具体的条件是:如果枕头位置`i`等于1(队首)或等于`n`(队尾),就将传递方向变量`flag`的符号反转(即`flag *= -1`)。这种方法确保了每当枕头传到两端时,都能正确地反转方向,从而继续传递。

如果`n`非常大而`time`相对较小,模拟方法的效率仍然是可接受的,因为模拟的次数主要取决于`time`而不是`n`。在这种情况下,模拟方法由于其简单性和直观性,可能是一个不错的选择。如果时间步数`time`非常大,可以考虑使用数学计算方法来优化,通过计算而非逐步模拟来快速得到最终位置,尤其是当`time`远大于`n`时,预计算周期性模式可能会有用。

使用`flag`变量来控制方向是一种非常直观和简单的方法,易于理解和实现。另一种可能的实现方式是使用队列或双端队列(deque)数据结构,通过将队首或队尾的元素移动到另一端来模拟枕头的传递。这种方法在处理枕头传递方向上可能更自然地映射到数据结构操作上,但在实际代码实现上可能会稍显复杂。