Bipartite Graph Check in DSA Rust - Time & Space Complexity
We want to know how long it takes to check if a graph can be split into two groups with no edges inside the same group.
How does the time needed grow as the graph gets bigger?
Analyze the time complexity of the following code snippet.
fn is_bipartite(graph: &Vec<Vec<usize>>) -> bool {
let n = graph.len();
let mut colors = vec![-1; n];
for i in 0..n {
if colors[i] == -1 {
let mut stack = vec![i];
colors[i] = 0;
while let Some(node) = stack.pop() {
for &neighbor in &graph[node] {
if colors[neighbor] == -1 {
colors[neighbor] = 1 - colors[node];
stack.push(neighbor);
} else if colors[neighbor] == colors[node] {
return false;
}
}
}
}
}
true
}
This code checks if a graph is bipartite by coloring nodes using depth-first search.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each node and its neighbors once during DFS.
- How many times: Each node and edge is checked at most once.
As the graph grows, the time grows with the number of nodes and edges.
| Input Size (n nodes, e edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 checks (nodes + edges) |
| 100 nodes, 200 edges | About 300 checks |
| 1000 nodes, 5000 edges | About 6000 checks |
Pattern observation: The work grows roughly with the sum of nodes and edges.
Time Complexity: O(n + e)
This means the time grows linearly with the total number of nodes and edges in the graph.
[X] Wrong: "The time depends only on the number of nodes, not edges."
[OK] Correct: Because edges connect nodes, checking neighbors means looking at edges too, so edges affect the time.
Understanding this time complexity helps you explain how graph traversal scales and shows you can analyze algorithms that explore connections efficiently.
"What if we used breadth-first search (BFS) instead of depth-first search (DFS)? How would the time complexity change?"
Practice
Solution
Step 1: Understand bipartite graph definition
A bipartite graph is one where vertices can be split into two sets so that no edge connects vertices within the same set.Step 2: Check options against definition
Its vertices can be divided into two groups with no edges inside the same group matches this definition exactly. Options A, B, and D describe unrelated properties.Final Answer:
Its vertices can be divided into two groups with no edges inside the same group -> Option AQuick Check:
Bipartite graph = two groups, no internal edges [OK]
- Confusing bipartite with equal vertices and edges
- Thinking bipartite graphs must have cycles
- Assuming all vertices have same degree
Solution
Step 1: Identify adjacency list type
An adjacency list for a graph is usually a vector of vectors, where each inner vector holds neighbors of a vertex.Step 2: Match code snippet to adjacency list
let graph: Vec = vec![vec![]; n]; declares a vector of vectors of usize, initialized with empty vectors for each vertex, which is correct. Options A, B, and C do not represent adjacency lists properly.Final Answer:
let graph: Vec = vec![vec![]; n]; -> Option DQuick Check:
Adjacency list = Vec of Vecs [OK]
- Using Vec<usize> instead of Vec
- Using HashMap with wrong types
- Using tuple vector which is edge list, not adjacency list
let graph = vec![
vec![1, 3],
vec![0, 2],
vec![1, 3],
vec![0, 2],
];
fn is_bipartite(graph: &Vec) -> bool {
let n = graph.len();
let mut colors = vec![-1; n];
for i in 0..n {
if colors[i] == -1 {
let mut stack = vec![i];
colors[i] = 0;
while let Some(u) = stack.pop() {
for &v in &graph[u] {
if colors[v] == -1 {
colors[v] = 1 - colors[u];
stack.push(v);
} else if colors[v] == colors[u] {
return false;
}
}
}
}
}
true
}
println!("{}", is_bipartite(&graph));Solution
Step 1: Analyze graph structure
The graph has 4 vertices with edges forming a square (0-1-2-3-0). This is an even cycle, which is bipartite.Step 2: Trace DFS coloring
The code colors vertex 0 as 0, then neighbors 1 and 3 as 1, then neighbors 2 as 0. No conflicts occur, so it returns true.Final Answer:
true -> Option AQuick Check:
Even cycle graph is bipartite = true [OK]
- Thinking all cycles are non-bipartite
- Confusing stack usage with queue
- Missing color conflict check
fn is_bipartite(graph: &Vec) -> bool {
let n = graph.len();
let mut colors = vec![-1; n];
for i in 0..n {
if colors[i] == -1 {
let mut stack = vec![i];
colors[i] = 0;
while let Some(u) = stack.pop() {
for &v in &graph[u] {
if colors[v] == -1 {
colors[v] = colors[u]; // Bug here
stack.push(v);
} else if colors[v] == colors[u] {
return false;
}
}
}
}
}
true
}Solution
Step 1: Identify color assignment bug
The code assigns neighbors the same color as current vertex, which breaks bipartite property requiring opposite colors.Step 2: Understand impact on bipartite check
This causes false negatives or positives because neighbors must have opposite colors, so the check fails.Final Answer:
Colors assigned to neighbors are same as current vertex, causing wrong bipartite check -> Option BQuick Check:
Neighbors must have opposite colors [OK]
- Assigning same color to neighbors
- Confusing stack and queue usage
- Initializing colors incorrectly
Solution
Step 1: Identify cycles in the graph
The graph has a cycle 0-1-2-3-0 which is length 4 (even). The path 3-4-5 is a chain, not a cycle.Step 2: Check bipartite condition
Graphs with no odd cycles are bipartite. Since the only cycle is even, the graph is bipartite.Final Answer:
The graph is bipartite because it contains no odd cycles -> Option CQuick Check:
No odd cycles means bipartite [OK]
- Mistaking chains for cycles
- Thinking even cycles break bipartiteness
- Assuming degree affects bipartiteness
Ready to prove it?
Pass the test and this pattern moves unknown
