Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 5ded3ee

Browse files
perf: lazy BFS initialization in EdgeBetweennessAnalyzer — O(reachable) per source instead of O(V)
1 parent 4c4e654 commit 5ded3ee

1 file changed

Lines changed: 24 additions & 17 deletions

File tree

Gvisual/src/gvisual/EdgeBetweennessAnalyzer.java

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ public EdgeBetweennessAnalyzer(Graph<String, Edge> graph) {
101101

102102
/**
103103
* Compute edge betweenness centrality using Brandes' algorithm (BFS variant).
104+
*
105+
* <p>Uses lazy initialization: only vertices reachable from each BFS source
106+
* are initialized, avoiding the O(V) per-source setup cost that made total
107+
* initialization O(V²). For sparse or disconnected graphs this is a major
108+
* win since each BFS touches only its connected component.</p>
104109
*/
105110
public void compute() {
106111
betweenness.clear();
@@ -111,54 +116,56 @@ public void compute() {
111116
Collection<String> vertices = graph.getVertices();
112117

113118
for (String s : vertices) {
114-
// BFS from s
115-
Stack<String> stack = new Stack<>();
119+
// BFS from s — lazy init: only reachable vertices are tracked.
120+
// predecessors/dist/sigma use containsKey checks instead of
121+
// pre-populating entries for every vertex in the graph.
122+
ArrayDeque<String> stack = new ArrayDeque<>();
116123
Map<String, List<String>> predecessors = new HashMap<>();
117124
Map<String, Integer> dist = new HashMap<>();
118125
Map<String, Double> sigma = new HashMap<>();
119126

120-
for (String v : vertices) {
121-
predecessors.put(v, new ArrayList<>());
122-
dist.put(v, -1);
123-
sigma.put(v, 0.0);
124-
}
125127
dist.put(s, 0);
126128
sigma.put(s, 1.0);
129+
predecessors.put(s, new ArrayList<>());
127130

128131
Queue<String> queue = new ArrayDeque<>();
129132
queue.add(s);
130133

131134
while (!queue.isEmpty()) {
132135
String v = queue.poll();
133136
stack.push(v);
137+
int distV = dist.get(v);
138+
double sigmaV = sigma.get(v);
134139
for (String w : graph.getNeighbors(v)) {
135-
// First visit?
136-
if (dist.get(w) < 0) {
137-
dist.put(w, dist.get(v) + 1);
140+
// First visit? (not yet in dist map)
141+
if (!dist.containsKey(w)) {
142+
dist.put(w, distV + 1);
143+
sigma.put(w, 0.0);
144+
predecessors.put(w, new ArrayList<>());
138145
queue.add(w);
139146
}
140147
// Shortest path via v?
141-
if (dist.get(w) == dist.get(v) + 1) {
142-
sigma.put(w, sigma.get(w) + sigma.get(v));
148+
if (dist.get(w) == distV + 1) {
149+
sigma.put(w, sigma.get(w) + sigmaV);
143150
predecessors.get(w).add(v);
144151
}
145152
}
146153
}
147154

148-
// Back-propagation
149-
Map<String, Double> delta = new HashMap<>();
150-
for (String v : vertices) delta.put(v, 0.0);
155+
// Back-propagation — only over vertices in the stack (reachable from s)
156+
Map<String, Double> delta = new HashMap<>(stack.size() * 2);
151157

152158
while (!stack.isEmpty()) {
153159
String w = stack.pop();
160+
double deltaW = delta.getOrDefault(w, 0.0);
154161
for (String v : predecessors.get(w)) {
155-
double c = (sigma.get(v) / sigma.get(w)) * (1.0 + delta.get(w));
162+
double c = (sigma.get(v) / sigma.get(w)) * (1.0 + deltaW);
156163
// Find the edge between v and w
157164
Edge edge = findEdge(v, w);
158165
if (edge != null) {
159166
betweenness.put(edge, betweenness.getOrDefault(edge, 0.0) + c);
160167
}
161-
delta.put(v, delta.get(v) + c);
168+
delta.merge(v, c, Double::sum);
162169
}
163170
}
164171
}

0 commit comments

Comments
 (0)