Java 计算非平凡问题的时间复杂度
我在计算下面所示程序的时间复杂度时遇到困难。这是一个生成有效括号的简单程序,如“(())”“(())”等。但是,我真的不知道如何估计这类问题的时间复杂度 如果您能在这里分享一些您觉得有用的技术,我们将不胜感激。如果您能分析我链接的程序(例如:)将是最好的选择 我的目标是:Java 计算非平凡问题的时间复杂度,java,algorithm,recursion,time-complexity,catalan,Java,Algorithm,Recursion,Time Complexity,Catalan,我在计算下面所示程序的时间复杂度时遇到困难。这是一个生成有效括号的简单程序,如“(())”“(())”等。但是,我真的不知道如何估计这类问题的时间复杂度 如果您能在这里分享一些您觉得有用的技术,我们将不胜感激。如果您能分析我链接的程序(例如:)将是最好的选择 我的目标是: 估计非平凡程序的时间复杂度。通常是一个递归程序,有一些修剪 我在寻找一个快速的估计解决方案,而不是严格的数学证明 先谢谢你 有关守则: public ArrayList<String> generateParent
public ArrayList<String> generateParenthesis(int n) {
ArrayList<String> res = new ArrayList<String>();
String oneSolu = "";
Generate(n, n, res, oneSolu);
return res;
}
private void Generate(int l, int r, ArrayList<String> res, String oneSolu) {
if (l==0 && r==0) {
res.add(oneSolu);
return ;
}
//add left
if (l > 0) {
String t = oneSolu;
t += "(";
Generate(l-1, r, res, t);
}
if (r>l) {
String t = oneSolu;
t += ")";
Generate(l, r-1, res, t);
}
}
public ArrayList generateParenthesis(int n){
ArrayList res=新的ArrayList();
字符串oneSolu=“”;
生成(n,n,res,oneSolu);
返回res;
}
私有void生成(int l、int r、ArrayList res、字符串oneSolu){
如果(l==0&&r==0){
res.add(oneSolu);
返回;
}
//左加
如果(l>0){
字符串t=oneSolu;
t+=“(”;
生成(l-1,r,res,t);
}
如果(r>l){
字符串t=oneSolu;
t+=”;
生成(l,r-1,res,t);
}
}
使用n对生成的有效括号数是n个加泰罗尼亚数,定义为2nCn/(n+1)
,但如果需要更简化的边界,则为O(4^n)
。更一般地说,任何递归函数都是由其最大分支因子和深度作为上界的,如O(b^d)
如果在每个级别上所做的工作是O(1)
,那么在这种情况下,深度=2N,分支因子大约为2,因此T(n)=2^(2N)=4^n。我必须承认,您的特定用例似乎特别困难,所以不要对自己太苛刻
估计非平凡程序的时间复杂度。通常是递归的
有一些修剪的程序
我在寻找一个快速的估计解决方案,而不是一个严格的数学模型
证明
当我分析运行时,我可以给你我的正常思维过程。对于这种特殊情况,它不会有太大的帮助,但在一般情况下肯定会有帮助(如果您稍后在分析其他程序时遇到问题)
我不能保证不使用严格的数学;如果我真的想确定一个界限,我倾向于默认它。对于松散的边界,这些东西通常足够简单,但它不是一个大问题
首先,我通常会考虑两件主要的事情
1)我至少可以写下重现期吗?
许多人都熟悉一些复发(比如T(n)=T(n-1)+T(n-2)),而有些复发已经被广泛研究过(比如任何可以用master方法解决的问题)。如果一个程序属于这一类,认为自己相当幸运。
在你的特殊情况下,复发似乎是
如果R>L
T(L,R)=T(L-1,R),否则,基本情况下
T(0,R)=R
这不是最好的开始
2)分析使用特定参数调用特定函数的次数
这种方法通常在动态规划中更有用,在动态规划中存储过去的结果以节省计算量,但它也是皮带中的另一种工具。也就是说,如果您无法计算使用特定参数调用函数的次数,那么这不是一个选项
然而,在这种情况下,这种方法在数学上很难实现。基本问题是使用特定的l
和r
调用Generate()
的次数完全取决于oneSolu
的可能值。(ArrayList是一个累加器,因此不必担心)
在我们的例子中,我们碰巧知道字符串的长度(因为第一次调用的长度是l=r=n
,并且每个递归调用正好将两个调用中的一个减少了1),我们还可以证明这一点
对于传入的oneSolu
的每个值,我们可以保证每个前缀的(
s)都比)
s多
这一特定长度的每一个这样的字符串都包含在内
我很确定这个值是可以找到的,但是1)数学很快就会变得难看,2)即使你走了那么远,你也必须用一个双重求和来计算它。不实用,甚至到目前为止,数学处理的比你想要的还要多
现在来看看获得上界的粗糙方法。这是一种“快速”的方法,但是它没有考虑任何类型的修剪,所以如果你想要一个紧凑的边界,它可能是非常无用的。它已经被贴出来了,但我还是要加上它,这样这个答案就可以自己总结一切了
3)将最大深度乘以最大分支因子。
正如@Vikramhat已经指出的,你的分支因子是2,最大深度是2n,所以你看到的是一个22n=4n的(非常非常)松散的边界,正如@KarolyHorvath在评论中指出的,每个节点的功是线性的,因此我们得到了一个O(n4n)运行时间。为什么在不使用的情况下,左右两边的时间都过去了?我忘了把它拿走..从来没有mind@RobinGreen是的,OP给出的代码是用Java编写的,但听起来OP所要求的是语言不可知的。@DennisMeng好的,我想大多数计算机科学家应该能够阅读Java代码。这是一个公平的观点。链接到加泰罗尼亚数字维基百科我不太确定这是否正确。是的,匹配括号任务是加泰罗尼亚数字有用性的经典例子。但是,只有假设每个解决方案/节点都可以使用O(1)生成时,此分析和深度分析才是正确的。我认为这里的字符串创建增加了一个O(N)乘数。“让我想想。”卡洛利霍瓦特说