Lesson 122 of 164
Remove an Edge From a Directed Graph
Given a directed graph as an adjacency list and an edge u → v, remove that edge from the graph. Report whether the edge existed.
Example
graph LR
subgraph before["before"]
a0["0"] --> a1["1"]
a0 --> a2["2"]
a1 --> a3["3"]
a2 --> a3
end
subgraph after["after removing 0 -> 2"]
b0["0"] --> b1["1"]
b1 --> b3["3"]
b2["2"] --> b3
endadj before: [[1, 2], [3], [3], []]
adj after: [[1], [3], [3], []]Brute force: rebuild the graph without the edge
Turn the adjacency list back into a list of edges, drop the one to remove, and build a new adjacency list.
import java.util.ArrayList;
import java.util.List;
public class Main {
static List<List<Integer>> removeEdge(List<List<Integer>> adj, int u, int v) {
List<List<Integer>> rebuilt = new ArrayList<>();
for (int i = 0; i < adj.size(); i++) rebuilt.add(new ArrayList<>());
for (int from = 0; from < adj.size(); from++) {
for (int to : adj.get(from)) {
if (!(from == u && to == v)) {
rebuilt.get(from).add(to);
}
}
}
return rebuilt;
}
public static void main(String[] args) {
List<List<Integer>> adj = List.of(List.of(1, 2), List.of(3), List.of(3), List.of());
System.out.println(removeEdge(adj, 0, 2));
}
}[[1], [3], [3], []]Time: O(V + E). Space: O(V + E) for the new graph.
Optimal: edit only adj.get(u)
A directed edge u → v is stored in exactly one place: adj.get(u), an ArrayList<Integer>. Find v in that list and remove it. Nothing else changes.
import java.util.ArrayList;
import java.util.List;
public class Main {
static boolean removeEdge(List<List<Integer>> adj, int u, int v) {
int i = adj.get(u).indexOf(v);
if (i == -1) {
return false;
}
adj.get(u).remove(i); // remove(int index) keeps the order of the remaining neighbors
return true;
}
public static void main(String[] args) {
List<List<Integer>> adj = new ArrayList<>();
adj.add(new ArrayList<>(List.of(1, 2)));
adj.add(new ArrayList<>(List.of(3)));
adj.add(new ArrayList<>(List.of(3)));
adj.add(new ArrayList<>());
System.out.println(removeEdge(adj, 0, 2) + " " + adj);
System.out.println(removeEdge(adj, 0, 2)); // already gone
}
}true [[1], [3], [3], []]
falseList.of(...) creates an immutable list that throws UnsupportedOperationException on remove, so the mutable example copies the lists into ArrayLists. A call like list.remove(v) with an int argument removes the element at index v, not the value v. Use indexOf first, or list.remove(Integer.valueOf(v)) to remove by value.
Time: O(deg(u)): finding v scans u's list, and remove shifts the elements after it. Space: O(1).
Choosing the removal method
| Method | Time | Order of the other neighbors | Removes |
|---|---|---|---|
list.remove(i) |
O(deg u) |
kept | one edge |
list.set(i, list.get(last)) then list.remove(last) |
O(1) after finding i |
changed: the last neighbor moves into slot i |
one edge |
list.removeIf(w -> w == v) |
O(deg u) |
kept | every copy of the edge |
Neighbor order matters only if something depends on it, such as the exact BFS or DFS visiting order. Otherwise, swapping with the last element is the fastest, because removing the last element of an ArrayList shifts nothing.
Undirected graphs and frequent changes
- In an undirected graph, the edge is stored in both
adj.get(u)andadj.get(v), so remove it from both lists. - If edges are added and removed often, store each vertex's neighbors in a
HashSet<Integer>instead of a list. Removing an edge is thenO(1)expected, at the cost of an unspecified neighbor order.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Main {
public static void main(String[] args) {
List<Set<Integer>> adj = new ArrayList<>();
adj.add(new HashSet<>(Set.of(1, 2)));
adj.add(new HashSet<>(Set.of(0)));
adj.add(new HashSet<>(Set.of(0)));
// Remove the undirected edge 0 - 2 from both ends:
boolean removed = adj.get(0).remove(2) & adj.get(2).remove(0);
System.out.println(removed + " " + adj.get(0).contains(2) + " " + adj.get(2).isEmpty());
}
}true false true