递归基本转换时间复杂度分析

时间:2017-01-14 19:51:33

标签: c++ algorithm recursion numbers base

  

给定整数adb shell am broadcast -a com.android.intent.action.SET_LOCALE --es com.android.intent.extra.LOCALE "en_US" com.android.customlocale2 和目标基础p,在基础b中返回p的字符串表示形式。该字符串在末尾应该具有最低有效位

^这是我给自己的问题。

我提出的天真递归算法(在C ++中)如下:

b

虽然算法非常简单,但我想确保理解复杂性故障。我的想法在下面,我想知道他们是否正确或错误,如果他们错了,那么知道我在哪里偏离轨道会很好!

要求:

  • string convertIntToBaseRecursive(int number, int base) { // Base case if (!number) return ""; // Adding least significant digit to "the rest" computed recursively // Could reverse these operations if we wanted the string backwards. return convertToBaseRecursive(number / base, base) + to_string(number % base); } 是返回字符串的长度
  • 时间复杂度:n = logb(p)
  • 空间复杂度:O(n^2)

推理:

为了将最低有效位保留在字符串的末尾,当它是我们先计算的值之前,我们要么必须:

  1. 按照
  2. 递归计算字符串
  3. 每次我们计算一点时保持“移位”数组,这样我们可以将最新的位添加到字符串的前面,而不是结尾
  4. 向后写字符串,在返回之前将其反转(最有效)
  5. 我们正在使用上述C ++算法中的第一个方法,O(n)运算符在每个堆栈帧处创建一个新字符串。初始帧创建并返回长度为+的字符串,下一帧创建长度为nn-1n-2的字符串,依此类推。遵循这一趋势(没有进入n-3原因的证明,很明显时间复杂度为1 + 2 + 3 ... + n = O(n^2)。我们也只需要随时将O(n^2) = O(logb^2(p))内容存储在内存中当原始堆栈帧解析时(就在算法完成之前),我们将根据原始字符串获得内存,但在它解析之前,它将使用单个字符(O(n))+递归堆栈帧(O(1))。我们会在每个级别存储O(n)个单个字符,直到完成为止。因此空间复杂度为n

    当然,此解决方案的更高效版本将是

    O(n)

    我相信上述解决方案,其中string convertIntToBaseIterative(int number, int base) { string retString = ""; while (number) { retString += to_string(number % base); number /= base; } // Only needed if least significant reverse(retString.begin(), retString.end()); return retString; } 具有:

    • 时间复杂度:n = logb(p)
    • 空间复杂度:O(n)

    这些分析是正确的还是我在某个地方?

2 个答案:

答案 0 :(得分:0)

由于返回值必须包含输出,因此无法获得比a_1, a_2, a_3, ..., a_n更好的空间复杂度。

假设输出字符串按顺序由以下数字组成:"a_1" + "a_2" + .... + "a_n"。在递归方法中,我们创建一个字符串,如下所示:(((...(a_1) + a_2) + a_3) + ... + a_n)))...)。在迭代方法中,我们执行:O(n^2)。因此,两种情况下的时间复杂度应该在string处相同(在C ++ 03中。请参阅下面的C ++ 11注释)。

如您所见,这两种方法都受到实施细节的严重影响。 n类型对于涉及重复串联的操作不是很有用。如果您有一个预先分配的大小为O(n)的数组,则可以将复杂度降低到O(1)

注1:有一些关于追加操作的细节。在C ++ 03中,追加操作没有指定的复杂性,并且可能导致Copy-On_write(如果字符串无法扩展到位并且需要重定位)。在C ++ 11中,不允许CoW和绳式实现,并且追加应该导致每个角色分摊O(n)个时间。因此,在C ++ 11的情况下,我们应该能够为这两种实现获得O(n)时间复杂度。

注意2 :要使用用户定义的字符串实现(包含长度)获得void convertToBaseRecursive(int number, int base, MyString& str) 时间复杂度,需要在函数中通过引用传递字符串。这将导致函数签名变为:

text-align

如果字符串使用预先分配的数组,此实现将允许字符串共享和更新。

答案 1 :(得分:0)

注意:

鉴于聊天室与@user1952500的对话,我根据我们所讨论的内容对他的回答进行了一些编辑。以下是他的答案的编辑版本,反映了我们所谈论的最新内容和我学到的内容:

编辑回答:

由于返回值必须包含输出,因此无法获得比O(n)更好的空间复杂度。

假设输出字符串按顺序由以下数字组成:a_1, a_2, a_3, ..., a_n。在里面 递归方法(项目符号#1),我们创建一个字符串,如下"a_1" + "a_2" + .... + "a_n",其中包含 递归产生O(n^2)时间复杂度。在子弹#2中,迭代方法不断推动角色 在(((...(a_1) + a_2) + a_3) + ... + a_n)))...)之类的字符串前面移动整个字符串 每个角色的添加也会产生O(n^2)时间复杂度。在你的书面迭代方法(子弹#3) 时间复杂度可以根据C ++的版本进行优化(参见下面的注释)。

字符串类型对于涉及重复串联的操作不是很有用。在旧版本的C ++中你 通过预先分配大小为n的字符串可以实现O(n)时间复杂度。在C ++ 11中this answer表示某些追加操作可以优化为单个字符的摊销O(1)。假设 这是真的,写出的迭代版本将具有O(1)时间复杂度而无需任何额外的工作。

注意:要使用此算法的递归版本获得O(n)时间复杂度,我们可以利用已摊销的O(1) character追加并使用通过引用传递的单个字符串。这将需要递归版本的函数签名 重写如下:

void convertToBaseRecursive(int number, int base, string& str)