英文:
How to find time complexity of connected components graph algorith,
问题
我正在尝试找出以下算法的时间复杂度。到目前为止,我有两个选项:O(VE) 和 O(V + E)(其中V是顶点数,E是边数)。但我无法确定。
以下是算法:
对于图中的每个节点:
将其组件设置为其值; // O(n)
对于图中的每条边: // O(v)
如果其端点位于不同的组件中
将它们各自的组件中的所有节点设置为相同的组件 // O(n)
我已经得到了特定部分的最坏情况时间复杂度(如上所示),但不知道整体的时间复杂度如何。该如何从中得到总体时间复杂度呢?
谢谢!
我尝试了获取算法各个部分的复杂度。我不知道如何从中获取整体复杂度。
我有这个备选算法。对于邻接表,我猜测它的复杂度应该是O(EV + V^2)。我对这个比第一个更有信心,但如果有人能确认一下就好了!
组件(G):
对于图中的所有顶点v:
componentOf[v] = -1
compID = 0 // 组件ID
对于图中的所有顶点v:
如果componentOf[v] == -1:
dfsComponent(G, v, compID)
compID = compID + 1
dfsComponent(G, v, id):
componentOf[v] = id
对于图中的每个边(v, w):
如果componentOf[w] == -1:
dfsComponent(G, w, id)
英文:
I am trying to find the time complexity of the following algorithm. So far I have two options: O(VE) and O(V + E) (where V is the number of vertices and E the number of edges). I just can't figure it out though.
Here is the alorithm:
for each node in the graph:
set its component to its value; // O(n)
for every edge in the graph: // O(v)
if its end vertices are in different components
set all the nodes in each of their components to the same component // O(n)
I have the worst case time complexities of specific parts (indicated above) but not the whole thing. How do I get the overall time complexity from this?
thanks!
I tried getting the complexity of individual sections of the algorithm. I don't know how to get the overall complexity from this.
I have this alternative algorithm. For an adjacency list, I am guessing this would be O(EV + V^2). I am more confident about this one than the first one, but if someone could confirm that would be great!
components(G):
for all vertices v ∈ G:
componentOf[v] = -1
compID = 0 // component ID
for all vertices v ∈ G:
if componentOf[v] == -1:
dfsComponent(G, v, compID)
compID = compID + 1
dfsComponent(G, v, id):
componentOf[v] = id
for each (v,w) ∈ edges(G):
if componentOf[w] == -1:
dfsComponent(G, w, id)
答案1
得分: 0
以下是您要翻译的内容:
由于输入的变量是 𝑉 和 𝐸,在您的分析中不应该出现类似于 O(𝑛) 的东西。此外,第二个(外部)循环的迭代次数取决于 𝐸 而不是 𝑉。
其次,步骤“将它们各自的所有节点设置为相同的组件”可以以不同的方式实现:
- 选择一个新的组件号并更新两个组件中的节点
- 读取第一个组件中使用的组件号,并将第二个组件中的节点更新为该组件号
- 检查这两个组件中哪一个较小(通过维护每个组件号的 大小),并更新 该 组件中的节点以获取另一个组件的组件号(并更新所选组件号的大小)。
变种 1
在第一种变种中,最坏的情况发生在我们访问的每个边连接一个到目前为止仍然是独立组件的节点。这意味着我们必须更新逐渐增加的节点数量的组件号:2 + 3 + 4 + ... + 𝑉。这比三角数少一个,因此代表着 O(𝑉²)。再加上我们必须访问所有边,包括那些连接已知属于同一组件的节点的边,我们最终得到 O(𝐸 + 𝑉²)。
变种 2
在最坏的情况下,第二种变种与第一种具有相同的复杂度,因为我们可能不走运,总是更新大组件的节点,使其具有节点自身的组件号。
变种 3
第三个变种改进了这一点。
如果我们在第一个算法中进行了修正,那么在以前的变种中会成为最坏情况(将一个到目前为止孤立的节点添加到一个增长的组件),在这里变为最佳情况:每个添加的边仅代表 O(1) 的工作(仅更新一个节点的组件号)。这对于这种变种来说不是最坏情况。现在最坏情况是两个节点属于大小差不多的组件。我们可以想象大小为 1 + 1 = 2,2 + 2 = 4,... 等。我们可以看到特定节点将在最多 log𝑉 次合并中成为一部分,因为它所属的组件将在每次合并时增加一倍的大小。这导致了时间复杂度为 O(𝐸 + 𝑉log𝑉)。
DFS 算法
您在最后呈现的 DFS 算法再次改进了。如果我们将图视为无向图,那么边最多会被遍历两次:一次是在每个方向上,第二次不会有递归调用,因为终点节点已经被访问过(并且具有组件号)。我假设边的列表 (v,w)
可以从邻接列表中检索,其时间与节点 v
具有的边的数量成正比(这在我们有邻接 矩阵 的情况下并不成立)。
就节点而言,我们可以看到我们至少需要在主循环中访问每个节点一次。
因此,其时间复杂度为 O(𝐸 + 𝑉)。
英文:
As the variables of the input are 𝑉 and 𝐸, there shouldn't be something like O(𝑛) in your analysis. Also, the second (outer) loop will have a number of iterations that depends on 𝐸, not 𝑉.
Secondly, the step "set all the nodes in each of their components to the same component" can be implemented in different ways:
- Choose a new component number and update the nodes in both components
- Read the component number used in the first component, and update the nodes in the second component to that component number
- Check which of the two components is the smallest (by maintaining a size per component number), and update the nodes in that component to get the component number of the other component (and update the size of the chosen component number).
Variant 1
In the first variant, the worst case occurs when each edge we visit, connects a single component with a node that is so far a component on its own. That means we have to update the component number of a steadily increasing number of nodes: 2 + 3 + 4 + ... + 𝑉. This is one less than a triangular number, and so represents O(𝑉²). Add to this that we have to visit all edges, also those that connect nodes that are already known to belong to the same component, and we end up with O(𝐸 + 𝑉²).
Variant 2
In the worst case, the second variant has the same complexity as the first, as we may be unlucky and always update the nodes of the large component with the component number of the node that is a component on its own.
Variant 3
The third variant improves on this.
If we correct that in the first algorithm, then what would be a worst case in the previous variants (adding a so-far isolated node to a growing component), becomes a best case here: each added edge would then represent O(1) work (updating the component number of one node only). This is not the worst case for this variant. Now it is worst case if the two nodes belong to components that are about the same size. We can imagine sizes 1 + 1 = 2, and 2 + 2 = 4, ...etc. We can see that a specific node will be part of a merge at most log𝑉 times, because the component it belongs to will double in size at each merge. This leads to a time complexity of O(𝐸 + 𝑉log𝑉).
DFS algorithm
The DFS algorithm you presented at the end is again an improvement. If we consider the graph to be undirected, then an edge will be traversed at most two times: once in each direction, and the second time no recursive call will follow, as the end node will already have been visited (and have a component number). I assume the list of edges (v,w)
can be retrieved from an adjacency list in a time that is linear to the number of edges the node v
has (which would not be the case when we have an adjacency matrix).
In terms of nodes we can see we will have to visit each node at least once in the main loop.
So its time complexity is O(𝐸 + 𝑉).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论