题目地址:
https://leetcode.com/problems/circular-permutation-in-binary-representation/
给定两个正整数 n n n和 s s s,要求返回一个长 2 n 2^n 2n的列表,使得其以 s s s开头,并且任意相邻两个数字的二进制表示恰好有一位不同,且首尾的两个数字也如此。
其实就是格雷码问题,可以递归地解决。若 n = 1 n=1 n=1,则 ( 0 , 1 ) (0,1) (0,1)即为满足条件的格雷码。若 n ≥ 2 n\ge 2 n≥2,假设已经生成了 n − 1 n-1 n−1的格雷码,现在将这个格雷码每个数字加上 2 n − 1 2^{n-1} 2n−1后,再逆序然后接在原列表后面,就得到了长 2 n 2^n 2n的列表,该列表就是合法的格雷码。当然实际写程序的时候可以写成循环的形式。代码如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Solution {
public List<Integer> circularPermutation(int n, int start) {
List<Integer> list = new ArrayList<>(Arrays.asList(0, 1));
for (int i = 0; i < n - 1; i++) {
int size = list.size();
for (int j = 0; j < size; j++) {
list.add(list.get(size - 1 - j) + (1 << i + 1));
}
}
// 找到start的位置
int idx = 0;
while (list.get(idx) != start) {
idx++;
}
// 变为以start开始
List<Integer> res = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
res.add(list.get(idx + i < list.size() ? idx + i : idx + i - list.size()));
}
return res;
}
}
时空复杂度 O ( 2 n ) O(2^n) O(2n)。
也可以不递归地做。考虑 f ( k ) = k ∧ ( k > > 1 ) f(k)=k\wedge (k>>1) f(k)=k∧(k>>1),当 k k k从 0 0 0取到 2 n − 1 2^n-1 2n−1的时候, f f f就取遍了格雷码。而 f ( 0 ) = 0 f(0)=0 f(0)=0,所以 s ∧ f ( k ) s\wedge f(k) s∧f(k)也取遍了格雷码,并且以 s s s开头。代码如下:
import java.util.ArrayList;
import java.util.List;
public class Solution {
public List<Integer> circularPermutation(int n, int start) {
List<Integer> res = new ArrayList<>();
for (int i = 0; i < 1 << n; i++) {
res.add(start ^ i ^ i >> 1);
}
return res;
}
}
时间复杂度 O ( 2 n ) O(2^n) O(2n),空间 O ( 1 ) O(1) O(1)。