算法:分治法和时间复杂度 O(nlogn) 有啥关系?

Posted

技术标签:

【中文标题】算法:分治法和时间复杂度 O(nlogn) 有啥关系?【英文标题】:algorithms: how do divide-and-conquer and time complexity O(nlogn) relate?算法:分治法和时间复杂度 O(nlogn) 有什么关系? 【发布时间】:2015-07-07 18:39:07 【问题描述】:

在我的算法和数据结构课程中,引入了第一个 divide-and-conquer algorithmmerge sort

在为作业实施算法时,我想到了几个问题。

    使用分而治之范式实现的算法是否具有 O(nlogn) 的时间复杂度?

    是不是该方法中的递归部分有能力将运行在 O(n^2) 的算法浓缩为 O(nlogn)?

    首先是什么让这样的算法在 O(nlogn) 中运行?

对于 (3),我假设这与递归树和可能的递归数有关。有人可能会用一个在 O(nlogn) 中运行的简单分治算法来展示,实际上是如何计算复杂性的?

干杯, 安德鲁

【问题讨论】:

你可能想看看bigocheatsheet.com 【参考方案1】:

我认为您的问题的所有答案都可能来自Master Theorem 它有点告诉您对于您拥有的几乎所有分而治之的解决方案,您的复杂性是什么,是的,它必须使用递归树来做所有事情,通过玩参数你会发现一些分而治之的解决方案不会有 O(nlogn) 复杂度,实际上有divide and conquer algorithms that have O(n) complexity。

就问题2而言,并非总是如此,事实上,有些问题被认为不可能比O(n^2)更快地解决,这取决于问题的性质。

以 O(nlogn) 运行并且我认为具有非常简单、清晰和有教育意义的运行时间分析的算法示例是 MergeSort。可以从下图把握:

所以每个递归步骤将输入分成两部分,然后征服部分花费 O(n),因此树的每一层花费 O(n),棘手的部分可能是递归级别(树高)为 logn。这或多或少很简单。所以在每一步中,我们将输入分成两部分,每部分 n/2 个元素,并递归地重复,直到我们有一些恒定大小的输入。因此,在第一级,我们除以 n/2,在下一个 n/4,然后是 n/8,直到我们达到一个恒定大小的输入,这将是树的一个叶子,以及最后一个递归步骤。

所以在第 i 个递归步骤,我们将 n/2^i 相除,所以让我们在最后一步找到 i 的值。我们需要 n/2^i = O(1),这是在 2^i = cn 时实现的,对于某个常数 c,我们从两边取以 2 为底的对数,得到 i = clogn。所以最后一个递归步骤将是第 clogn 步,因此树具有 clogn 高度。

因此,对于每个 clogn 递归(树)级别,MergeSort 的总成本将是 cn,这给出了 O(nlogn) 复杂度。

一般来说,只要递归步骤具有 O(n) 复杂度,您就可以确信您的算法将具有 O(nlogn) 复杂度,并且您可以划分为 b 个大小为 n/b 的问题,甚至更一般,如果这些部分是 n 的线性分数,加起来为 n。在不同的情况下,您很可能会有不同的运行时。

回到问题 2,在 QuickSort 的情况下,一个可能从 O(n^2) 到 \Theta(nlogn) 正是因为平均随机情况实现了一个很好的分区,尽管运行时分析比这更复杂.

【讨论】:

谢谢哈维尔。你做得非常好,为我节省了数小时的思考时间。 :-) 您能解释一下“n/2^i = O(1), 这是在 2^i = cn” 时实现的吗?对我来说, O(1)=k 。所以我们有 n/(2^i)=k 所以 k*(2^i)=n。这是否意味着您的 c 等于(对于我的 k) 1/k ?【参考方案2】:

不,分而治之不能保证 O(nlogn) 性能。这完全取决于每次递归如何简化问题。

在归并排序算法中,原问题分为两半。然后对结果执行 O(n) 操作。这就是 O(n...) 的来源。

这两个子操作中的每一个现在都有自己的n,它的大小是原来的一半。每次你递归时,你又把问题一分为二。这意味着递归数将为 log2(n)。这就是 O(...logn) 的来源。

【讨论】:

你刚刚是通过Master Theorem挥手吗? @Degustaf 我只是想用最简单的术语解释我自己对这个过程的理解,以提供一些见解。【参考方案3】:

使用分而治之范式实现的算法是否具有 O(nlogn) 的时间复杂度?

平均而言,Quicksort 和 Mergesort 的时间复杂度为 O(n log(n)),但不一定总是这样。 Big O Cheat Sheet

是不是该方法中的递归部分有能力将运行在 O(n^2) 到 O(nlogn) 的算法浓缩?

不仅仅是表面上看到的,它还取决于其他因素,例如每个递归调用与输入相关的操作数。

我强烈推荐这个video,在那里你可以看到为什么 MergeSort 是 O(n log(n))。

是什么让这样的算法首先在 O(nlogn) 中运行。

同样,这只是一个算法消耗多少时间与输入大小相关的指标,因此说算法的时间复杂度为 O (n log (n)) 并没有提供任何信息关于算法是如何实现的,它只是说当输入开始增加很多时,使用的时间不会成正比增加,而是需要更多的时间和更多。

【讨论】:

【参考方案4】:

使用分而治之范式实现的算法是否具有 O(nlogn) 的时间复杂度?

不,分而治之的一般公式是:

2是每个递归调用内部的操作数,是除以子问题的递归调用,是征服的线性操作数

是什么让这样的算法首先在 O(nlogn) 中运行?

对数线性时间的一个很好的例子是合并排序算法m:

方法中的递归部分是否有能力将运行在 O(n^2) 到 O(nlogn) 的算法浓缩?

主定理用于确定分治算法的运行时间

如果重复是这种形式

那么

示例

a = 2
b = 4
d = 1/2

因为 2 = 4 ^ 1/2 情况 2 适用

【讨论】:

以上是关于算法:分治法和时间复杂度 O(nlogn) 有啥关系?的主要内容,如果未能解决你的问题,请参考以下文章

复杂度为 O(n log n) 的分治算法

算法基础快速排序——O(nlogn)

算法基础快速排序——O(nlogn)

归并排序

O(nlogn) 分治算法找到可见线

分治算法总结(未完结)