如何分析时间复杂度?

时间:2015-12-13 17:32:16

标签: java recursion time

假设您正和朋友一起玩以下翻转游戏:给定一个仅包含这两个字符的字符串:namespace textMuc { public class Class1 { private static int capdo = 100; public int Muc { get { return capdo; } set { capdo = value; } } } } +,您和您的朋友轮流连续翻转两个{{1进入-。当一个人不能再采取行动时,游戏结束,因此另一个人将成为胜利者。

编写一个函数来确定首发球员是否可以保证获胜。

例如,给定s = "++",返回true。首发玩家可以通过将中间"--"翻转为"++++"来保证获胜。

这是我的代码:

"++"

它有效,但我不知道如何计算时间复杂度,因为函数会一直调用自己直到返回false。有人在这里分享一些想法吗?

同样在搜索过程中,我们会遇到重复计算,所以我想我可以使用hashmap来避免这些重复。 Key:String,Value:Boolean。

我使用hashmap更新的代码:

"+--+"

不过,我想知道如何分析这里的时间和空间复杂性?

1 个答案:

答案 0 :(得分:2)

取n = arr.length - 1

首先通过n次递归调用。对于每个你已经删除了两个+,所以每个最多将有n-2个递归调用,依此类推。

所以你最多有n + n(n-2)+ n(n-2)(n-4)+ ......递归调用。

本质上这是n !!(1 + 1/2 + 1 /(2 * 4)+ 1 /(2 * 4 * 8)+ ...)因为1 + 1/2 + 1 /(2 * 4)+ 1 /(2 * 4 * 8)+ ...收敛,≤2,你有O(n !!)

关于内存,每个递归调用都有一个长度为n的数组,所以你有n + n n + n n n + n ...(n / 2次)... * n = n(n ^(n / 2)-1)/(n-1),这是O(n ^(n / 2))

这显然表明没有比详尽搜索更好的表现。

对于散列改进,您要求使用代码创建的所有可能组合。但是,你的代码与实际创建所有组合的代码差别不大,除了你用两个代替两个+的事实,这是通过某种因素而不是它的水平来降低复杂性。 。总的来说,最坏的情况与n / 2个位置中的比特组合的数量相同,即2 ^(n / 2)。观察到哈希函数本身可能有一些隐藏的日志,所以总复杂度将是搜索O(2 ^(n / 2)* ln(n / 2))和内存O(2 ^(n / 2))。

这是最糟糕的情况。但是,如果有安排让你无法获胜,那么当没有获胜策略时,上述内容实际上就是你需要依赖的复杂性。

平均情景的问题是你能够/不能获胜的案件数量及其在所有安排中的分配问题。这个问题与您的算法没什么关系,需要一套完全不同的工具才能解决。

经过一段时间检查上述推理是否正确无论如何,我对结果非常满意,因为它告诉我所有我需要知道的事情。你不能指望你会有一个有利的安排,我真的怀疑你只有0.01%的最坏情况安排,所以你需要准备最坏情况的情况,除非这是一些特殊的项目后面 - 信封计算是你的朋友。

无论如何,这些类型的计算是为了正确准备测试用例,而不是正确的最终实现。使用测试,你可以找到O()中隐藏的因素,考虑到编译器,内存消耗,分页等。

尽管如此,我们仍然不能保留这一点,当然,我们总能改进背后的推理。例如,实际上每个步骤都没有n-2,因为它取决于奇偶校验。例如+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ,甚至是n-4。所以半数的调用可能有n-3个递归调用,这些调用将是n / 2(n-3)+ n / 2(n-2)= n(n-5/2)

观察,因为n!= n !!(n-1)!!我们可以拿n !!≈√n!,再次n!= n !!!(n-1)!!!(n-2)!!!或者是!!!≈∛n!这可能导致我们应该得到类似O((n!)^(5/2))的结论。测试会告诉我在O((n!)^(x))中我们可以减少多少x = 3.

(在一个特定形式中寻找复杂性是很正常的,就像我们有O((n!)^(x))一样,尽管它可以用不同的方式表达。所以我会继续使用复杂形式O( (N!)^(X)),1≤x≤3)