构建字典序最大的可行序列
难度:
标签:
题目描述
代码结果
运行时间: 28 ms, 内存: 16.4 MB
/*
* 给定一个整数 n,找到满足以下条件的一个序列:
* 1. 整数 1 在序列中只出现一次。
* 2. 2 到 n 之间每个整数都恰好出现两次。
* 3. 对于每个 2 到 n 之间的整数 i,两个 i 之间出现的距离恰好为 i。
* 4. 返回满足上述条件中字典序最大的序列。
* 思路:
* 从 n 开始向下放置数字。优先放置大的数字,这样可以确保字典序最大。
*/
import java.util.Arrays;
import java.util.stream.IntStream;
public class Solution {
public int[] constructSequence(int n) {
int[] result = new int[2 * n - 1];
Arrays.fill(result, -1); // 使用 -1 作为占位符
IntStream.rangeClosed(2, n).map(i -> n - i + 2).forEach(i -> {
IntStream.range(0, 2 * n - 1 - i).filter(j -> result[j] == -1 && result[j + i + 1] == -1).findFirst().ifPresent(j -> {
result[j] = i;
result[j + i + 1] = i;
});
});
IntStream.range(0, 2 * n - 1).filter(i -> result[i] == -1).findFirst().ifPresent(i -> result[i] = 1);
return result;
}
public static void main(String[] args) {
Solution sol = new Solution();
int n = 3;
System.out.println(Arrays.toString(sol.constructSequence(n))); // 输出: [3, 1, 2, 3, 2]
}
}
解释
方法:
此题解基于回溯算法。首先,初始化一个长度为2n-1的数组res,用于存放最终的序列。数组中的每个位置初始值为None,表示尚未放置任何数字。使用一个集合visited来记录当前已经被放置在序列中的数字。回溯函数recurse通过递归的方式尝试在序列res中放置数字,从n开始尝试到1,以确保字典序最大。对于每个数字i,如果i未被访问过,并且位置上的放置是可行的(对于大于1的i,需要确保res[idx + i]也未被占用),则将i放入当前位置和对应的间隔位置(如果i大于1)。然后,继续递归尝试填充下一个位置。如果递归成功,返回True;否则,撤销当前放置,尝试下一个数字。通过这种方式,算法尝试构建字典序最大的序列。
时间复杂度:
O(n!)
空间复杂度:
O(n)
代码细节讲解
🦆
为什么在递归函数中从数字n开始递减尝试放置,而不是从1开始放置?
▷🦆
在回溯算法中,如果放置了数字i但未能成功构建整个序列,为什么需要撤销当前位置和间隔位置的放置?
▷🦆
在寻找下一个空位置时,是否存在更高效的方法来确定下一个未被占用的位置,而不是通过逐个检查?
▷🦆
题解中没有详细解释如何确保构建的是字典序最大的序列,能否进一步解释这一点?
▷