BM97 旋转数组¶
中等通过率 42.44%python3核心代码模式样例通过牛客 AC数组
讲解章节:序列与数组
一句话
先整体翻转,再分别翻转前 m 个与后 n-m 个,原地 O(1) 空间。
解题思路¶
这题考什么¶
「拼接两段」这个操作能用三次翻转实现,从而绕开额外数组。 右移 m 位就是把数组切成 A = a[0 : n-m] 与 B = a[n-m : n], 结果要变成 B + A。题面不许用另外的数组,于是(把 reverse 记作 rev):
成立的依据是 rev(X Y) = rev(Y) rev(X):整体一翻,两段的相对次序就换了, 只是各自内部也倒了过来,再把两段各翻一次即可复原。
具体到示例 [1,2,3,4,5,6],m = 2:
翻转本身用左右双指针交换,a[i], a[j] = a[j], a[i] 原地完成, 全程只用常数个额外变量。每个元素恰好被碰两次,时间 O(n)。
数据规模与复杂度¶
0 < n <= 100,0 <= m <= 1000,时限「其他语言 2 秒」, 题面进阶要求空间 O(1)、时间 O(n)。三次翻转合计 O(n) 次交换、O(1) 额外空间。 直接写 a[:] = a[-m:] + a[:-m] 也对,但那要临时构造两段切片, 空间是 O(n),拿不到进阶分。
坑在哪¶
- m 必须先对 n 取模。题面里 m 最大到 1000 而 n 只有 100, 右移 n 的整数倍等于没移。不取模的话,切分点 n - m 会变成负数, 两段的划分整个错位,翻转范围也跟着越界或空转。
- 取模前要先挡住 n <= 0,否则 m %= n 是除零错误。
- 取模后 m == 0 时可以直接返回:不返回也无妨(三次翻转退化成 「整体翻两次再翻整段」,结果仍是原数组),但那是三趟无用的搬运。
- reverse 收的是闭区间端点,三次调用的范围分别是 [0, n-1]、[0, m-1]、[m, n-1]。把哪一个的右端写成开区间, 都会漏翻或多翻一个元素;尤其 [0, m-1] 的 m-1 与 [m, n-1] 的 m 必须严丝合缝地首尾相接,中间既不能重叠也不能留空。
- 签名要求返回 integer[],所以原地改完之后要把 a 本身返回, 不能只改不返回。
参考实现¶
[:octicons-arrow-left-16: BM96](BM96.md) [BM98 :octicons-arrow-right-16:](BM98.md)