我遇到了一些肯定可以改进的代码,但我想知道我改进的Big-O符号。
他们的原始代码为数组添加了一个元素,每次执行此操作时,它会创建一个新的n + 1数组,并像这样复制旧数组:
public MyType GetNewType()
{
MyType[] tempTypes = new MyType[_types.Count + 1];
_types.CopyTo(tempTypes, 0);
_types = tempTypes;
_types[types.Count - 1] = new MyType();
return _types[types.Count - 1];
}
据我所知,这将是一个O(n)操作。因此,我将其改写如下:
private int _currentIndex; //initialized in the constructor
public MyType GetNewType()
{
if (_types.Length == _currentIndex)
{
MyType[] tempTypes = new MyType[_types.Length + 10];
_types.CopyTo(tempTypes, 0);
_types = tempTypes;
}
_types[_currentIndex] = new MyType();
_currentIndex++;
return _types[_currentIndex - 1];
}
这些更改的结果是否意味着该函数现在将以O(n / 10)运行,因为它每10次调用只需要一次复制操作?或者它不是那么好用吗?
答案 0 :(得分:2)
这是一种常见且优秀的优化。它通常被称为“摊销的恒定时间”,这意味着大多数时候,添加单个元素是O(1),除非它不是。实现者通常会将数组的大小加倍,或者至少乘以1.5,而不是仅添加10个元素。
也就是说,C#有一些非常可爱的内置列表类,它们可以自动完成这一切,并且只要有可能,使用它们就比使用裸阵列更受欢迎。
答案 1 :(得分:2)
就Big-O表示法而言,(n/10)
的复杂性将是O(n)
,因为它并不关心这么小的常量。
答案 2 :(得分:1)
分摊常数时间仅在每次用完免费物品时双倍数组大小时才有效! 如果不是,平均大O符号将始终为O(n)。
每次列表计数等于容量时,C#List实现将数组的大小加倍。
要使插入方法的平均值为O(1),您需要这样的内容:
MyType[] tempTypes = new MyType[Math.Max(8, _types.Length * 2)];