优美的排列
难度:
标签:
题目描述
假设有从 1 到 n 的 n 个整数。用这些整数构造一个数组 perm
(下标从 1 开始),只要满足下述条件 之一 ,该数组就是一个 优美的排列 :
perm[i]
能够被i
整除i
能够被perm[i]
整除
给你一个整数 n
,返回可以构造的 优美排列 的 数量 。
示例 1:
输入:n = 2 输出:2 解释: 第 1 个优美的排列是 [1,2]: - perm[1] = 1 能被 i = 1 整除 - perm[2] = 2 能被 i = 2 整除 第 2 个优美的排列是 [2,1]: - perm[1] = 2 能被 i = 1 整除 - i = 2 能被 perm[2] = 1 整除
示例 2:
输入:n = 1 输出:1
提示:
1 <= n <= 15
代码结果
运行时间: 32 ms, 内存: 16.0 MB
/*
题目思路:
使用Java Stream和Lambda表达式实现。
通过递归和流操作来生成排列并检查是否满足条件。
由于每个数字都必须被检查多次,因此我们仍然需要使用回溯法。
*/
import java.util.stream.IntStream;
public class SolutionStream {
public int countArrangement(int n) {
return countArrangement(n, 1, new boolean[n + 1]);
}
private int countArrangement(int n, int pos, boolean[] visited) {
if (pos > n) {
return 1;
}
return IntStream.range(1, n + 1)
.filter(i -> !visited[i] && (i % pos == 0 || pos % i == 0))
.map(i -> {
visited[i] = true;
int count = countArrangement(n, pos + 1, visited);
visited[i] = false;
return count;
})
.sum();
}
}
解释
方法:
这个题解采用了回溯法来解决问题。首先,为了方便查找每个位置 i 可以放置哪些数字,创建了一个字典 match,其中 match[i] 包含所有可以放在位置 i 的数字(即满足 perm[i] 能被 i 整除或 i 能被 perm[i] 整除的数)。接着,使用 vis 集合来跟踪哪些数字已经在排列中被使用过。backtrack 函数从位置 1 到 n 递归地尝试所有可能的数字,如果构成了一个优美的排列,则增加结果计数器 self.res。
时间复杂度:
O(n!)
空间复杂度:
O(n^2)
代码细节讲解
🦆
在构建match字典时,为什么选择将所有满足条件的数字存储而不是直接在回溯过程中计算?
▷🦆
如何确保在递归过程中使用的vis集合正确地跟踪了哪些数字已经被使用,以避免重复?
▷🦆
在backtrack函数中,当index等于n+1时,为什么可以直接将结果计数器res增加1,而不需要进一步验证当前排列的有效性?
▷相关问题
优美的排列 II
给你两个整数 n
和 k
,请你构造一个答案列表 answer
,该列表应当包含从 1
到 n
的 n
个不同正整数,并同时满足下述条件:
- 假设该列表是
answer = [a1, a2, a3, ... , an]
,那么列表[|a1 - a2|, |a2 - a3|, |a3 - a4|, ... , |an-1 - an|]
中应该有且仅有k
个不同整数。
返回列表 answer
。如果存在多种答案,只需返回其中 任意一种 。
示例 1:
输入:n = 3, k = 1 输出:[1, 2, 3] 解释:[1, 2, 3] 包含 3 个范围在 1-3 的不同整数,并且 [1, 1] 中有且仅有 1 个不同整数:1
示例 2:
输入:n = 3, k = 2 输出:[1, 3, 2] 解释:[1, 3, 2] 包含 3 个范围在 1-3 的不同整数,并且 [2, 1] 中有且仅有 2 个不同整数:1 和 2
提示:
1 <= k < n <= 104