What is a Bipartite Graph?
A Bipartite Graph (or bigraph) is a graph whose vertices can be divided into two disjoint and independent sets and such that every edge connects a vertex in to one in . In other words, no two vertices within the same set are adjacent. It is 2-colorable and contains no odd-length cycles.
Explanation
- Bipartite graphs are fundamental in computer science, especially for modeling relationship networks between two different classes of objects. A key mathematical property is that a graph is bipartite if and only if it contains no odd-length cycles.
Real-World Analogy
- Job Applicants & Job Openings (Matching): An applicant can apply for jobs, but applicants do not connect to other applicants, and job openings do not connect to other job openings.
- Actors & Movies (Bipartite Network): An actor acts in a movie. An actor does not connect directly to another actor, and a movie does not connect directly to another movie.
- Users & Products (Recommendation Systems): Users rate products. Connections only exist between users and products, never between users themselves or products themselves.
How It Works
Core Mechanics
- Checking if a graph is bipartite is equivalent to checking if it is 2-colorable.
- We traverse the graph using Breadth-First Search (BFS) or Depth-First Search (DFS) and color each uncolored node with a starting color (e.g.,
0). - For each visited node, color all its adjacent unvisited neighbors with the opposite color (
1 - current_color). - If an adjacent neighbor is already colored and has the same color as the current node, the graph contains an odd-length cycle and is not bipartite.
- Since graphs can be disconnected, we must run this coloring check for all components by looping over all vertices.
BFS vs DFS Bipartiteness Check
- Both BFS and DFS can check bipartiteness in time.
- BFS Approach: Explores level-by-level, making it intuitive for layering colors.
- DFS Approach: Explores paths deeply and backtracks, which is equally efficient but uses call stack memory instead of a queue.
Visual Walkthrough
1. Bipartite Graph (Square: 0-1-2-3-0)
0(Color A) ----- 1(Color B)
| |
| |
3(Color B) ----- 2(Color A)
- Color Assignment Process:
- Start BFS at
0, color it A. - Neighbors of
0are1and3. Color both B. - Dequeue
1. Neighbor2is uncolored; color it A. Neighbor0is already colored A (no conflict). - Dequeue
3. Neighbor2is already colored A (no conflict). Neighbor0is already colored A (no conflict). - Dequeue
2. All neighbors colored. BFS ends. - The partition is and . Valid!
- Start BFS at
2. Non-Bipartite Graph (Triangle: 0-1-2-0)
0(Color A) ----- 1(Color B)
\ /
\ /
2(Color B / Conflict!)
- Color Assignment Process:
- Start BFS at
0, color it A. - Neighbors of
0are1and2. Color both B. - Dequeue
1. Neighbor2is already colored B. But1is also colored B! - Conflict: Neighbor
2has the same color as current node1. Bipartiteness fails because an odd cycle () of length 3 exists.
- Start BFS at
Time & Space Complexity
| Algorithm / Representation | Time Complexity | Space Complexity |
|---|---|---|
| BFS Bipartite Check (Adjacency List) | ||
| DFS Bipartite Check (Adjacency List) |
- Note: is the number of vertices, and is the number of edges. Space is used for the color tracking array and the BFS queue or DFS recursion stack.
Implementation
from collections import deque
def is_bipartite(V, adj):
"""
Determines if a graph represented as an adjacency list is bipartite.
Handles disconnected graphs.
Colors:
-1 : Uncolored
0 : Color A
1 : Color B
"""
color = [-1] * V
for start in range(V):
if color[start] == -1:
# Start BFS for this component
color[start] = 0
queue = deque([start])
while queue:
u = queue.popleft()
for v in adj[u]:
if color[v] == -1:
# Color neighbor with the opposite color
color[v] = 1 - color[u]
queue.append(v)
elif color[v] == color[u]:
# Conflict detected: adjacent nodes share the same color
return False
return True
# Example Usage
if __name__ == "__main__":
# Bipartite Graph (Square: 0-1-2-3-0)
V1 = 4
adj1 = [
[1, 3], # 0
[0, 2], # 1
[1, 3], # 2
[0, 2] # 3
]
print(f"Graph 1 Bipartite? {is_bipartite(V1, adj1)}") # Output: True
# Non-Bipartite Graph (Triangle: 0-1-2-0)
V2 = 3
adj2 = [
[1, 2], # 0
[0, 2], # 1
[0, 1] # 2
]
print(f"Graph 2 Bipartite? {is_bipartite(V2, adj2)}") # Output: False#include <iostream>
#include <vector>
#include <queue>
class BipartiteChecker {
public:
/**
* Determines if a graph is bipartite.
* Handles disconnected graphs.
*
* Colors:
* -1 : Uncolored
* 0 : Color A
* 1 : Color B
*/
static bool isBipartite(int V, const std::vector<std::vector<int>>& adj) {
std::vector<int> color(V, -1);
for (int start = 0; start < V; ++start) {
if (color[start] == -1) {
// Start BFS for this component
color[start] = 0;
std::queue<int> q;
q.push(start);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (color[v] == -1) {
// Color neighbor with the opposite color
color[v] = 1 - color[u];
q.push(v);
} else if (color[v] == color[u]) {
// Conflict detected: adjacent nodes share the same color
return false;
}
}
}
}
}
return true;
}
};
int main() {
// Bipartite Graph (Square: 0-1-2-3-0)
int V1 = 4;
std::vector<std::vector<int>> adj1(V1);
adj1[0] = {1, 3};
adj1[1] = {0, 2};
adj1[2] = {1, 3};
adj1[3] = {0, 2};
std::cout << "Graph 1 Bipartite? "
<< (BipartiteChecker::isBipartite(V1, adj1) ? "Yes" : "No")
<< std::endl; // Output: Yes
// Non-Bipartite Graph (Triangle: 0-1-2-0)
int V2 = 3;
std::vector<std::vector<int>> adj2(V2);
adj2[0] = {1, 2};
adj2[1] = {0, 2};
adj2[2] = {0, 1};
std::cout << "Graph 2 Bipartite? "
<< (BipartiteChecker::isBipartite(V2, adj2) ? "Yes" : "No")
<< std::endl; // Output: No
return 0;
}
When to Use
✅ Use Bipartite Check When:
- You are solving the Maximum Bipartite Matching problem (e.g. assigning jobs to candidates, students to dorm rooms).
- You need to determine if a graph can be 2-colored (e.g. checking if register allocation is possible with only 2 registers).
- You are analyzing social networks or transaction networks to partition them into two distinct categories (e.g. buyers and sellers).
Variations & Related Concepts
- Complete Bipartite Graph (): A special bipartite graph where every vertex of Set () is connected to every vertex of Set (). It has a total of edges.
- Graph Coloring: Coloring nodes such that no two adjacent nodes share the same color. A graph is bipartite if and only if its chromatic number is .
- Odd Cycle Detection: Finding if a graph has a cycle of odd length. A graph has an odd cycle if and only if it is NOT bipartite.
Key Takeaways
- A graph is bipartite if its vertices can be split into two independent sets with no intra-set edges.
- A graph is bipartite if and only if it does not contain any odd-length cycles.
- You can check bipartiteness in time using BFS or DFS 2-coloring.
- Remember to loop over all vertices to handle disconnected graphs correctly.