我试图用经典问题来实现一个算法来打印n对括号的所有有效组合。
我找到了这个程序(效果很好):
public static void addParen(ArrayList<String> list, int leftRem, int rightRem, char[] str, int count) {
if (leftRem < 0 || rightRem < leftRem) return; // invalid state
if (leftRem == 0 && rightRem == 0) { /* all out of left and right parentheses */
String s = String.copyValueOf(str);
list.add(s);
} else {
if (leftRem > 0) { // try a left paren, if there are some available
str[count] = '(';
addParen(list, leftRem - 1, rightRem, str, count + 1);
}
if (rightRem > leftRem) { // try a right paren, if there’s a matching left
str[count] = ')';
addParen(list, leftRem, rightRem - 1, str, count + 1);
}
}
}
public static ArrayList<String> generateParens(int count) {
char[] str = new char[count*2];
ArrayList<String> list = new ArrayList<String>();
addParen(list, count, count, str, 0);
return list;
}
据我了解,我们的想法是尽可能添加左括号。对于右括号,只有右括号的剩余数量大于左括号时才会添加它。如果我们使用了所有左括号和右括号,我们将新结果添加到结果中。我们可以确定不会有任何重复的构造字符串。
对我来说,这个递归就像我们使用树一样,例如我们进行预订遍历:我们去左边的节点每次都可以,如果不是我们走的话,然后我们尝试在这一步之后离开。如果我们做不到,我们会回来&#34;并且向右走,我们重复遍历。在我看来,这里的想法完全相同。
所以,天真地,我认为时间复杂度将类似于O(log(n)),O(n.log(n))或具有对数的东西。但是,当我试图搜索那个时,我找到了一个名为&#34; CATALAN&#34;的数字,我们可以用它来计算括号组合的数量....(https://anonymouscoders.wordpress.com/2015/07/20/its-all-about-catalan/)
您认为时间复杂度是多少?我们可以在这里应用主定理吗?
答案 0 :(得分:3)
此代码的复杂性为O(n * Cat(n)),其中Cat(n)是第n个加泰罗尼亚数字。 Cat(n)个可能的有效字符串是括号的有效组合(参见https://en.wikipedia.org/wiki/Catalan_number),并且每个字符串都创建了长度为n的字符串。
由于Cat(n)=选择(2n,n)/(n + 1),O(n * Cat(n))= O(选择(2n,n))= O(4 ^ n / sqrt( n))(见https://en.wikipedia.org/wiki/Central_binomial_coefficient)。
你的推理存在两个主要缺陷。第一个是搜索树不平衡:当你关闭右支撑时搜索的树与你添加另一个左支撑时搜索的树的大小不同,因此更常用的计算复杂性的方法不在于:工作。第二个错误是,即使你假设树是平衡的,搜索树的高度也是n,叶子的数量是O(2 ^ n)。这与二元搜索树的分析不同,在二叉搜索树中,树中通常有n个东西,高度为O(log n)。
我不认为这里有任何标准的方法来计算时间复杂度 - 最终你将会重现像计算有效的括号字符串时所做的数学一样 - 以及师父定理并没有为你提供动力。
但是这里有一个有用的见解:如果一个程序产生f(n)事物,并且如果c(n)产生每个事件的成本,那么程序的复杂性可能比O更好。 (C(N)F(N))。这里,f(n)= Cat(n)和c(n)= 2n,因此即使分析代码很困难,也可以快速获得复杂性的下限。这个技巧会立即导致你放弃复杂性为O(log n)或O(n log n)的想法。