寻找数组的错位排列
难度:
标签:
题目描述
代码结果
运行时间: 150 ms, 内存: 15.9 MB
/*
* 寻找数组的错位排列
* 题目描述:给定一个数组,你需要找到这个数组的错位排列。
* 错位排列指的是数组中元素的位置和它的初始位置不相同。
* 思路:
* 1. 使用Java Stream生成所有的排列。
* 2. 使用过滤器检查每个排列是否是错位排列。
*/
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class DerangementStream {
public static void main(String[] args) {
int[] nums = {1, 2, 3};
List<int[]> result = findDerangements(nums);
result.forEach(arr -> System.out.println(Arrays.toString(arr)));
}
public static List<int[]> findDerangements(int[] nums) {
List<int[]> permutations = permute(nums);
return permutations.stream()
.filter(DerangementStream::isDerangement)
.collect(Collectors.toList());
}
private static List<int[]> permute(int[] nums) {
if (nums.length == 0) {
return Collections.singletonList(new int[0]);
}
return IntStream.range(0, nums.length).boxed()
.flatMap(i -> {
int[] subArray = IntStream.concat(IntStream.of(nums).limit(i), IntStream.of(nums).skip(i + 1)).toArray();
return permute(subArray).stream().map(tail -> {
int[] result = new int[nums.length];
result[0] = nums[i];
System.arraycopy(tail, 0, result, 1, tail.length);
return result;
});
}).collect(Collectors.toList());
}
private static boolean isDerangement(int[] nums) {
return IntStream.range(0, nums.length).allMatch(i -> nums[i] != i + 1);
}
}
解释
方法:
这个题解使用动态规划的思想来解决错位排列问题。我们用 f[i] 表示长度为 i 的数组的错位排列数目,那么可以得到递推式:f[i] = (i-1) * (f[i-1] + f[i-2])。这是因为我们可以考虑数字 i 放在哪里:如果把 i 放在某个位置,其余 i-1 个数有 f[i-1] 种错位排列方式;如果不把 i 放在任何位置,那么就相当于求 i-1 个数的错位排列数,即 f[i-2]。为了避免重复计算和节省空间,我们可以只用两个变量 f1 和 f2 来分别表示 f[i-1] 和 f[i-2],并在每次循环中更新它们的值。
时间复杂度:
O(n)
空间复杂度:
O(1)
代码细节讲解
🦆
为什么在计算错位排列数时递推式为`f[i] = (i-1) * (f[i-1] + f[i-2])`?这里的`(i-1)`和`(f[i-1] + f[i-2])`分别代表什么意义?
▷🦆
你是如何确定基础情况`f[0]`和`f[1]`的值分别为0和0,以及`f[2]`为1的?这些情况有何特殊之处?
▷🦆
在动态规划的更新过程中,使用`(i - 1) * (f1 + f2) % MOD`来避免整数溢出,但为什么选择这个特定的模`10**9 + 7`?
▷