O (1)“常数时间”——所需的时间与输入的大小无关。作为一个粗略的类别,我将在这里包括哈希查找和联合查找等算法,尽管它们实际上都不是O(1)。
O (log (n))“对数”——输入越大,速度越慢,但一旦输入变得相当大,它的变化就不会大到让人担心的程度。如果运行时可以接受合理大小的数据,那么您可以用任意多的额外数据来填充它,这仍然是可以的。
O (n)“线性”-输入越多,花费的时间就越长,在一个平衡的权衡中。三倍的输入大小将花费大约三倍的时间。
O (n log (n))“比二次元更好”——增加输入大小会带来伤害,但仍然是可控的。算法可能还不错,只是潜在的问题比那些可以在线性时间内解决的问题更难(决策相对于输入数据的本地化程度较低)。如果你的输入大小越来越大,不要认为你可以在不改变架构的情况下处理两倍的大小(例如将事情转移到隔夜批量计算,或者不按帧处理)。不过,如果输入大小增加一点也没关系;只是要注意倍数。
O (n ^ 2)二次函数,它只适用于你输入的一定大小,所以要注意它能达到多大。此外,你的算法可能很糟糕——仔细想想是否有O(n log(n))算法可以满足你的需求。一旦你来到这里,就会非常感激我们拥有的这些神奇的硬件。不久以前,你正在做的事情无论从什么实际角度来说都是不可能的。
O (n ^ 3)"立方"和O(n²)在性质上差别不大。同样的评论也适用,甚至更适用。有一个更聪明的算法很有可能将这个时间缩短到更小的时间,例如O(n²log(n))或O(n^2.8…),但话说回来,这很有可能不值得这么麻烦。(您的实际输入大小已经受到限制,因此更聪明的算法可能需要的常数因子可能会淹没它们在实际情况下的优势。而且,思考是缓慢的;总的来说,让电脑来咀嚼可以节省你的时间。)
O (2 ^ n)“指数型”——这个问题要么根本难以计算,要么你就是个白痴。这些问题都有其显而易见的特点。您的输入大小被限制在一个相当具体的硬限制。你很快就会知道你是否符合这个限制。
对于List<int> numbers = new List<int> {1,2,3,4,5,6,7,12,543,7};
O(1)看起来像
return numbers.First();
O(n)看起来像
int result = 0;
foreach (int num in numbers)
{
result += num;
}
return result;
O(nlog (n))是这样的
int result = 0;
foreach (int num in numbers)
{
int index = numbers.Count - 1;
while (index > 1)
{
// yeah, stupid, but couldn't come up with something more useful :-(
result += numbers[index];
index /= 2;
}
}
return result;
O(n2)看起来像
int result = 0;
foreach (int outerNum in numbers)
{
foreach (int innerNum in numbers)
{
result += outerNum * innerNum;
}
}
return result;
let result = 0;
for (num of numbers){
result += num;
}
O(nlog (n))是这样的
let result = 0;
for (num of numbers){
let index = numbers.length - 1;
while (index > 1){
// yeah, stupid, but couldn't come up with something more useful :-(
result += numbers[index];
index = Math.floor(index/2)
}
}
O(n2)看起来像
let result = 0;
for (outerNum of numbers){
for (innerNum of numbers){
result += outerNum * innerNum;
}
}