问题描述
小R遇到了一个非常有趣的数学问题。他手里有一个排列A,这个排列是从1到N的整数序列。我们称排列A为神奇排列,当且仅当该排列能够生成恰好K个不同的整数。
对于排列A,如果存在某个位置i,使得A[i] + i等于一个整数X,那么我们说整数X是由排列A生成的。
例如,当K为2时,排列[2, 1, 3]是神奇的,因为通过计算A[i] + i,生成了两个不同的整数:2和5。
现在,你得到两个整数N和K,请你返回从1到N的整数的字典顺序最小的神奇排列P。可以证明在给定约束下,总是存在至少一个符合要求的神奇排列。
测试样例
样例1:
输入:
n = 4, k = 3输出:[1, 2, 4, 3]
样例2:
输入:
n = 5, k = 2输出:[1, 5, 4, 3, 2]
样例3:
输入:
n = 6, k = 4输出:[1, 2, 3, 6, 5, 4]
要求
- 返回的排列必须是字典顺序最小的。
- 排列
A必须能够生成恰好K个不同的整数。 - 排列
A是从1到N的整数序列。
程序思路概述与关键点详细解析
1. 特殊情况处理
-
( N == K ) :
- 如果需要生成的不同整数数量等于排列的大小 ( N ),每个位置 ( A[i] + i ) 都能生成唯一的整数,无需调整。
- 直接返回:
[1, 2, ..., N]。
-
( K == 1 ) :
- 如果只需要生成 1 个整数,所有元素反转(如
[1, 2, 3] -> [3, 2, 1]),保证A[i] + i的结果相同。
- 如果只需要生成 1 个整数,所有元素反转(如
2. 遍历调整子区间
-
枚举从索引
i = 1开始的子区间:res = A[:]创建当前排列的副本。res[i:] = reversed(res[i:])反转子区间,尝试生成新的排列。
3. 检查生成的不同整数
-
对当前排列
res,计算所有可能的A[i] + i的值:- 使用
set去重,统计生成的不同整数数量。
- 使用
4. 字典序最小性
- 枚举从索引
i = 1开始的反转操作,保证尽可能少地改变排列结构,从而保持字典序最小。
def solution(n: int, k: int) -> list:
A = list(range(1, n + 1))
if n == k:
return A
if k == 1:
return A[::-1]
for i in range(1, n):
res = A[:]
res[i:] = reversed(res[i:])
tem = set([j + v for j, v in enumerate(res)])
if len(tem) == k:
return res
if __name__ == '__main__':
print(solution(n=4, k=3) == [1, 2, 4, 3])
print(solution(n=5, k=2) == [1, 5, 4, 3, 2])
print(solution(n=6, k=4) == [1, 2, 3, 6, 5, 4])
print(solution(n=1, k=1) == [1])
print(solution(n=14, k=1) == list(reversed(range(1, 14 + 1))))
程序复杂度分析
-
时间复杂度:
- 枚举子区间反转:最多 ( N-1 ) 次。
- 每次计算不同整数数量(使用集合):最多 ( O(N) )。
- 总复杂度:( O(N^2) )。
-
空间复杂度:
res和tem:额外空间为 ( O(N) )。- 总复杂度:( O(N) )。