void solve(string op, int n, int zeros, int ones)
{
if(n==0){
cout<<op<<" ";
return;
}
string op1 = op;
op1.push_back('1');
solve(op1, n-1, zeros, ones+1);
if(ones > zeros){
string op2 = op;
op2.push_back('0');
solve(op2, n-1, zeros+1, ones);
return;
}
}
求解函数的时间复杂度是多少?是O(2 ^ N)吗?有人可以解释一下您如何找到递归函数的复杂性吗?
链接到问题:https://www.geeksforgeeks.org/print-n-bit-binary-numbers-1s-0s-prefixes/
答案 0 :(得分:4)
因此,我们要在这里估计最坏的情况。更糟糕的情况是条件(1> 0)始终返回真。可能吗?是的,如果一零> = n。由于我不知道您的任务背景,我可以假设它。
让T(n)是函数的复杂性。您的函数用(n-1)调用两次。那是
T(n) = T(n-1) + T(n-1) + c
其中c是例行程序的常量,您正在执行其他操作,例如附加'1'或'0',条件求值等。不依赖n的内容。
所以
T(n) = 2T(n-1) + c =
= 2(2T(n-2) + c) + c = 4T(n-2) + 3c =
= 4(2T(n-3) + c) + 3c = 8T(n-3) + 7c =
= ...
= 2^k*T(n-k) + (2^k-1)*c
因此,如果(n-k)== 0,那么就完成了,因为T(0)= z。因此,当k == n时,我们就完成了。关于z,这并不明显。在当前的实现中,我们确实输出了一个字符串O(n)。如果我们只计算字符串,则为O(1)。如果我们打印是必不可少的,那么最终复杂度将是O(n2 ^ n),否则将是O(2 ^ n)
那是
T(n) = z*2^n + (2^n - 1)*c = O(z2^n)
[UPDATE1]
在意识到从代码片段中看不到但在从所提供的链接中读取信息后变得清晰的实际问题之后,我会说复杂性有所不同。
在做出假设的情况下,以上计算仍然正确。
现在,解决这个问题。我们要查找长度为n的1和0的所有序列,其中每个前缀包含不小于该前缀中0的数目的1。
该算法提供了此问题的解决方案。正如您在每次递归调用中所注意到的那样,算法会将0或1添加到结果序列中。这意味着递归调用的数量恰好是结果字符串中符号的数量。
我们知道每个结果字符串的长度为n。因此,我们需要找出字符串的数量。让我们看一下您的程序在不同n值下的查找结果:
n | number of strings
-----------------------
1 | 1
2 | 2
3 | 3
4 | 6
5 | 10
6 | 20
7 | 35
8 | 70
因此,如果仔细看,您会发现它们是二项式系数C(n, n/2)
。可以通过大~2^n/(\pi * n/2)
将这个二项式系数估计为n
。因此,算法的复杂度为O(2^n/(n))
。但是,如果我们还考虑在递归结束时进行打印,则需要乘以n,因为它是输出字符串的长度。因此,我们最终得到O(2 ^ n)
要干净,我们需要证明我们的假设是正确的。希望您可以通过归纳法或其他方法来做到这一点。
[UPDATE2]
您的实现在每次迭代时都会遇到字符串问题。这使执行速度降低了n倍。您可以通过传递对字符串的引用并在像这样的递归调用之后删除添加的字符来避免这种情况:
void solve(string &op, int n, int zeros, int ones)
{
if(n==0){
cout<<op<<" ";
return;
}
op.push_back('1');
solve(op, n-1, zeros, ones+1);
op.pop_back();
if(ones > zeros){
op.push_back('0');
solve(op, n-1, zeros+1, ones);
op.pop_back();
return;
}
}
答案 1 :(得分:0)
如果我对您的理解正确,该函数的时间复杂度为O(N * 2 ^ N)。我使用递归树来分析结果。