题目链接
题目描述
这是一个函数实现题。有 n
个小朋友(编号 1 到 n
)围成一圈,每个小朋友的衣服上有一个数字,记录在数组 a
中(a[i]
是第 i+1
个小朋友的数字)。
旺仔哥哥从 1 号小朋友旁边开始,总共移动 m
次。每次移动的规则如下:
- 查看当前所在位置小朋友衣服上的数字,设为
k
。 - 逆时针走过
k
个小朋友,到达新的位置。
你需要实现 stopAtWho
函数,计算并返回经过 m
次移动后,旺仔哥哥最终所在位置的小朋友的编号。
示例:
- 输入:
a = [2, 1, 4, 5, 2, 3]
,m = 3
- 你的函数应返回:
5
- 说明:
- 开始在 1 号位。1 号数字是 2。逆时针走 2 位,到达 5 号位。
- 当前在 5 号位。5 号数字是 2。逆时针走 2 位,到达 3 号位。
- 当前在 3 号位。3 号数字是 4。逆时针走 4 位,到达 5 号位。
- 移动结束,最终在 5 号位。
解题思路
本题是一个纯粹的模拟题,我们只需要按照题目描述的规则,一步一步地模拟旺仔哥哥的移动过程即可。核心在于处理环形结构中的索引计算。
-
状态表示与索引:
- 小朋友总数
n
就是数组a
的长度。 - 小朋友编号是 1-based (1 to
n
),而数组索引是 0-based (0 ton-1
)。我们需要在它们之间进行转换。一个小朋友的编号为p
,对应的数组索引是p-1
。 - 我们可以用一个变量
current_idx
来表示旺仔哥哥当前位置的 0-based 索引。初始时,他在 1 号小朋友旁边,所以current_idx
初始化为0
。
- 小朋友总数
-
模拟移动:
- 我们需要一个循环,执行
m
次,代表m
次移动。 - 在每次循环内部,我们执行一次移动操作。
- 我们需要一个循环,执行
-
计算新位置 (核心):
- 获取步数: 首先,根据当前位置
current_idx
,从数组a
中获取步数:steps = a[current_idx]
。 - 环形移动: 由于小朋友是围成一圈的,移动
steps
步和移动steps % n
步的效果是一样的。为了防止steps
过大,我们先取模:effective_steps = steps % n
。 - 逆时针计算: 逆时针移动相当于在数组中向左移动,即索引减小。
new_idx = current_idx - effective_steps
。 - 处理负数索引 (回绕):
current_idx - effective_steps
的结果可能是负数。例如,在 6 个小朋友的圈中,从索引 0 逆时针移动 2 步 (0 - 2 = -2
),应该到达索引 4。为了正确处理这种情况,我们使用一个健壮的模运算技巧:new_idx = (current_idx - effective_steps + n) % n
- 先加上
n
可以保证即使current_idx - effective_steps
为负,结果也会变为正数。 - 最后对
n
取模,将索引规范到[0, n-1]
的范围内。
- 先加上
- 获取步数: 首先,根据当前位置
-
最终结果:
- 循环
m
次后,我们得到的current_idx
就是最终位置的 0-based 索引。 - 将其转换回 1-based 的小朋友编号
current_idx + 1
,并作为函数结果返回。
- 循环
代码
注意:以下是你需要提交的全部内容,即在牛客网给定的模板中填写的代码。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 计算出旺仔哥哥最后会站在哪位小朋友旁边
* @param a int整型vector 第 i 个小朋友的数字是 a_i
* @param m int整型 表示旺仔哥哥的移动次数
* @return int整型
*/
int stopAtWho(std::vector<int>& a, int m) {
int n = a.size();
if (n == 0) {
return 0; // 或者根据题意处理异常情况
}
long long current_idx = 0; // 使用 long long 防止中间计算溢出,虽然本题int足够
for (int i = 0; i < m; ++i) {
long long steps = a[current_idx];
// 逆时针移动,即索引减小。加上n再取模以处理负数情况。
current_idx = (current_idx - (steps % n) + n) % n;
}
return current_idx + 1; // 返回1-based的编号
}
};
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 计算出旺仔哥哥最后会站在哪位小朋友旁边
* @param a int整型一维数组 第 i 个小朋友的数字是 a_i
* @param m int整型 表示旺仔哥哥的移动次数
* @return int整型
*/
public int stopAtWho(int[] a, int m) {
int n = a.length;
if (n == 0) {
return 0; // 或者根据题意处理异常
}
// Java的%对于负数结果也是负数,所以需要特殊处理
// currentIdx - steps % n 的结果可能是负数
int currentIdx = 0;
for (int i = 0; i < m; i++) {
int steps = a[currentIdx];
int effectiveSteps = steps % n;
// (currentIdx - effectiveSteps % n + n) % n 是一种健壮的写法
currentIdx = (currentIdx - effectiveSteps + n) % n;
}
return currentIdx + 1; // 转换为1-based编号
}
}
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 计算出旺仔哥哥最后会站在哪位小朋友旁边
# @param a int整型一维数组 第 i 个小朋友的数字是 a_i
# @param m int整型 表示旺仔哥哥的移动次数
# @return int整型
#
class Solution:
def stopAtWho(self, a: List[int], m: int) -> int:
n = len(a)
if n == 0:
return 0 # 题目保证 n >= 1,此为防御性代码
current_idx = 0
for _ in range(m):
steps = a[current_idx]
# Python的 % 运算符对负数处理得很优雅,能直接得到正确结果
# -1 % 6 = 5, -2 % 6 = 4 etc.
current_idx = (current_idx - steps) % n
return current_idx + 1 # 转换为1-based编号
算法及复杂度
- 算法: 循环模拟。
- 时间复杂度:
,其中
是移动的总次数。我们进行
M
次循环,每次循环内部的操作(取数、模运算、赋值)都是的。
- 空间复杂度:
。我们只使用了常数级别的额外空间来存储当前位置、循环变量等。