我收到了这个面试问题,我仍然对它很困惑。 问题就像标题所示,我会解释。
您将获得一个随机创建功能。
函数输入是整数n。假设我用3来称呼它。
它应该给我1到3的数字的排列。所以例如它会给我2,3,1。
我再次调用该函数后,它不会给我相同的排列,现在它会给我1,2,3,例如。
现在,如果我打电话给n = 4.我可能会得到1,4,3,2。
再次调用3将不会输出2,3,1或1,2,3,因为之前已经输出了,它会给出3个不同的排列!可能的排列。
我对这个问题感到困惑,现在我还是。在正常运行时间内如何实现?在我看来,必须有一些静态变量来记住在函数完成执行之前或之后调用的内容。 所以我的想法是创建一个静态哈希表(键,值),它将输入作为键,值是n!的长度数组。 然后我们使用random方法从这些实例中输出一个随机实例并将此实例移到后面,这样它就不会被再次调用,从而使输出保持唯一。
时空复杂性对我来说似乎很大。 我在这个问题上遗漏了什么吗?
答案 0 :(得分:1)
您可以将静态变量存储为下一个排列的种子
在这种情况下,我们可以使用int更改每个数字将放入哪个插槽(例如,这是硬编码为4个数字的集合)
private static int seed = 0;
public static int[] generate()
{
//s is a copy of seed, and increment seed for the next generation
int s = seed++ & 0x7FFFFFFF; //ensure s is positive
int[] out = new int[4];
//place 4-2
for(int i = out.length; i > 1; i--)
{
int pos = s % i;
s /= i;
for(int j = 0; j < out.length; j++)
if(out[j] == 0)
if(pos-- == 0)
{
out[j] = i;
break;
}
}
//place 1 in the last spot open
for(int i = 0; i < out.length; i++)
if(out[i] == 0)
{
out[i] = 1;
break;
}
return out;
}
这是一个将大小作为输入的版本,并使用HashMap存储种子
private static Map<Integer, Integer> seeds = new HashMap<Integer, Integer>();
public static int[] generate(int size)
{
//s is a copy of seed, and increment seed for the next generation
int s = seeds.containsKey(size) ? seeds.get(size) : 0; //can replace 0 with a Math.random() call to seed randomly
seeds.put(size, s + 1);
s &= 0x7FFFFFFF; //ensure s is positive
int[] out = new int[size];
//place numbers 2+
for(int i = out.length; i > 1; i--)
{
int pos = s % i;
s /= i;
for(int j = 0; j < out.length; j++)
if(out[j] == 0)
if(pos-- == 0)
{
out[j] = i;
break;
}
}
//place 1 in the last spot open
for(int i = 0; i < out.length; i++)
if(out[i] == 0)
{
out[i] = 1;
break;
}
return out;
}
此方法有效,因为种子存储了要放置的每个元素的位置
尺寸4:
4
3
2
1
此方法可扩展至12个!对于整数,13!溢出,或20!为多头(21!溢出)
如果您需要使用更大的数字,您可以使用BigInteger
s替换种子
答案 1 :(得分:1)
Jonathan Rosenne的答案被低估了,因为它只是链接,但在我看来,它仍然是正确的答案,因为这是一个众所周知的问题。您还可以在维基百科中看到最小的解释:https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order。
为了解决您的空间复杂性问题,在词典排序中生成排列具有O(1)空间复杂度,除了当前排列之外,您不需要存储任何其他内容。该算法非常简单,但最重要的是,它的正确性非常直观。想象一下,您拥有所有排列的集合,并按字典顺序排序。按顺序前进到下一个然后循环返回将为您提供最大循环而不重复。这个问题同样是空间复杂性,因为你需要存储所有可能的排列;该算法为您提供了一种获取下一个排列而无需存储任何内容的方法。可能需要一段时间才能理解,但是一旦我明白它就会很有启发性。