有时候,仔细检查一下,很容易确定算法的时间复杂度。具有两个嵌套循环算法显然为。该探索的所有可能的组合的算法两个值中的基团是明显。N 2 N 2 N
但是,我不知道如何“发现”具有复杂度的算法。例如,递归mergesort实现就是一个。mergesort或其他算法的共同特征是什么,如果我正在分析的话会给我一个提示?Θ (N log N )
我敢肯定,算法具有复杂度的方法不止一种,因此任何答案都值得赞赏。顺便说一句,我正在寻求一般特征和提示,而不是严格的证明。
有时候,仔细检查一下,很容易确定算法的时间复杂度。具有两个嵌套循环算法显然为。该探索的所有可能的组合的算法两个值中的基团是明显。N 2 N 2 N
但是,我不知道如何“发现”具有复杂度的算法。例如,递归mergesort实现就是一个。mergesort或其他算法的共同特征是什么,如果我正在分析的话会给我一个提示?Θ (N log N )
我敢肯定,算法具有复杂度的方法不止一种,因此任何答案都值得赞赏。顺便说一句,我正在寻求一般特征和提示,而不是严格的证明。
Answers:
原型是一个分而治之的算法,它可以在线性时间内划分(并重新组合)工作,然后重复执行。合并排序的工作方式是:花时间将输入分为两个大致相等的部分,递归地对每个部分进行排序,然后花费时间将两个已排序的两半合并。O (n )Θ (n )
直觉上,继续采用分而治之的思想,每个分割阶段总共花费线性时间,因为分割所要花费的时间是线性的,所以要分割的件数的增加恰好与零件尺寸的减小相匹配。总运行时间是一个划分阶段的总成本乘以划分阶段数的乘积。由于每次都将碎片的大小减半,因此有分割阶段,因此总运行时间为。(直到一个乘法常数,对数的底数是无关紧要的。)ñ ⋅ 日志(Ñ )
将其放在等式()中,估算这种算法的运行时间的一种方法是递归表示:。显然,此算法花费的时间比线性时间还多,我们可以通过除以来了解更多: 当翻倍时,增加一个常数:对数增加,换句话说,。T (n )= 2 T (n / 2 )+ Θ (n )n T (n )nT(n)/nT(n)/nT(n)=Θ(nlogn)
这是更一般的模式的一个实例:母定理。对于任何递归算法,其将它的大小的输入成块尺寸的和花费时间进行拆分重组,运行时间满足。这导致取决于和的值以及的形状的闭合形式。如果并且,则主定理指出。一个Ñ / b ˚F (Ñ )ţ (Ñ )= 一个⋅ Ť (ñ / b )+ ˚F (Ñ )一个b ˚F 一个= b ˚F (Ñ )= Θ (Ñ )ţ (Ñ )= Θ (Ñ 登录n )
另外两种需要时间的算法:
依次处理每个项目并且需要对数时间来处理每个项目的算法(例如HeapSort或许多平面扫描计算几何算法)。
运行时间主要由排序预处理步骤决定的算法。(例如,在最小生成树的Kruskal算法中,第一步可以按权重对边缘进行排序)。
另一类:输出的大小为,因此Θ (n log n )的运行时间在输出大小中是线性的。
尽管此类算法的细节通常使用分而治之的技术,但不一定必须如此。运行时间从根本上取决于所提问题,因此我认为值得一提。
这出现在基于增强型二进制搜索树的数据结构中,其中每个节点存储一个线性大小的数据结构,以搜索该节点的子树中的叶子。这种数据结构经常出现在几何范围搜索中,并且通常基于分解方案。请参阅Agarwal的调查。
对于一个具体示例,请考虑range-tree,它是为回答二维正交范围查询而构建的。尽管后来使用某些压缩技术将多个对象打包成一个单词来减少了空间,但是数据结构的教科书(也是最直观的)版本需要空间(每个叶子在每个位置存储在一个辅助结构中)从叶到根的路径上的节点,或在O (log n )位置),构造算法在空间需求中采用线性时间。
通常,分而治之算法将产生O(N log N)复杂度。
for (i = 0; i < constant; i++){
for(j = 0; j < n; j++){
// Do some O(1) stuff on the input
}
}
// Alternative Variant:
for (i = 0; i < constant; i++){
for(j = n; j < constant; j++){
// Do some O(1) stuff on the input
}
}
我无法给出使用此循环的算法的具体示例,但是在编写自定义算法时经常会出现这种情况。