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

C++ 递归基转换时间复杂度分析,c++,algorithm,recursion,numbers,base,C++,Algorithm,Recursion,Numbers,Base,给定整数p和目标基b,在基b中返回p的字符串表示形式。字符串的末尾应具有最低有效位 ^这就是我给自己的问题 我提出的朴素递归算法(在C++中)如下所示: string convertIntToBaseRecursive(int number, int base) { // Base case if (!number) return ""; // Adding least significant digit to "the rest" computed recursively /

给定整数
p
和目标基
b
,在基
b
中返回
p
的字符串表示形式。字符串的末尾应具有最低有效位

^这就是我给自己的问题

我提出的朴素递归算法(在C++中)如下所示:

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)
  • 空间复杂度:
    O(n)
推理: 为了在字符串末尾保留最低有效位,当它是我们计算的值时,我们必须:

  • 按原样递归计算字符串
  • 每次计算一个位时,请保持数组“移位”,以便将最近的位添加到字符串的前面,而不是末尾
  • 将字符串向后写入,并在返回前将其反转(效率最高)
  • 我们在上面的C++算法中做第一个方法,而+/Cuth>操作符在每个栈帧中创建一个新的字符串。初始帧创建并返回长度为

    n
    的字符串,下一帧创建长度为
    n-1
    n-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)
    • 空间复杂度:
      O(n)

    这些分析正确吗?或者我在什么地方出错了吗?

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

    假设输出字符串按顺序由以下数字组成:
    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)
    (在C++03中。有关C++11,请参见下面的注释)处应该是相同的

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

    注意1:关于追加操作有一些细节。在C++03中,追加操作没有指定的复杂性,并且可能导致在写入时进行复制(如果字符串不能适当扩展并且需要重新定位)。在C++11中,不允许使用CoW和rope样式的实现,追加应该导致每个字符的摊销时间
    O(1)
    。因此,在C++11的情况下,我们应该能够得到两种实现的时间复杂度

    注2:要通过用户定义的字符串实现(包含长度)获得
    O(n)
    时间复杂度,需要在函数中通过引用传递字符串。这将导致函数签名更改为:

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

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

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

    假设输出字符串按顺序由以下数字组成:
    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)
    (在C++03中。有关C++11,请参见下面的注释)处应该是相同的

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

    注意1:关于追加操作有一些细节。在C++03中,追加操作没有指定的复杂性,并且可能导致在写入时进行复制(如果字符串不能适当扩展并且需要重新定位)。在C++11中,不允许使用CoW和rope样式的实现,追加应该导致每个字符的摊销时间
    O(1)
    。因此,在C++11的情况下,我们应该能够得到两种实现的时间复杂度

    注2:要通过用户定义的字符串实现(包含长度)获得
    O(n)
    时间复杂度,需要在函数中通过引用传递字符串。这将导致函数签名更改为:

    void convertToBaseRecursive(int number, int base, MyString& str)
    
    此实现将允许共享字符串并就地更新,前提是该字符串使用的数组