Graph · 28 of 43
Check whether a graph is Bipartite or Not
Check if a class can be split into two teams so that every pair of friends ends up on different teams.
- Simple way
- time O(2^V * E)
room O(1) - Clever way
- time O(V + E)
room O(V)
The problem, simply
You get an undirected graph: V dots joined by two-way lines. Can you paint every dot red or blue so that every line joins a red dot to a blue dot? No line may join two dots of the same color.
Return true if it can be done and false if it cannot.
A story to remember it
Picture it
A square 0, 1, 3, 2 joined to a triangle 3, 4, 5. The square alone could be split. The triangle cannot.
- checking
Examples
A square
- In
V = 4, edges = [[0,1],[1,2],[2,3],[3,0]]- Out
true
Red team 0 and 2, blue team 1 and 3. Every line crosses between teams.
A triangle
- In
V = 3, edges = [[0,1],[1,2],[2,0]]- Out
false
0 is red, 1 is blue, 2 must differ from both. A loop of odd length can never be split.
Separate pieces
- In
V = 4, edges = [[0,1],[2,3]]- Out
true
The graph is in two pieces. You must start a new ripple in each piece, or dots 2 and 3 never get a color.
No lines at all
- In
V = 3, edges = []- Out
true
With no friendships, any split works.
Way 1: the simple way
Try every possible way to split the dots into two teams. With V dots there are 2^V splits. Write each split as a number whose bits say red (0) or blue (1), and check every line.
- For
maskfrom0to2^V - 1, dotiis blue if bitiofmaskis 1. - If every line has ends of different colors, return
true. - If no split works, return
false.
class BipartiteBrute {
public static boolean isBipartite(int V, int[][] edges) {
for (int mask = 0; mask < (1 << V); mask++) { // every way to split
boolean ok = true;
for (int[] e : edges) {
int a = (mask >> e[0]) & 1, b = (mask >> e[1]) & 1;
if (a == b) { ok = false; break; } // friends on the same team
}
if (ok) return true;
}
return false;
}
}How long it takes: 2^V splits, each checked against E lines, O(2^V * E). Only usable for about 20 dots. Extra room: O(1).
Way 2: the clever way
Do not guess. Once one dot has a color, the colors of all dots in its piece are forced. Spread the colors with a waiting line, a queue, and watch for a clash.
- Set every color to "none".
- For each dot that still has no color, paint it red and put it in the line. This handles graphs in many pieces.
- Take a dot from the front. For each neighbor: if it has no color, give it the opposite color and add it to the line. If it has the same color as this dot, return
false. - When every piece is done without a clash, return
true.
Spread the teams like a ripple and look for a clash
01 / 07
Start at 0 and put it on the red team. Put 0 in the waiting line.
- looking now
- checking
- done
- rejected
- just added
- ignored
class BipartiteBfs {
public static boolean isBipartite(int V, List<List<Integer>> adj) {
int[] color = new int[V];
Arrays.fill(color, -1); // -1 means no team yet
for (int start = 0; start < V; start++) {
if (color[start] != -1) continue; // already painted by an earlier ripple
color[start] = 0;
Queue<Integer> line = new ArrayDeque<>();
line.add(start);
while (!line.isEmpty()) {
int u = line.poll();
for (int v : adj.get(u)) {
if (color[v] == -1) {
color[v] = 1 - color[u]; // the other team
line.add(v);
} else if (color[v] == color[u]) {
return false; // two friends on one team
}
}
}
}
return true;
}
}How long it takes: each dot enters the line once and each line is looked at from both ends, O(V + E). Extra room: the color array and the queue, O(V).
Another way: depth first painting
The same idea works with recursion. Paint a dot, then walk into each unpainted neighbor with the other color. A clash anywhere sends false all the way back up.
class BipartiteDfs {
public static boolean isBipartite(int V, List<List<Integer>> adj) {
int[] color = new int[V];
Arrays.fill(color, -1);
for (int i = 0; i < V; i++) {
if (color[i] == -1 && !paint(i, 0, adj, color)) return false;
}
return true;
}
static boolean paint(int u, int c, List<List<Integer>> adj, int[] color) {
color[u] = c;
for (int v : adj.get(u)) {
if (color[v] == -1) {
if (!paint(v, 1 - c, adj, color)) return false;
} else if (color[v] == c) {
return false;
}
}
return true;
}
}Same O(V + E) time. The recursion can go V calls deep, so on a long chain of a million dots the BFS version is safer.
Watch out for
- Using a
visitedarray alone is not enough. You need the color itself to spot a clash. - A dot with a line to itself (a self-loop) can never be split. The check
color[v] == color[u]catches it. - LeetCode gives
graph[i]as the neighbor list ofidirectly; the code is the same withgraph[u]instead ofadj.get(u).
What interviewers ask next
- Why does an odd loop make it impossible? Colors must flip at every step, so after an odd number of steps you come back with the wrong color. A graph is two-colorable exactly when it has no odd loop.
- Can you do it with union find? Yes. For each dot, all of its neighbors must be on one team, so union them together. If a dot ends up in the same set as one of its neighbors, return
false. - How do you print the two teams? Collect dots by
colorat the end. - "Possible bipartition": people who dislike each other must be in different groups. It is this exact problem on the dislike graph.
- Where is this used? Matching problems: jobs to workers, students to projects. Matching algorithms first need the two sides.
Remember this
Grown-up words: dots are vertices, lines are edges, the two teams are the two parts of a bipartite graph, painting with two colors is 2-coloring, the ripple is breadth first search with a queue, and the pieces are connected components.