Algorithm 这个算法的空间复杂度是多少?
这是破解编码面试的问题9.6(第5版) 实现一个算法来打印n对括号的所有有效组合Algorithm 这个算法的空间复杂度是多少?,algorithm,recursion,time-complexity,space-complexity,catalan,Algorithm,Recursion,Time Complexity,Space Complexity,Catalan,这是破解编码面试的问题9.6(第5版) 实现一个算法来打印n对括号的所有有效组合 范例 输入:3 输出:(())、(())、(())()、(())、(())、(())、(()) 下面是我实现的算法(用Java) 私有静态集getAllComb(int n){ Set allPoss=new HashSet(); 如果(n>0){ 如果(n==1){ 全部可能添加(“()”); }否则{ Set before=getAllComb(n-1); for(字符串短语:before){ int leng
范例
输入:3
输出:(())、(())、(())()、(())、(())、(())、(()) 下面是我实现的算法(用Java)
私有静态集getAllComb(int n){
Set allPoss=new HashSet();
如果(n>0){
如果(n==1){
全部可能添加(“()”);
}否则{
Set before=getAllComb(n-1);
for(字符串短语:before){
int length=短语.length();
对于(int start=length-2;start>=0;start--){
如果(短语.charAt(开始)='('){
String phraseToConsider=短语.子字符串(0,开始+1)+“()”+
短语.子字符串(开始+1);
如果(!allPoss.contains(phraseToConsider)){
所有可能添加(措辞考虑);
}
}
}
字符串短语toconsider=“()”+短语子字符串(0);
如果(!allPoss.contains(phraseToConsider)){
所有可能添加(措辞考虑);
}
}
}
}
返回allpos;
}
这会产生正确的输出。我知道面试官(至少在亚马逊)喜欢问你解决方案的时间和空间复杂性。对于时间复杂性,我能够证明算法在O(n)中运行使用递归关系。我在分析空间复杂性方面遇到困难。这是一个递归解决方案,因此它至少应该是O(n),但在每次递归调用时,我也生成一个以n为界的集。由于n个递归调用,总空间是O(n)还是O(n2)因为为每个递归调用设置了绑定n的大小n
对于时间复杂度,我能够证明该算法在O(n)中运行,并且具有递归关系
这是错误的。平衡圆括号的序列数由以下公式给出:此类序列的数量是指数级的。如果您的算法也能正确地解决问题,那么它就不可能是线性的,因为仅仅输出指数级的解本身就需要指数级的时间
至于内存复杂性,您似乎在递归的每个步骤中存储了n-1
的所有解决方案,因此内存复杂性在我看来也是指数级的,加上您在每个步骤中创建的其他字符串和递归调用,这只会增加复杂性
不使用指数内存就可以解决这个问题:考虑一下如何摆脱存储所有以前的序列。写入n对正确匹配的括号的方法是第n个加泰罗尼亚数,它实际上是指数增长的,而不是平方增长的。仅输出的空间复杂度就为0(2^n);有关加泰罗尼亚数字的快速概述,请参阅
请注意,您并不是在每个深度进行一次递归调用,而是可能进行O(n)次递归调用。这方面的一些技巧与您的问题没有直接关系:1)您应该能够使用n=0作为基本情况;递归问题中的常见问题是不需要特别处理n=1 2)如果使用长度为2n的缓冲区,递归调用“fill-in”,则应该能够将空间复杂度降低到O(n)。对于n=4,有14种可能的方法可以正确地编写匹配的论文。长度为2n(或kn表示任意k)的缓冲区太小,通常无法保存输出。如何使用长度为2n的缓冲区?你不能预先定义集合的大小。@HugomgI只是希望有另一个N=1的基本情况,因为这只是添加“()”的一行代码。您不必遍历所有代码行来迭代上一个集合、所有字符串等。这真的是一个糟糕的设计吗?它本身并不坏,但额外的代码行意味着有更多的地方可以隐藏bug:)并且通常使用相同的尾码分支来处理N=1,而不是其他分支,这不会对性能造成很大影响。如果是,那么就有可疑的事情发生了……你不需要存储所有以前的序列来应用算法吗?如果不存储它们,就无法访问所有字符串并形成所有括号组合。@CommittedDroider-在每一步中,都有两种可能:放一个开括号或一个闭括号。通过这样做,您可以将内存使用保持在
O(n)
。这也会生成不平衡的序列,但如果您知道如何使它只生成平衡的序列,那么您就完成了。提示:记下你开了多少,关了多少。我不太明白你的意思。我是如何看待这个问题的,是如何找到在左括号前后加“()”的可能性。假设我有第一个函数调用f(1),它是()。在这种情况下,你说的每一步是什么意思,放一个开放的括号还是封闭的括号?您不需要同时放一个开放括号和一个封闭括号吗?@CommittedDroider思考如何通过从左到右放置(
或)
来构建解决方案:在每一步中,您必须有更多的开放而不是封闭。然后你可以回溯。打开n
后,必须关闭其余部分。如果可以的话,一定要打开门。例如,您从(())
开始。然后你回溯到((
)()。你可以将最后一个设置为关闭的:(())(
。然后,下一个可以是打开的:(())(
。现在你只能将其余的设置为关闭:(())
等等。通过你所做的,我看到了从((())到(())的过程,但你如何从((())到(())呢?您会首先将其回溯到什么?您所说的“注意,您并没有在e上进行一次递归调用”是什么意思
private static Set<String> getAllComb(int n) {
Set<String> allPoss = new HashSet<String>();
if(n>0) {
if(n==1) {
allPoss.add("()");
} else {
Set<String> before = getAllComb(n-1);
for(String phrase: before) {
int length = phrase.length();
for(int start = length - 2; start>=0; start--) {
if(phrase.charAt(start) == '(') {
String phraseToConsider = phrase.substring(0, start+1) + "()" +
phrase.substring(start + 1);
if(!allPoss.contains(phraseToConsider)){
allPoss.add(phraseToConsider);
}
}
}
String phraseToConsider = "()" + phrase.substring(0);
if(!allPoss.contains(phraseToConsider)){
allPoss.add(phraseToConsider);
}
}
}
}
return allPoss;
}