【问题标题】:Time compleity of dynamic arrays in CC中动态数组的时间复杂度
【发布时间】:2020-12-13 10:39:30
【问题描述】:

当我们考虑 Java 或 python (list) 中的动态数组时,附加元素的平均时间复杂度为 O(1),最差时间复杂度为 O(n)强>。假设我们试图追加“m”个元素,那么每次我们达到最大数组大小时,我们将大小加倍,然后复制所有需要 2m+m = 3m 的元素,因此,O(m) 来追加 m 个元素(摊销)。

问题:考虑 C 语言,其中,为了有一个动态数组,我们有一个指向大小为 m 的数组开头的指针。现在,如果指针达到最大大小,那么我们使用 realloc() 来增加大小,然后使用 realloc() 将指针指向新创建的数组的第一个位置。因此,我们不会再次复制所有元素,而是只是移动指针以指向新创建的大小增加的数组。那么这将如何影响时间复杂度呢?

C 示例代码取自:here

enter code here
#include <stdio.h>
#include <stdlib.h>

int main()
{
int *ptr, i , n1, n2;
printf("Enter size: ");
scanf("%d", &n1);

ptr = (int*) malloc(n1 * sizeof(int));

printf("Addresses of previously allocated memory: ");
for(i = 0; i < n1; ++i)
     printf("%u\n",ptr + i);

printf("\nEnter the new size: ");
scanf("%d", &n2);

// rellocating the memory
ptr = realloc(ptr, n2 * sizeof(int));

printf("Addresses of newly allocated memory: ");
for(i = 0; i < n2; ++i)
     printf("%u\n", ptr + i);

free(ptr);

return 0;

}

Enter size: 2
Addresses of previously allocated memory:26855472
26855476
Enter the new size: 4
Addresses of newly allocated memory:26855472
26855476
26855480
26855484

【问题讨论】:

  • 您如何看待realloc 提供了一个与旧数组内容相同的“新创建的数组”?
  • realloc 的重点在于它隐式分配了一个请求大小的新缓冲区,复制旧大小和新大小之间最小的那个,释放原始缓冲区,然后返回指向新的缓冲区。一个简单的realloc 实现的结果实际上是malloc() + memcpy() + free()。实际实现可以检查是否可以简单地扩展现有分配,但没有理由假设不会发生副本。
  • @Gerhardh 如果是这样的话,这是有道理的。让我阅读更多关于 realloc() 的内容。谢谢!
  • @HoneyKhandelwal,虽然realloc() 可能在某些时候能够就地重新分配,但您不能依赖它来做到这一点,也不能轻易预测它何时会做。那么,对于渐近复杂性分析,您必须假设它分配一个单独的块并执行适当的复制,就像您为 Java 和 Python 描述的那样。
  • @Gerhardh:关于“首先,realloc 将检查当前块之后是否有更多可用内存”:这不是realloc 规范的一部分,不应依赖在没有实现的规范文档的情况下。有多种内存分配方案。如果在一个上下文中的研究显​​示许多分配,例如 16 字节或更少,mallocrealloc 可能会使用位图来管理固定 16 字节大小的块的大型池和其他大小的其他方案…

标签: arrays c data-structures dynamic


【解决方案1】:

我不确定你在做什么,但我假设你想在代码中的某个地方使用

tmp = realloc(my_array, new size);
if(!tmp) {
//errors
}
my_array = tmp

而不是

new_array = malloc(array new size);
if(!new_array){
//errors
}
copy_array(my_array, new_array); //by a for loop or stuff like that
free(my_array);
my_array = new_array;

实际上两者是完全一样的,realloc 只是一个更具表现力的版本,它先执行 malloc 然后复制(也可能更快、更优化)。

realloc 是这样工作的:

  • 如果可能,增加/减少当前为指针分配的区域大小并返回它。
  • 如果没有(我们要扩展的区域前面已经分配了空间),请在 RAM 中找到其他位置并在那里分配,然后将旧区域复制到新区域中并返回指向新区域的指针。李>
  • 如果没有找到地方,返回null

因此,如果您在重新分配时仍然使用将大小加倍的技巧,则整体复杂性不会改变。它将是摊销常数,最坏情况下是线性的。 如果您不使用技巧,例如,如果您在每次插入时重新分配,则复杂性将是最坏情况线性、平均线性、最佳情况常量。

编辑: realloc 并不是为了降低复杂性,但它可以提高性能,因为它不会复制元素。请注意,从编译器到另一个编译器以及您选择的源,它可能会有所不同。 使用 realloc 的另一个好理由是更清楚你的意图。 最后,不要像 myptr = realloc(myptr) 那样重新分配指向自身的指针,因为重新分配仍然会失败并返回 null,并且在覆盖之前不会释放 myptr,这会导致内存泄漏。

【讨论】:

  • 谢谢,这很有帮助!
  • Re "realloc 并不是为了降低复杂性",的确。复杂性不是性能的衡量标准;它是衡量某事物如何扩展的指标。
  • @chux-ReinstateMonica Ouups 我的错,现在解决这个问题,谢谢!
猜你喜欢
  • 2014-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
相关资源
最近更新 更多