我希望您对我在Python中实现的算法的时间和空间复杂性有所了解,以计算算法的复杂性,以打印n对括号的所有有效(即正确打开和关闭)组合(请参阅all valid combinations of n-pair of parenthesis)
def find_par_n(n):
s = set(["()"])
for i in range(2, n + 1):
set_to_add = set()
for str_ in s:
set_temp = set()
ana = set()
for j in range(len(str_) + 1):
str_c = str_[0:j] + '(' + str_[j:]
if str_c in ana:
continue
ana.add(str_c)
for k in range(j + 1, len(str_) + 1):
str_a = str_c
str_a = str_a[0:k] + ')' + str_a[k:]
set_temp.add(str_a)
set_to_add.update(set_temp)
s = set_to_add
return s
最有可能的是,算法可以在时间和空间方面得到改善。请分享您的想法。
答案 0 :(得分:1)
为获得最佳效果,请避免使用集合。如果您只生成一次可能性,则可以将它们放入向量中。
这是一个非常简单的递归,比@ bcdan的答案稍慢:
def rget(n):
if n == 0:
return ['']
else:
return [fst + '(' + snd + ')' for i in range(n)
for fst in rget(i)
for snd in rget(n-i-1)]
如果你想要一个生成器而不是完整的结果列表,上面的变体可能是理想的,但对于完整列表的情况,计算从0到n的每个j的结果要快得多,使用先前计算的列表而不是递归调用:
def iget(n):
res = [['']]
for j in range(1, n+1):
res.append([fst + '(' + snd + ')' for i in range(j)
for fst in rget(i)
for snd in rget(j-i-1)])
return res[n]
结果显示速度提高了一个数量级(使用Python 3.3.2)。
您可以将任何平衡字符串分解为两个平衡子字符串。这不是一种独特的分解,但可以通过选择尽可能短的非空均衡后缀使其独特。最短的非空均衡后缀具有起始(
与结束)
匹配的属性;如果不是这种情况,它可以分解为两个较短的非空平衡序列。所以递归包括找到Fst(Snd)
形式的所有序列,其中 Fst 和 Snd 的大小总和为 n - 1。
n 对括号的平衡序列数是 n th Catalan number C n ,即O(4 n n 2/3 )。上面的代码在O( n )中生成每个序列(因为字符串连接占用O( n )时间),总复杂度为O(4 名词 名词 5/3 )
答案 1 :(得分:0)
让我们尝试使用更简单的版本。
一些定理:
2k
的正确配对字符串,可以通过在字符串中的每个可能位置插入一对括号2(k + 1)
来构造长度为'()'
的所有字符串。有2k + 1
个职位。n
对,我们需要递归插入步骤n
次(并获取长度为2n
的字符串。请注意,这还不足以生成所有唯一正确配对的字符串,例如,将()
插入()
会产生两次相同的字符串(()()
)。但是作为一个上限就足够了。
def add_parens(s, nparens):
if not s:
add_parens('()', nparens)
max_length = 2 * nparens
if len(s) == max_length:
print(s)
else:
for i in range(0, len(s)):
add_parens(s[0:i] + '()' + s[i:], nparens)
n = 5
add_parens('', n)
时间复杂度:
1
个插入点。 3
有()
个插入点。
... T(n) = 1 * 3 * ... * 2n + 1 ~ O(n!)
递归版本的空间复杂度为O(n(2n + 1))
,但我非常确定可以将其降低到线性。
答案 2 :(得分:0)
我好奇并写了我自己的版本。以下是一些规范(这些都在 Python 2.7 中):
n=8
我的:21毫秒
你的:89毫秒
n=10
我的:294毫秒
你的:1564毫秒
def get(n):
def find(current):
out = []
last_index = 0
for j in range(current.count('(')):
last_index = current.index('(',last_index) + 1
out.append(current[:last_index] + '()' + current[last_index:])
return out
seed = '()'
current = '()'
temp = set(['()'])
for i in range(n):
new = set()
for thing in temp:
new.update(find(thing))
temp = new
return [a[1:-1] for a in temp]
我认为最大的区别是我的只有3个for循环,你的有4个。这发生在str.count
函数。使用这种内置功能可能会大大提高速度。此外,在每个阶段之后,我确定了重复项,并且这会按时按指数减少。
实际上,我看到的最大区别是我只在括号后插入,然后在完成时剥离外部的两个。它创建了更少的重复项。因此,((())())
已创建,并在删除封闭的(())()
后缩减为正确的()
形式。
答案 3 :(得分:0)
递归版似乎非常简单,只花时间在有效的分支上:
def gen(sofar, l, r, n):
"""
sofar: string generated so far
l: used left parentheses
r: used right parentheses
n: total required pairs
"""
result = []
if l == n and r == n:
# solution found
result.append(sofar)
else:
if l > r:
# can close
result.extend(gen(sofar + ")", l, r + 1, n))
if l < n:
# can open
result.extend(gen(sofar + "(", l + 1, r, n))
return result
def generate(n):
return gen("", 0, 0, n)
print generate(4)
答案 4 :(得分:0)
可能的组合数量是加泰罗尼亚数字。 因此复杂性至少是该数字指定的复杂性。 https://en.wikipedia.org/wiki/Catalan_number