合并排序的变体称为a-to-b合并排序(A,low,high)总是将数组A的未排序段从索引低到高除以a:b而不是1:1计算a到b合并排序的时间复杂度。 我试图解决并获得这个: T(n)= aT(n / b)+ O(n)。通过主定理,A)如果a> b T(n)= O(n ^(loga / logb)(B)如果a = b T( n)= O(n ^(loga / logb)* n)(C)如果< b T(n)= O(n ^(loga / logb)这个解是正确的吗?
答案 0 :(得分:3)
为了使数学更容易,而不是考虑分裂的a:b比,让我们假设你有一个小数分裂,将一个元素的ε分数放在一个子序列和剩余的(1-ε)分数在另一个。为简单起见,我们假设1-ε≥ε,以便我们可以讨论数组中较大和较小的一半。
使用这种修改后的合并排序,每次调用仍然完成线性工作,加上执行两次递归调用所需的工作,一次在εn元素上,另一次在(1-ε)n元素上。这意味着我们有递归关系
T(n)= T(εn)+ T((1-ε)n)+ O(n)
那么这种复发解决了什么?好吧,我们可以通过几种不同的方式来思考这个问题。我认为最简单的选择是想象一个递归树并计算每个级别的工作。
想象一下,我们有一个非常大的n并考虑递归树将采用什么形状。在顶层,我们只有一个大小为n的调用。在下面的级别,我们有一个大小为εn的调用和一个大小(1-ε)n。在下面我们有一个尺寸为ε 2 n的调用,两个尺寸为ε(1-ε)n,另一个尺寸为(1-ε) 2 n。 (这将是一个很好的时间来取出一张纸并将其画出来,以便你可以看到发生了什么 - 用视觉效果,这将有很多意义。)
这可能听起来像毛茸茸和可怕,但它实际上并没有那么糟糕。请记住,每次调用所做的工作都是O(n),因此我们可以通过总结树中每个单独层中所有调用所完成的所有工作来确定每个级别的工作。顶层有一个大小为n的调用,因此完成了O(n)工作。如果总结第二层中所有调用的大小,您将看到它也适用于n,因此在该层中完成的总工作量为O(n)。在第三层中数学有点困难,但是所有调用的大小也适用于n,所以O(n)总工作也在该层完成。 (再次,请自行检查以确认您知道原因!)
事实证明,这不是巧合。请注意,每个递归调用都会将数组干净地分成两部分并逐个重复,因此当我们从一个层转到下一个层时,子调用中数组的总大小应始终为n。 (好吧,大多数情况下。最终阵列变得如此之小以至于递归最低点,但在一分钟内就更多了)。这意味着我们每个级别都在进行O(n)工作,因此完成的总工作量将是层数的O(n)倍。那是什么?
好吧,与许多分治算法一样,请注意,在此算法中,每个子阵列的大小始终是小于原始数组的常数小数。无论何时看到这种模式,你都应该快速得出结论:在O(log n)步骤之后,数组缩小到1,我们就完成了。为什么?因为如果重复除以常数,则需要将O(log n)次迭代缩小为1。
总的来说,这个非常粗略的分析告诉我们运行时应该是O(n log n):每层O(n)工作和O(log n)层。
现在,在这里需要注意一些问题,因为递归树的形状有点奇怪。具体来说,由于分割不平衡,树不是完整的二叉树,其分支在收缩因子(1-ε)之前缩小了ε的因子。但这不是问题。如果我们假装所有这些丢失的调用仍然发生,我们最终会完成总工作的近似值,因此我们的O(n log n)绑定在最坏的情况下会夸大运行时。结果是渐渐紧张。一种看待这种情况的方法是在分支之前有O(log n)层缩小了ε因子,并且在对应于该区域的树的部分中我们知道O(n log n)工作已经完成
这看起来像是家庭作业,所以我将作为练习留下填补所有空白的细节,并找出所有对数的基础。祝你好运!