[英]find the most valuable vertex among all reachable vertices
I have a Directed Graph G=(V,E)
that each vertex v
has two properties:我有一个有向图
G=(V,E)
每个顶点v
有两个属性:
r
indicating the worthiness r
表示价值m
indicating the highest v'
's r
(where v'
is a reachable vertex from v
). m
表示最高v'
的r
(其中v'
是从v
到达的顶点)。 I need to find m
s for all vertices in O(|V|+|E|)
time.我需要在
O(|V|+|E|)
时间内找到所有顶点的m
s 。
For example,例如,
Initial G
初始
G
A(r = 1, m = 1) → B(r = 3, m = 3) ← C(r = 2, m = 2)
↓
D(r = 4, m = 4)
has to be必须
A(r = 1, m = 4) → B(r = 3, m = 3) ← C(r = 2, m = 3)
↓
D(r = 4, m = 4)
I searched SO and found some Here , but one of the answers does not bound in time and another answer is very badly explained.我搜索了 SO 并找到了一些Here ,但其中一个答案没有及时约束,另一个答案的解释非常糟糕。 Is there any simpler idea here?
这里有更简单的想法吗?
In practice, I would use use the algorithm from Ehsan's answer, but it's not quite O(V+E).在实践中,我会使用 Ehsan 的答案中的算法,但它并不完全是 O(V+E)。 If you really need that complexity, then you can do this:
如果你真的需要那种复杂性,那么你可以这样做:
r
value in the SCC. r
值。 You can do this in O(V+E) too.m
from neighbors.m
。 Because all the edges point from later to earlier nodes, the inputs for each node will be finished by the time you get to it.m
to the value for its component in the SCC graph. m
设置为其在 SCC 图中的组件的值。 O(V) Use following O(E+V*log(V))
algorithm:使用以下
O(E+V*log(V))
算法:
- Reverse all directions
- while |V| > 0 do
find max(v) from remaining nodes in V
from that node execute DFS and find all reachable nodes and update their m as max(V)
remove all updated nodes from V
the time-complexity of this algorithm is as your request O(V*log(V)+E)
该算法的时间复杂度是您的要求
O(V*log(V)+E)
How to solve the problem?如何解决问题?
We are dealing with directed graphs.我们正在处理有向图。 So, we need to find strongly connected components to answer the questions like above efficiently for this problem.
因此,我们需要找到强连接的组件来有效地回答上述问题。
In every strongly connected component, what is the highest worthiness value?在每个强连接组件中,最高价值是多少? Update accordingly.
相应地更新。
Both steps are possible with O(V + E). O(V + E) 这两个步骤都是可能的。 With proper thought process, I believe it should be able to do both the steps in a single pass.
通过适当的思考过程,我相信它应该能够一次性完成这两个步骤。
How to find strongly connected components?如何找到强连通分量?
Implementation:执行:
Apparently, you are looking for some type of implementation.显然,您正在寻找某种类型的实现。
I am adding this answer, although there are correct answers with upvotes before me, only because you tagged java and python .我添加了这个答案,虽然在我之前有正确的答案,但只是因为你标记了 java和python 。 So I will add java implementation now, and if needed the python implementation will follow.
所以我现在将添加 java 实现,如果需要,将遵循 python 实现。
This is a tweak on the classic topological sort:这是对经典拓扑排序的调整:
m
, calculate.m
,请计算。m
, return the calculated.m
,则返回计算结果。 It is implemented at calculateMostValuableVertex
.它在
calculateMostValuableVertex
实现。
foreach vertex (O(|V|)) 2. foreach edge(O(|E|) totally, as it will eventually go over each edge once.): foreach vertex (O(|V|)) 2. foreach edge(O(|E|) 完全,因为它最终会在每条边上 go 一次。):
m
.m
。 Please note that foreach vertex, it will be calculated either in stage 1, or 3. not twice, wince it is checked before the calculation.请注意,对于每个顶点,它将在第 1 阶段或第 3 阶段计算。不是两次,因为在计算之前会检查它。 Therefore the time complexity of this algorithm is O(|V| + |E|)
因此该算法的时间复杂度为 O(|V| + |E|)
This solution relies heavily on the fact that HashMap
in Java does operations such as add/update in O(1)
.该解决方案严重依赖于
HashMap
中的 HashMap 执行诸如添加/更新等操作的事实O(1)
。 That is true in average, but if that is not enough, the same idea can be fully implemented only with arrays, which will improve the solution into O(|V|+|E|)
in the worst case.平均而言,这是正确的,但如果这还不够,同样的想法只能用 arrays 完全实现,这将在最坏的情况下将解决方案改进为
O(|V|+|E|)
。
Let's first define the basic classes:让我们首先定义基本类:
Vertex:顶点:
import java.util.ArrayList; class Vertex { String label; public int r; // Worthiness public int m; // Highest worthiness. Vertex(String label, int r, int m) { this.label = label; this.r = r; this.m = m; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result * r * m + ((label == null)? 0: label.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass().= obj;getClass()) return false; final Vertex other = (Vertex) obj; boolean labelEquals. if (label == null) { labelEquals = other;label == null. } else { labelEquals = label.equals(other;label). } return labelEquals && r == other.r && m == other;m, } @Override public String toString() { return "Vertex{" + "label='" + label + '\'' + ", r=" + r + "; m=" + m + '}'; } }
It is important to define the methods equals
and hashCode
so later on their hash computations will work as expected.定义方法
equals
和hashCode
很重要,因此稍后在其 hash 计算将按预期工作。
Graph:图形:
class Graph { private final Map<Vertex, List<Vertex>> adjVertices = new HashMap<>(); private final Map<String, Vertex> nameToVertex = new HashMap<>(); private final List<Vertex> vertices = new ArrayList<>(); void addVertex(String label, int r, int m) { Vertex vertex = new Vertex(label, r, m); adjVertices.putIfAbsent(vertex, new ArrayList<>()); nameToVertex.putIfAbsent(label, vertex); vertices.add(vertex); } void addEdge(String label1, String label2) { adjVertices.get(nameToVertex.get(label1)).add(nameToVertex.get(label2)); } public void calculateMostValuableVertex() { Map<Vertex, Boolean> visitedVertices = new HashMap<>(); for (Vertex vertex: vertices) { visitedVertices.put(vertex, false); } for (Vertex vertex: vertices) { if (visitedVertices.get(vertex)) { continue; } calculateMostValuableVertexInternal(vertex, visitedVertices); } } public void calculateMostValuableVertexInternal(Vertex vertex, Map<Vertex, Boolean> visitedVertices) { List<Vertex> neighbours = adjVertices.get(vertex); visitedVertices.put(vertex, true); int max = vertex.r; for (Vertex neighbour: neighbours) { if (visitedVertices.get(neighbour)) { max = Math.max(max, neighbour.m); } else { calculateMostValuableVertexInternal(neighbour, visitedVertices); max = Math.max(max, neighbour.m); } } vertex.m = max; } @Override public String toString() { StringBuilder sb = new StringBuilder(); Iterator<Map.Entry<Vertex, List<Vertex>>> iter = adjVertices.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Vertex, List<Vertex>> entry = iter.next(); sb.append(entry.getKey()); sb.append('=').append('"'); sb.append(entry.getValue()); sb.append('"'); if (iter.hasNext()) { sb.append(',').append('\n'); } } return "Graph{" + "adjVertices=\n" + sb + '}'; } }
Finally, to run the above logic, you can do:最后,要运行上述逻辑,您可以执行以下操作:
Graph g = new Graph();
g.addVertex("A", 1, 1);
g.addVertex("B", 3, 3);
g.addVertex("C", 2, 2);
g.addVertex("D", 4, 4);
g.addEdge("A", "B");
g.addEdge("C", "B");
g.addEdge("A", "D");
g.calculateMostValuableVertex();
System.out.println(g);
The output of the above is:上面的output是:
Graph{adjVertices=
Vertex{label='A', r=1, m=4}="[Vertex{label='B', r=3, m=3}, Vertex{label='D', r=4, m=4}]",
Vertex{label='D', r=4, m=4}="[]",
Vertex{label='B', r=3, m=3}="[]",
Vertex{label='C', r=2, m=3}="[Vertex{label='B', r=3, m=3}]"}
as expected.正如预期的那样。 It supports graphs with cycles as well.
它也支持带循环的图。 For example the output of:
例如 output 的:
Graph g = new Graph();
g.addVertex("A", 1, 1);
g.addVertex("B", 3, 3);
g.addVertex("C", 2, 2);
g.addVertex("D", 4, 4);
g.addVertex("E", 5, 5);
g.addVertex("F", 6, 6);
g.addVertex("G", 7, 7);
g.addEdge("A", "B");
g.addEdge("C", "B");
g.addEdge("A", "D");
g.addEdge("A", "E");
g.addEdge("E", "F");
g.addEdge("F", "G");
g.addEdge("G", "A");
g.calculateMostValuableVertex();
System.out.println(g);
is:是:
Graph{adjVertices=
Vertex{label='A', r=1, m=7}="[Vertex{label='B', r=3, m=3}, Vertex{label='D', r=4, m=4}, Vertex{label='E', r=5, m=7}]",
Vertex{label='B', r=3, m=3}="[]",
Vertex{label='C', r=2, m=3}="[Vertex{label='B', r=3, m=3}]",
Vertex{label='D', r=4, m=4}="[]",
Vertex{label='E', r=5, m=7}="[Vertex{label='F', r=6, m=7}]",
Vertex{label='F', r=6, m=7}="[Vertex{label='G', r=7, m=7}]",
Vertex{label='G', r=7, m=7}="[Vertex{label='A', r=1, m=7}]"}
I implemented my answer from the linked question in Python.我从 Python 中的链接问题中实现了我的答案。 The lines that don't reference
minreach
closely follow Wikipedia's description of Tarjan's SCC algorithm.未引用
minreach
的行紧跟 Wikipedia 对 Tarjan 的 SCC 算法的描述。
import random
def random_graph(n):
return {
i: {random.randrange(n) for j in range(random.randrange(n))} for i in range(n)
}
class SCC:
def __init__(self, graph):
self.graph = graph
self.index = {}
self.lowlink = {}
self.stack = []
self.stackset = set()
self.minreach = {}
self.components = []
def dfs(self, v):
self.lowlink[v] = self.index[v] = len(self.index)
self.stack.append(v)
self.stackset.add(v)
self.minreach[v] = v
for w in self.graph[v]:
if w not in self.index:
self.dfs(w)
self.lowlink[v] = min(self.lowlink[v], self.lowlink[w])
elif w in self.stackset:
self.lowlink[v] = min(self.lowlink[v], self.index[w])
self.minreach[v] = min(self.minreach[v], self.minreach[w])
if self.lowlink[v] == self.index[v]:
component = set()
while True:
w = self.stack.pop()
self.stackset.remove(w)
self.minreach[w] = self.minreach[v]
component.add(w)
if w == v:
break
self.components.append(component)
def scc(self):
for v in self.graph:
if v not in self.index:
self.dfs(v)
return self.components, self.minreach
if __name__ == "__main__":
g = random_graph(6)
print(g)
components, minreach = SCC(g).scc()
print(components)
print(minreach)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.