原题描述如下:
给你一个偶数 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. 暴力算法
最让人容易想到的肯定是暴力算法呀!不过这样时间复杂度和空间复杂度都很高,虽然可以成功提交,但是在leetcode相比较其他用户而言,差太多了。
class Solution(object):
def reinitializePermutation(self, n):
"""
:type n: int
:rtype: int
"""
perm = [i for i in range(n)]
perm1 = perm
res = 0
arr = [0 for i in range(n)]
while arr != perm:
arr = [0 for i in range(n)]
for i in range(n):
if i%2 == 0:
arr[i] = perm1[i//2]
else:
arr[i] = perm1[n//2+(i-1)//2]
res += 1
perm1 = arr
return res
# 暴力算法
2. 利用数学知识找规律
下面就演示一下这个排列perm中数据的变化:
n = 6 时,res = 4
index 0 1 2 3 4 5
data 0 1 2 3 4 5
1 0 3 1 4 2 5
2 0 4 3 2 1 5
3 0 2 4 1 3 5
4 0 1 2 3 4 5
n = 8 时,res = 3
index 0 1 2 3 4 5 6 7
data 0 1 2 3 4 5 6 7
1 0 4 1 5 2 6 3 7
2 0 2 4 6 1 3 5 7
3 0 1 2 3 4 5 6 7
小编发现如果根据数据1的下标索引index的变化,可以发现如下规律如下:
- 如果index*2小于n,那么index = index*2;
- 否则index = (index-n/2)*2+1
其实这个是可以根据题意推出来的:
因为数据1的下标索引初始值是1,那么经过1次操作之后,它的下标索引值变为2,再经过1次操作之后,它的下标索引变为4,以此类推;当数据1的下标索引乘以2已经大于n了,显然已经不能用上述计算方式,此时计算方式应为:下标索引 = (上次的下标索引-n/2)*2+1(根据arr[i] = perm[n / 2 + (i - 1) / 2]倒推即可),读者自己尝试几次数据变化就会发现如上规律。
如果经过上述变化,数据1的下标索引正好等于n/2,此时的变化次数+1即是这个算法题目结果(描述的可能不是很清楚哈!)。
参考代码为:
class Solution(object):
def reinitializePermutation(self, n):
"""
:type n: int
:rtype: int
"""
index = 1
res = 1
while index != n//2:
if index*2 < n:
index = index*2
else:
index = (index - n//2)*2+1
res +=1
return res
运行结果: