Bipartite Graph Check in DSA Go - 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.
func isBipartite(graph [][]int) bool {
n := len(graph)
colors := make([]int, n)
for i := 0; i < n; i++ {
if colors[i] == 0 {
if !dfsCheck(graph, colors, i, 1) {
return false
}
}
}
return true
}
func dfsCheck(graph [][]int, colors []int, node, color int) bool {
colors[node] = color
for _, neighbor := range graph[node] {
if colors[neighbor] == 0 {
if !dfsCheck(graph, colors, neighbor, -color) {
return false
}
} else if colors[neighbor] == color {
return false
}
}
return true
}
This code uses depth-first search to color nodes and check if the graph is bipartite.
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 once in total.
As the number of nodes and edges grows, the time to check grows roughly in proportion to their total count.
| Input Size (n nodes, m 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 linearly with the number of nodes plus edges.
Time Complexity: O(n + m)
This means the time grows in a straight line 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: Checking neighbors means looking at edges too, so edges affect the time just as much as nodes.
Understanding this time complexity helps you explain how your graph checks scale, showing you know how to handle bigger data efficiently.
"What if we used a queue and breadth-first search instead of recursion? 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 groups so that no edge connects vertices within the same group.Step 2: Compare options with definition
Its vertices can be divided into two sets with no edges inside the same set matches this definition exactly, while others describe unrelated properties.Final Answer:
Its vertices can be divided into two sets with no edges inside the same set -> Option DQuick Check:
Bipartite graph = Two sets, no internal edges [OK]
- Confusing bipartite with equal vertices and edges
- Thinking bipartite means having cycles
- Assuming all vertices have same degree
Solution
Step 1: Identify graph representation needs
We need to store each vertex and its connected vertices (neighbors).Step 2: Match data structures to graph representation
map[int][]int maps each vertex to a list of neighbors, perfect for adjacency list representation.Final Answer:
map[int][]int -> Option AQuick Check:
Adjacency list = map[int][]int [OK]
- Using []int which is just a list, not graph
- Using map[int]int which maps to single int, not neighbors
- Using [][]int without clear vertex indexing
graph := map[int][]int{
0: {1, 3},
1: {0, 2},
2: {1, 3},
3: {2, 0},
}
result := isBipartite(graph)
fmt.Println(result)
Assuming isBipartite returns true if graph is bipartite, false otherwise.Solution
Step 1: Analyze the graph structure
The graph forms a cycle of length 4: 0-1-2-3-0.Step 2: Check bipartite condition for even cycle
Even length cycles are bipartite, but here edges connect 0-3 and 2-3, making it a 4-cycle which is bipartite. However, 0-3 edge closes the cycle.Step 3: Re-examine edges
Edges are 0-1,1-2,2-3,3-0 forming a square (4-cycle). This is bipartite, so isBipartite should return true.Final Answer:
false -> Option AQuick Check:
Even cycle graph is bipartite = false [Incorrect]
- Thinking all cycles are not bipartite
- Confusing edge directions
- Assuming runtime errors without code errors
func isBipartite(graph map[int][]int) bool {
color := make(map[int]int)
var dfs func(int, int) bool
dfs = func(node, c int) bool {
if val, ok := color[node]; ok {
return val == c
}
color[node] = c
for _, nei := range graph[node] {
if !dfs(nei, 1-c) {
return false
}
}
return true
}
for node := range graph {
if _, ok := color[node]; !ok {
if !dfs(node, 0) {
return false
}
}
}
return true
}Solution
Step 1: Review color map initialization
color map is initialized properly with make(map[int]int).Step 2: Check DFS logic and color flipping
DFS flips color with 1-c correctly and returns false if conflict found.Step 3: Confirm DFS returns and main loop
DFS returns bool, main loop calls DFS on uncolored nodes, returns false if conflict.Final Answer:
No bug, function correctly checks bipartite -> Option CQuick Check:
DFS with color flipping and map works correctly [OK]
- Forgetting to flip color with 1-c
- Not returning DFS results properly
- Not initializing color map
Solution
Step 1: Analyze the graph structure
The graph is a cycle 0-1-2-3-4-5-0 of length 6 (even) with an additional edge 1-4.Step 2: Check for odd-length cycles
Check cycles involving the extra edge 1-4: cycle 1-4-5-0-1 has length 4 (even), but cycle 1-2-3-4-1 is length 4 (even). However, the presence of edge 1-4 creates a triangle 1-4-5-0-1? Actually, no triangle. But check carefully: 1-4-5-0-1 is length 4, no odd cycle. But 1-4-5-0-1 is 4 edges, so even. So no odd cycle. So graph is bipartite.Final Answer:
Yes, because it has no odd cycles -> Option BQuick Check:
No odd cycles = bipartite [OK]
- Thinking the extra edge 1-4 creates an odd cycle
- Assuming all cycles are even without checking
- Confusing disconnected with bipartite
Ready to prove it?
Pass the test and this pattern moves unknown
