Structs & Data Structures · intermediate · ~14 min
Two-colouring and odd cycles.
A graph is bipartite when its vertices divide into two groups such that every edge goes between the groups, never within one — equivalently, it can be coloured with two colours so no edge joins same-coloured vertices. A BFS/DFS assigns alternating colours; a conflict (an edge between two equally-coloured vertices) proves the graph is not bipartite.
Bipartite structure models 'two kinds of things related across the divide': jobs and applicants, students and courses, users and items. Bipartite graphs enable matching algorithms, and the 2-colouring test is the classic way to detect an odd cycle.
2-colouring. Colour a start vertex 0, its neighbours 1, theirs 0, alternating outward.
Odd-cycle obstruction. A graph is bipartite iff it contains no odd-length cycle; the colour clash is exactly where an odd cycle closes.
All components. Restart the colouring on every uncoloured vertex — the graph is bipartite only if every component is.
Why odd cycles are the only obstruction. Walking around any cycle the colours must alternate; an even cycle returns to the start with the original colour, but an odd one arrives back with the opposite, which is a contradiction. So a graph is bipartite precisely when it contains no odd cycle — the colour clash the algorithm detects is exactly that contradiction surfacing.
#include <stdlib.h>
/* 2-colour every component; 0 and 1 are the two sides, -1 = uncoloured */
int is_bipartite(const int *adj, int n) {
int *col = malloc((size_t)n * sizeof *col);
int *q = malloc((size_t)n * sizeof *q);
if (!col || !q) { free(col); free(q); return -1; }
for (int i = 0; i < n; i++) col[i] = -1;
int ok = 1;
for (int s = 0; s < n && ok; s++) { /* EVERY component, not just vertex 0 */
if (col[s] != -1) continue;
int head = 0, tail = 0;
col[s] = 0; q[tail++] = s;
while (head < tail && ok) {
int v = q[head++];
for (int u = 0; u < n; u++)
if (adj[v*n + u]) {
if (col[u] == -1) { col[u] = 1 - col[v]; q[tail++] = u; }
else if (col[u] == col[v]) { ok = 0; break; } /* odd cycle */
}
}
}
free(col); free(q);
return ok;
}
Key points:
1 - col[v] flips between the two colours.s is essential: a graph is bipartite only if every component is.A graph is bipartite if its vertices split into two sets with every edge crossing between them — equivalently, it is 2-colourable. BFS/DFS alternates colours as it walks; a neighbour that already carries the current vertex's colour reveals an odd cycle, the exact obstruction to bipartiteness. Disconnected graphs must be checked component by component.
#include <stdio.h> int main(void){ int n=4; /* a 4-cycle 0-1-2-3-0 : bipartite / int adj[16]={ 0,1,0,1, 1,0,1,0, 0,1,0,1, 1,0,1,0 }; int color[4]={-1,-1,-1,-1}, q[4], head=0,tail=0, ok=1; color[0]=0; q[tail++]=0; while(head<tail&&ok){ int v=q[head++]; for(int u=0;u<n;u++) if(adj[vn+u]){ if(color[u]<0){ color[u]=1-color[v]; q[tail++]=u; } else if(color[u]==color[v]) ok=0; } } printf("bipartite? %s\n", ok?"yes":"no"); if(ok) for(int i=0;i<n;i++) printf("vertex %d -> set %d\n", i, color[i]); return 0; }
| Step | Line | What happens |
|---|---|---|
| 1 | col[s] = 0 |
The start vertex of a component is assigned to side 0 arbitrarily. |
| 2 | uncoloured neighbour | Gets 1 - col[v], the opposite side, and joins the queue. |
| 3 | already-coloured neighbour | Compared against col[v]: opposite is fine, same is a violation. |
| 4 | same colour found | An odd cycle exists, so the graph cannot be bipartite -> ok = 0. |
| 5 | 4-cycle example | Colours alternate 0,1,0,1 all the way round and close consistently -> bipartite. |
| 6 | outer loop continues | The next uncoloured vertex starts a fresh component with its own arbitrary side-0 seed. |
Checking only the component containing vertex 0 and ignoring the rest. Concluding 'bipartite' as soon as one component works. Confusing 'has no edges' (trivially bipartite) with an error case.
Compiler errors and warnings:
-Wmaybe-uninitialized if col is malloced without the -1 fill.Runtime symptoms:
!= where == was meant.col was zero-initialised, so vertices look pre-coloured as side 0. The sentinel must be -1.Technique: test an even cycle (bipartite), an odd cycle (not), a tree (always bipartite), and a graph whose second component contains a triangle — the last one catches the single-component bug.
-1. With calloc, every vertex reads as already coloured 0, which both breaks the algorithm and skips the enqueue that keeps the queue sized correctly.n-slot queue — a real out-of-bounds write.Concrete uses: Bipartite structure models two-sided relationships — students and courses, jobs and applicants, users and items, tasks and machines — and detecting it is the precondition for bipartite matching algorithms (Hopcroft-Karp) used in assignment problems. Two-colourability is also the simplest case of graph colouring, which underlies register allocation and scheduling; and the odd-cycle test is used in checking consistency of constraint systems where relations must alternate.
Professional best practices:
Beginner:
-1 for uncoloured so 0 remains a valid side.Intermediate:
1. (Beginner) Bipartite test. Implement is_bipartite covering all components. Example: a 4-cycle -> 1; a triangle -> 0. Concepts: 2-colouring, component sweep.
2. (Beginner) Validate a colouring. Implement int is_valid_coloring(const int *adj, int n, const int *color) returning 0 if any edge joins two equal colours. Concepts: checking a constraint directly.
3. (Intermediate) Report the partition. Return the two vertex sets when the graph is bipartite. Concepts: the colour array as output.
4. (Intermediate) Find the odd cycle. When the test fails, print the cycle that caused it. Hint: keep a parent array and walk both endpoints back to their common ancestor. Concepts: diagnostics.
A graph is bipartite exactly when it can be 2-coloured so no edge joins two vertices of the same colour — equivalently, when it contains no odd cycle. BFS assigns a start vertex to side 0 and every neighbour the opposite side with 1 - col[v]; encountering an already-coloured neighbour with the same colour is the odd cycle that proves bipartiteness impossible. The two details that matter are sweeping every component (a graph is bipartite only if all of them are) and using -1 rather than 0 for "uncoloured", since 0 is a real side. Colour vertices as you enqueue them so the queue cannot overflow, and keep the colour array — it is the partition itself.