Last Updated: April 5, 2026
Imagine you are organizing a debate tournament. You have a list of participants, and some pairs of them refuse to be on the same team. Your job is to split everyone into exactly two teams so that no two people who refuse to cooperate end up together. Can you always find a valid split? And if you can, how do you figure it out efficiently?
This is the bipartite graph problem. Model each participant as a node, each conflict as an edge, and the question becomes: can you color every node with one of two colors so that no two adjacent nodes share the same color? If yes, the graph is bipartite. If not, there is no valid split, and the reason always comes down to the same thing: an odd-length cycle lurking in the graph.
A graph is bipartite if you can divide its vertices into two disjoint sets, say Set A and Set B, such that every edge connects a vertex in Set A to a vertex in Set B. No edge connects two vertices within the same set.
This is equivalent to saying the graph is 2-colorable: you can assign one of two colors to every vertex so that no two adjacent vertices share the same color.
There is a third, elegant way to think about it. A graph is bipartite if and only if it contains no odd-length cycle. A cycle of length 3 (triangle), 5, 7, or any odd number makes 2-coloring impossible. Even-length cycles are fine.
Why does an odd cycle break things? Picture a cycle with 3 nodes: A-B-C-A. Color A with 0. Then B must be 1 (it is adjacent to A). Then C must be 0 (it is adjacent to B). But C is also adjacent to A, and both are color 0. Contradiction. The same logic extends to any odd-length cycle.
The diagram below shows a bipartite graph on the left and a non-bipartite graph on the right. Notice the triangle in the non-bipartite graph, which makes a valid 2-coloring impossible.
In the bipartite graph, nodes A, B, C form one set (teal) and D, E, F form the other (orange). Every edge crosses between the two sets. In the non-bipartite graph, the triangle X-Y-Z makes 2-coloring impossible. No matter how you assign two colors, two adjacent nodes in that triangle will end up with the same color.
Given an undirected graph, determine whether it is bipartite. In other words, can you assign one of two labels (say 0 and 1) to every vertex so that no edge connects two vertices with the same label?
A brute-force approach would try all possible 2-colorings. With n vertices, there are 2^n possible assignments. For each assignment, you would check if every edge connects differently colored vertices. That is exponential and far too slow.
You do not need to try all colorings. You can determine the correct coloring greedily using BFS or DFS. Here is why.
Pick any unvisited node and assign it color 0. Now, every neighbor of that node must be color 1 (there is no other valid choice). Every neighbor of those neighbors must be color 0. And so on. The coloring is forced at every step. There is no decision to make.
If, during this forced coloring, you ever find a neighbor that has already been colored and its color matches the current node's color, you have found a conflict. That conflict means the graph is not bipartite, and there is guaranteed to be an odd-length cycle passing through those two nodes.
Think of it like painting a fence where alternating posts must be different colors. You start with one color, alternate as you go, and if you ever circle back to a post that is already the wrong color, the fence has an odd number of posts in that loop, and alternating colors is impossible.
The algorithm is a minor extension of standard BFS or DFS. Instead of just tracking "visited or not," you track the color assigned to each node. The rest of the traversal logic stays the same.
The BFS-based approach is the most common one you will see in interviews. Here is the step-by-step procedure.
The DFS approach uses exactly the same coloring logic. The only difference is the traversal order: instead of expanding level by level, DFS goes deep along one path before backtracking.
The recursive version is particularly clean:
dfs(node, color) for each uncolored node.dfs, assign the node its color.dfs(neighbor, 1 - color). If the recursive call returns false, propagate the failure.Both BFS and DFS have the same time and space complexity for this problem. BFS is often preferred in interviews because the iterative queue-based approach avoids potential stack overflow issues on very deep graphs. DFS is slightly more concise in its recursive form, which can be nice for quick whiteboard implementations.
Let us trace through two examples to see the algorithm in action.
Consider this graph with 6 nodes:
This forms a path-like structure: 0-1-2-3-4-5 with an extra edge 0-3.
Here is the BFS trace starting from node 0:
Every edge connects a teal node (color 0) to an orange node (color 1). The 2-coloring is valid.
Now consider a graph with a triangle:
Nodes 0, 1, 2 form a triangle (odd cycle of length 3).
The conflict happens at step 3 when we discover that node 2 (already colored 1) is a neighbor of node 1 (also colored 1). Both got the same color because they are both neighbors of node 0, but they are also neighbors of each other. This is the triangle 0-1-2, an odd cycle, and it makes 2-coloring impossible.
The primary implementation uses BFS with a color array. The color array stores -1 for uncolored nodes, 0 for one color, and 1 for the other. The outer loop ensures all connected components are checked.
The code follows the algorithm described earlier. A few things worth noting:
graph[i] is a list of neighbors of node i. This is the format used by LeetCode 785 ("Is Graph Bipartite?"), so the code is directly interview-ready.Common Mistakes:
color[neighbor] == color[node], not color[neighbor] != -1. A neighbor being colored is fine as long as the colors differ.Overall: O(V + E)
The analysis is identical to standard BFS:
Overall: O(V)
The DFS approach uses recursion instead of a queue. It is functionally equivalent but sometimes more concise to write on a whiteboard.
The logic mirrors BFS exactly. The only difference is traversal order: DFS colors one branch completely before moving to the next, while BFS colors level by level.
Union-Find offers an alternative perspective. The idea is: for each node, all its neighbors must belong to the opposite group. So all neighbors of a node should be in the same group as each other.
For each node u, union all neighbors of u together. Then check if u and any of its neighbors ended up in the same group. If they did, the graph is not bipartite.
This approach has a time complexity of O(V + E * alpha(V)), where alpha is the inverse Ackermann function (effectively constant). It is slightly less intuitive than BFS coloring but can be useful if you are already using Union-Find for other parts of the problem.
Bipartite checking is the k=2 case of graph coloring. A natural question is: what about 3-colorable, 4-colorable, etc.? Unfortunately, determining if a graph is k-colorable for k >= 3 is NP-complete. There is no known polynomial-time algorithm. This makes the k=2 case special, and it is one of the few graph coloring problems that can be solved efficiently.
| Approach | Time Complexity | Space Complexity | Best For |
|---|---|---|---|
| BFS Coloring | O(V + E) | O(V) | Interviews, iterative preference |
| DFS Coloring | O(V + E) | O(V) | Quick whiteboard, recursive preference |
| Union-Find | O(V + E * alpha(V)) | O(V) | When Union-Find is already in use |
| Algorithm | Purpose | Time | Space | Key Difference |
|---|---|---|---|---|
| BFS 2-Coloring | Bipartite detection | O(V + E) | O(V) | Colors nodes with 2 colors during traversal |
| DFS 2-Coloring | Bipartite detection | O(V + E) | O(V) | Same result, different traversal order |
| Union-Find | Bipartite detection | O(V + E * alpha(V)) | O(V) | Groups neighbors, checks for conflicts |
| DFS Cycle Detection | Find any cycle | O(V + E) | O(V) | Detects cycles but does not check parity |
| Graph Coloring (k >= 3) | k-colorability | NP-complete | - | No efficient algorithm known for k >= 3 |
Problem type: "Can we divide n elements into two groups such that no two elements in the same group have a conflict?"
How bipartite detection helps: Model elements as nodes, conflicts as edges. Run bipartite check. If the graph is bipartite, the two color classes give you the partition. If not, no valid partition exists.
Example: LeetCode 886 "Possible Bipartition" asks exactly this. Given n people and a list of pairs who dislike each other, can you split them into two groups so no one is in the same group as someone they dislike?
Problem type: "Assign one of two labels to each node respecting edge constraints."
How bipartite detection helps: This is literally 2-coloring. Any problem that asks you to assign binary labels (true/false, 0/1, team A/team B) to nodes with pairwise constraints is a bipartite check in disguise.
Problem type: Maximum matching, minimum vertex cover, or assignment problems on bipartite graphs.
How bipartite detection helps: Many matching algorithms (Hopcroft-Karp, Hungarian method) require the input graph to be bipartite. The first step is often verifying bipartiteness. Once confirmed, you can apply specialized bipartite matching algorithms that are more efficient than general matching algorithms.