1806. 还原排列的最少操作步数
题目描述
给你一个偶数 n
,已知存在一个长度为 n
的排列 perm
,其中 perm[i] == i
(下标 从 0 开始 计数)。
一步操作中,你将创建一个新数组 arr
,对于每个 i
:
- 如果
i % 2 == 0
,那么arr[i] = perm[i / 2]
- 如果
i % 2 == 1
,那么arr[i] = perm[n / 2 + (i - 1) / 2]
然后将 arr
赋值给 perm
。
要想使 perm
回到排列初始值,至少需要执行多少步操作?返回最小的 非零 操作步数。
示例 1:
输入:n = 2 输出:1 解释:最初,perm = [0,1] 第 1 步操作后,perm = [0,1] 所以,仅需执行 1 步操作
示例 2:
输入:n = 4 输出:2 解释:最初,perm = [0,1,2,3] 第 1 步操作后,perm = [0,2,1,3] 第 2 步操作后,perm = [0,1,2,3] 所以,仅需执行 2 步操作
示例 3:
输入:n = 6 输出:4
提示:
2 <= n <= 1000
n
是一个偶数
解法
方法一:找规律 + 模拟
我们观察数字的变化规律,发现:
新数组的偶数位数字依次是原数组的前半段数字;
新数组的奇数位数字依次是原数组的后半段数字。
即,如果原数组的某个数字下标 $i$ 在 [0, n >> 1)
范围内,那么这个数字的新下标就是 i << 1
;否则,新下标就是 (i - (n >> 1)) << 1 | 1
。
另外,每一轮操作,数字移动的路径都是一样的,只要有一个数字(数字 $0$ 和 $n-1$ 除外)回到了它原来的位置,那么整个序列就和之前的一致了。
因此,我们选择数字 $1$,初始时下标也是 $1$,每次将数字 $1$ 移动到新的位置,直到数字 $1$ 回到原来的位置,就可以得到最小的操作次数。
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
Python3
class Solution:
def reinitializePermutation(self, n: int) -> int:
ans, i = 0, 1
while 1:
ans += 1
if i < n >> 1:
i <<= 1
else:
i = (i - (n >> 1)) << 1 | 1
if i == 1:
return ans
Java
class Solution {
public int reinitializePermutation(int n) {
int ans = 0;
for (int i = 1;;) {
++ans;
if (i < (n >> 1)) {
i <<= 1;
} else {
i = (i - (n >> 1)) << 1 | 1;
}
if (i == 1) {
return ans;
}
}
}
}
C++
class Solution {
public:
int reinitializePermutation(int n) {
int ans = 0;
for (int i = 1;;) {
++ans;
if (i < (n >> 1)) {
i <<= 1;
} else {
i = (i - (n >> 1)) << 1 | 1;
}
if (i == 1) {
return ans;
}
}
}
};
Go
func reinitializePermutation(n int) (ans int) {
for i := 1; ; {
ans++
if i < (n >> 1) {
i <<= 1
} else {
i = (i-(n>>1))<<1 | 1
}
if i == 1 {
return ans
}
}
}