最小化连续成本的解决方案的复杂性分析

时间:2018-04-19 18:02:15

标签: algorithm heap complexity-theory asymptotic-complexity

这是关于分析流行面试问题解决方案的复杂性。

问题

有一个函数concat(str1, str2)连接两个字符串。函数的成本由两个输入字符串len(str1) + len(str2)的长度来衡量。实现仅使用concat_all(strs)函数连接字符串列表的concat(str1, str2)。目标是最小化总的连续成本。

警告

通常在实践中,你会非常谨慎地在循环中连接字符串对。可以找到一些很好的解释herehere。实际上,我目睹了由此类代码引起的严重性事故。除了警告,让我们说这是一个面试问题。对我来说真正感兴趣的是围绕各种解决方案的复杂性分析。

如果您想考虑问题,可以暂停。我将在下面透露一些解决方案。

解决方案

  1. 天真的解决方案。循环遍历列表并连接 def concat_all(strs): result = '' for str in strs: result = concat(result, str) return result
  2. 最小堆解决方案。我们的想法是首先连接较短的字符串。根据字符串的长度维护字符串的最小堆。每个连接在最小堆之间连接2个字符串,结果被推回到最小堆。直到堆上只剩下一个字符串。 def concat_all(strs): heap = MinHeap(strs, key_func=len) while len(heap) > 1: str1 = heap.pop() str2 = heap.pop() heap.push(concat(str1, str2)) return heap.pop()
  3. Binary concat。可能不直观清楚。但另一个好的解决方案是递归地将列表拆分一半并连接。 def concat_all(strs): if len(strs) == 1: return strs[0] if len(strs) == 2: return concat(strs[0], strs[1]) mid = len(strs) // 2 str1 = concat_all(strs[:mid]) str2 = concat_all(strs[mid:]) return concat(str1, str2)
  4. 复杂性

    我真正在努力并且在这里问的是上面使用最小堆的第二种方法的复杂性。

    我们假设列表中的字符串数为n,所有字符串中的字符总数为m。天真解决方案的上限是O(mn)。 binary-concat具有theta(mlog(n))的精确边界。这是对我来说难以捉摸的最小堆方法。

    我猜它有O(mlog(n) + nlog(n))的上限。第二个术语nlog(n)与维护堆有关;有n个concats,每个concat都在log(n)更新堆。如果我们只关注连接的成本并忽略维护最小堆的成本,那么min-heap方法的总体复杂性可以降低到O(mlog(n))。然后min-heap是一种比二元连接更优化的方法,因为前者mlog(n)是上限,而后者则是精确界限。

    但我似乎无法证明这一点,甚至找不到支持猜测上限的良好直觉。上限是否可以低于O(mlog(n))

1 个答案:

答案 0 :(得分:1)

让我们调用enter image description here字符串1到n的长度,m是所有这些值的总和。

  1. 对于天真的解决方案,显然最坏的情况出现了 m1 几乎等于m,并且你指出了O(nm)复杂度。

  2. 对于最小堆,最坏情况有点不同,它包含任何字符串的相同长度。在这种情况下,它的工作方式与二进制concat的情况完全相同,但您还必须维护最小堆结构。所以是的,它会比现实生活中的案例3贵一点。然而,从复杂的角度来看,两者都在O(m log n),因为我们m > nO(m log n + n log n)可以缩减为O(m log n)

  3. 为了更严格地证明最小堆复杂性,我们可以证明当我们在一组k个字符串中取两个最小的字符串,并用S表示两个最小字符串的总和时,我们就有了:(m-S)/(k-1) >= S/2(它只是意味着两个最小字符串的平均值小于k-2其他字符串的平均值)。重新制定导致S <= 2m/(k+1)。让我们将它应用于最小堆算法:

    • 在第一步,我们可以证明我们采取的2个字符串的总长度最多为2米/(n + 1)
    • 在第一步,我们可以证明我们采取的2个字符串的总长度最多为2米/(n)
    • ...
    • 在最后一步,我们可以证明我们采取的2个字符串的总长度最多为2米/(1)

    因此,最小堆的计算时间为2m*[1/(n+1) + 1/n + ... + 1/2 + 1] <{1}}