Algorithm While循环中包含收缩列表的算法的大O表示法

Algorithm While循环中包含收缩列表的算法的大O表示法,algorithm,time-complexity,big-o,graph-algorithm,asymptotic-complexity,Algorithm,Time Complexity,Big O,Graph Algorithm,Asymptotic Complexity,我试图估计一个算法的最坏情况,看起来像这样(注释中的估计复杂度是我的,V是顶点数,E是图中的边数): while(nodes.size()!=0){//O(V),其中节点是链接列表 顶点u=Collections.min(节点);//O(V) nodes.remove(u);//O(1) for(Map.Entry邻接:u.adj.entrySet()){//O(E) //关于O(1)语句的几个问题 if(nodes.contains(v)){//O(v) //关于O(1)语句的几个问题 } }

我试图估计一个算法的最坏情况,看起来像这样(注释中的估计复杂度是我的,
V
是顶点数,
E
是图中的边数):

while(nodes.size()!=0){//O(V),其中节点是链接列表
顶点u=Collections.min(节点);//O(V)
nodes.remove(u);//O(1)
for(Map.Entry邻接:u.adj.entrySet()){//O(E)
//关于O(1)语句的几个问题
if(nodes.contains(v)){//O(v)
//关于O(1)语句的几个问题
}
}
}   
我的问题很简单: 在
while
循环中的每一轮之后,
节点
链接列表
将变得越来越小。 最终,
Collections.min()
nodes.contains()
操作每轮所需的时间都会减少

我的理解是,大O符号总是考虑最差的,因此上述复杂性应该是正确的


否则,请您解释一下如何在上述场景中计算出正确的复杂性?是的,这些看起来是正确的。把它们放在一起,你会得到时间
O(V*(V+E))
。(更正,
O((1+E)*V^2)
-我错过了
O(V)
内循环的
O(E)
。)


然而,你的理解有一个重要的修正。大O符号并不总是最坏的情况。表示法是估计数学函数增长的一种方法。这些函数是最坏情况,还是平均值,或者它们的度量值完全取决于手头的问题。例如,快速排序可以在
O(n^2)
最坏情况下的运行时间中实现,使用
O(n log(n))
平均运行时间,使用
O(log(n))
平均额外内存和
O(n)
最坏情况下的额外内存。

是的,这些看起来是正确的。把它们放在一起,你会得到时间
O(V*(V+E))
。(更正,
O((1+E)*V^2)
-我错过了
O(V)
内循环的
O(E)
。)


然而,你的理解有一个重要的修正。大O符号并不总是最坏的情况。表示法是估计数学函数增长的一种方法。这些函数是最坏情况,还是平均值,或者它们的度量值完全取决于手头的问题。例如,快速排序可以在
O(n^2)
最坏情况下的运行时间中实现,使用
O(n log(n))
平均运行时间,使用
O(log(n))
平均额外内存和
O(n)
在最坏的情况下需要额外的内存。

您可以在每个步骤中获取最大的可能值,但这可能会给出过高估计的最终值。为了确保该值是准确的,您可以将上界保留到最后,但通常它最终都是相同的

V
的值改变时,引入另一个变量
V
,它是一个特定迭代的值。那么每次迭代的复杂度是
v+(E*v)
。总复杂度是每次迭代的总和:

sum(v = 1...V) v+(E*v)
= 1+1E + 2+2E + 3+3E + ... + V+VE                 - Expand the sum
= (1 + 2 + 3 + ... + V) + (1 + 2 + 3 + ... + V)E  - Regroup terms
= (V^2 + V)/2 + (V^2 + V)E/2                      - Sum of arithmetic series
= (V^2 + V + EV^2 + EV)/2                         - Addition of fractions
= O(EV^2)                                         - EV^2 greater than all other terms

您可以在每个步骤中获取最大的可能值,但这可能会给出过高估计的最终值。为了确保该值是准确的,您可以将上界保留到最后,但通常它最终都是相同的

V
的值改变时,引入另一个变量
V
,它是一个特定迭代的值。那么每次迭代的复杂度是
v+(E*v)
。总复杂度是每次迭代的总和:

sum(v = 1...V) v+(E*v)
= 1+1E + 2+2E + 3+3E + ... + V+VE                 - Expand the sum
= (1 + 2 + 3 + ... + V) + (1 + 2 + 3 + ... + V)E  - Regroup terms
= (V^2 + V)/2 + (V^2 + V)E/2                      - Sum of arithmetic series
= (V^2 + V + EV^2 + EV)/2                         - Addition of fractions
= O(EV^2)                                         - EV^2 greater than all other terms

节点。contains
Θ(V)
中具有最坏情况的时间复杂度,for循环在
Θ(E)
中运行多次,因此在
Θ(V*E)
集合中具有最坏情况的时间复杂度。min在
Θ(V)
中具有最坏情况的时间复杂度,因此while循环体在
Θ(V+V*E)
中具有最坏情况下的时间复杂度,但是
V+V*E
本身就是
Θ(V*E)
(见下文),因此while循环体在
Θ(V*E)
中具有最坏情况下的时间复杂度。while循环执行
V
次。因此,执行算法的最坏情况时间是
Θ(V^2*E)

这里的简化——将
Θ(V+V*E)
替换为
Θ(V*E)
——是可以接受的,因为我们正在研究
V>1
的一般情况。也就是说,
V*E
总是比
V
大,所以我们可以将
V
吸收到一个有界常数因子中。说最差的施法时间在
Θ(V^2+E*V^2)
中也是正确的,但人们不会使用它,因为简化形式更有用


顺便说一句,凭直觉,您通常可以忽略在算法过程中容器“用完”的影响,例如插入排序需要查看的项目越来越少,或者此算法需要扫描的节点越来越少。这些影响转化为恒定因素并消失。只有当您每次都要消除一些有趣的元素时,例如使用quickselect算法或二进制搜索,这类事情才会开始影响渐进运行时间。

节点。contains
Θ(V)
中具有最坏的时间复杂度,for循环在
Θ(E)中运行多次
和so在
Θ(V*E)
集合中具有最坏情况的时间复杂度。min
Θ(V)
中具有最坏情况的时间复杂度,因此while循环体在
Θ(V+V*E)
中具有最坏情况的时间复杂度,但
V+V*E
本身就是
Θ(V*E)
(见下文),因此while循环的主体在
Θ(V*E)
中具有最坏的时间复杂度。whil