2Brute2Best
🔥0

Is Graph Bipartite?

Medium·LeetCode #785Basic Anchor
LeetCode
30-Second Mental ModelBipartite / two-colouring (BFS colours each piece)
Complexity:O(2^N · (N + E))→O(N + E)
Key Insight

Run a breadth-first search from each node that has no colour yet. Give the start colour 0. Every neighbour without a colour gets the opposite colour. If a neighbour already has the same colour as the node being popped, the answer is false. The outer loop covers every piece of the graph.

Core Validity Invariant

Every edge joins two nodes with different colours. Each edge is checked when its first end is popped, and by then both ends have colours.

01

Problem

You are given an undirected graph as an adjacency list. graph[u] lists every node that node u is joined to by an edge.

Can the nodes be split into two groups so that every edge joins a node in one group to a node in the other? Nodes inside one group may not be joined to each other.

Return true if such a split exists, otherwise false. The graph may have several separate pieces, and a node may have no edges at all.

Input

graph, an adjacency list of n nodes numbered 0 to n − 1.

Output

true if the nodes can be split into two groups with no edge inside a group, otherwise false.

Example 1
Input: graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
Output: false

Nodes 0, 1 and 2 are all joined to each other, a triangle. Each pair in the triangle must be in different groups, but with only two groups one pair always shares a group.

Example 2
Input: graph = [[1,3],[0,2],[1,3],[0,2]]
Output: true

Put nodes 0 and 2 in one group and nodes 1 and 3 in the other. Every edge joins the two groups.

Constraints
  • • graph.length = n
  • • 1 ≤ n ≤ 100
  • • 0 ≤ graph[u].length < n
  • • 0 ≤ graph[u][i] ≤ n − 1
  • • graph[u] does not contain u
  • • All the values in graph[u] are unique
  • • If graph[u] contains v, then graph[v] contains u
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    What does an edge tell me about two nodes: must they be in the same group, or in different groups?

  2. 2.

    If I give one node a group, which groups are forced on its neighbours?

  3. 3.

    What goes wrong if I only look at the piece that contains node 0?

  4. 4.

    How does a clash show up: between a node and a neighbour that already has a group?

  5. 5.

    With n nodes and two groups, how many splits are there to try?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceTry all 2^n colourings; accept the first one where no edge has equal coloursO(2^N · (N + E))O(1)
OptimalColour a node, give its neighbours the other colour; start a new BFS for each uncoloured nodeO(N + E)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “split the nodes into two groups”Two groups are two colours: a 2-colouring.
  • “every edge joins a node in one group to a node in the other”A rule on every edge: its two ends must get different colours.
  • “several separate pieces, nodes with no edges”Start a new BFS in every piece, not only the one that holds node 0.
Graph of nodes and edges+two colours+a neighbour must take the other colour→Bipartite / two-colouring (BFS colours each piece)

Colour one node and the rest of its piece is forced. That turns the check into a BFS: colour as you go, and a clash on any edge means false. This is the anchor of the Bipartite sub-pattern. The next question uses the same check, after building the graph from a list of pairs.

10

Don't make this mistake

Pattern confusion: checking only the piece with node 0
✗ color[0] = 0; BFS from 0 only; return true;
✓ for (int s = 0; s < n; s++) if (color[s] == -1) { color[s] = 0; BFS from s; }

On [[1],[0],[3,4],[2,4],[2,3]] (an edge 0–1 and a triangle 2–3–4), a BFS from node 0 alone colours 0 and 1, finds no clash, and returns true. The triangle is never looked at. The correct answer is false.

Checking only neighbours that have no colour yet
✗ if (color[v] == -1) { color[v] = 1 - color[u]; q.push(v); } // no else branch
✓ if (color[v] == -1) { color[v] = 1 - color[u]; q.push(v); } else if (color[v] == color[u]) return false;

On the triangle [[1,2],[0,2],[0,1]], popping 0 colours nodes 1 and 2 with colour B. Popping 1 finds node 2 already coloured B, the same colour as 1, but the wrong version does not look at it. Popping 2 checks nothing, because its neighbour 1 is skipped the same way. It returns true, but a triangle cannot be split.

Using 1 << n for large n
✗ for (int mask = 0; mask < (1 << n); mask++) // n up to 100
✓ // use the BFS two-colouring instead; the mask loop is only for tiny n

1 << n is undefined for n of 31 or more, and 2^100 colourings would never finish. The mask loop only works for small graphs, which is why it is the brute force and not the answer.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Graph BFS / DFS translate into production architectures and low-level memory mechanics.