PrevNext
Very Frequent
 0/21

Graph Traversal

Authors: Siyong Huang, Benjamin Qi

Contributors: Andrew Wang, Jason Chen, Ryan Chou

Traversing a graph with DFS and BFS.

Introduction

This section is not complete.

Any help would be appreciated! Just submit a Pull Request on Github.

introduce the topic

Applications

Connected Components

Focus Problem – try your best to solve this problem before continuing!

A connected component is a maximal set of connected nodes in an undirected graph. In other words, two nodes are in the same connected component if and only if they can reach each other via edges in the graph.

In the above focus problem, the goal is to add the minimum possible number of edges such that the entire graph forms a single connected component.

Graph Two-Coloring

Focus Problem – try your best to solve this problem before continuing!

Graph two-coloring refers to assigning a boolean value to each node of the graph, dictated by the edge configuration. The most common example of a two-colored graph is a bipartite graph, in which each edge connects two nodes of opposite colors.

In the above focus problem, the goal is to assign each node (friend) of the graph to one of two colors (teams), subject to the constraint that edges (friendships) connect two nodes of opposite colors. In other words, we need to check whether the input is a bipartite graph and output a valid coloring if it is.

Depth First Search

Resources
CPH

example diagram + code

From the second resource:

Depth-first search (DFS) is a straightforward graph traversal technique. The algorithm begins at a starting node, and proceeds to all other nodes that are reachable from the starting node using the edges of the graph.

Depth-first search always follows a single path in the graph as long as it finds new nodes. After this, it returns to previous nodes and begins to explore other parts of the graph. The algorithm keeps track of visited nodes, so that it processes each node only once.

Breadth First Search

Prerequisite - Queues & Deques

Queues

A queue is a First In First Out (FIFO) data structure that supports three operations, all in O(1)\mathcal{O}(1) time.

C++

std::queue

  • push: inserts at the back of the queue
  • pop: deletes from the front of the queue
  • front: retrieves the element at the front without removing it.
queue<int> q;
q.push(1); // [1]
q.push(3); // [3, 1]
q.push(4); // [4, 3, 1]
q.pop(); // [4, 3]
cout << q.front() << endl; // 3

Java

  • add: insertion at the back of the queue
  • poll: deletion from the front of the queue
  • peek: which retrieves the element at the front without removing it

Java doesn't actually have a Queue class; it's only an interface. The most commonly used implementation is the LinkedList, declared as follows:

Queue<Integer> q = new LinkedList<Integer>();
q.add(1); // [1]
q.add(3); // [3, 1]
q.add(4); // [4, 3, 1]
q.poll(); // [4, 3]
System.out.println(q.peek()); // 3

Python

Python has a queue built-in module.

  • Queue.put(n): Inserts element to the back of the queue.
  • Queue.get(): Gets and removes the front element. If the queue is empty, this will wait forever, creating a TLE error.
  • Queue.queue[n]: Gets the nth element without removing it. Set n to 0 for the first element.
from queue import Queue
q = Queue() # []
q.put(1) # [1]
q.put(2) # [1, 2]
v = q.queue[0] # v = 1, q = [1, 2]
v = q.get() # v = 1, q = [2]
v = q.get() # v = 2, q = []
v = q.get() # Code waits forever, creating TLE error.

Warning!

Python's queue.Queue() uses Locks to maintain a threadsafe synchronization, so it's quite slow. To avoid TLE, use collections.deque() instead for a faster version of a queue.

Deques

A deque (usually pronounced "deck") stands for double ended queue and is a combination of a stack and a queue, in that it supports O(1)\mathcal{O}(1) insertions and deletions from both the front and the back of the deque. Not very common in Bronze / Silver.

C++

std::deque

The four methods for adding and removing are push_back, pop_back, push_front, and pop_front.

deque<int> d;
d.push_front(3); // [3]
d.push_front(4); // [4, 3]
d.push_back(7); // [4, 3, 7]
d.pop_front(); // [3, 7]
d.push_front(1); // [1, 3, 7]
d.pop_back(); // [1, 3]

You can also access deques in constant time like an array in constant time with the [] operator. For example, to access the element i\texttt{i} for some deque dq\texttt{dq}, do dq[i]\texttt{dq[i]}.

Java

In Java, the deque class is called ArrayDeque. The four methods for adding and removing are addFirst , removeFirst, addLast, and removeLast.

ArrayDeque<Integer> deque = new ArrayDeque<Integer>();
deque.addFirst(3); // [3]
deque.addFirst(4); // [4, 3]
deque.addLast(7); // [4, 3, 7]
deque.removeFirst(); // [3, 7]
deque.addFirst(1); // [1, 3, 7]
deque.removeLast(); // [1, 3]

Python

In Python, collections.deque() is used for a deque data structure. The four methods for adding and removing are appendleft, popleft, append, and pop.

d = collections.deque()
d.appendleft(3) # [3]
d.appendleft(4) # [4, 3]
d.append(7) # [4, 3, 7]
d.popleft() # [3, 7]
d.appendleft(1) # [1, 3, 7]
d.pop() # [1, 3]

Resources

Resources
PAPS

grid, 8-puzzle examples

cp-algo

common applications

KA
Youtube

If you prefer a video format

Solution - Building Roads

Note that each edge decreases the number of connected components by either zero or one. So you must add at least C−1C-1 edges, where CC is the number of connected components in the input graph.

There are many valid ways to pick C−1C-1 new roads to build. One way is to choose a single representative from each of the CC components and link them together in a line.

DFS Solution

C++

#include <cstdio>
#include <vector>
const int MN = 1e5 + 10;
int N, M, ans, rep[MN];
std::vector<int> adj_list[MN];
bool visited[MN];
void dfs(int node) {

Java

import java.io.*;
import java.util.*;
public class Main {
public static ArrayList<Integer> adj[];
public static ArrayList<Integer> rep = new ArrayList<Integer>();
public static boolean visited[];
public static void main(String[] args) throws IOException {
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in));

Python

def solve(n, adj):
unvisited = set(range(1, n + 1))
starts = set()
def dfs(current):
for next in adj[current]:
if next in unvisited:
unvisited.remove(next)
dfs(next)

Unfortunately, a danger in using DFS when dealing with large bounds is the possibility of a RecursionError. This commonly occurs for N>103N>10^3 since the recursion limit in Python is set to 1000 by default. So if you submit the above code, you'll get the following:

Traceback (most recent call last):
  File "input/code.py", line 28, in <module>
    solve(n, adj)
  File "input/code.py", line 14, in solve
    dfs(start, start)
  File "input/code.py", line 9, in dfs
    dfs(start, next)
  File "input/code.py", line 9, in dfs
    dfs(start, next)
  File "input/code.py", line 9, in dfs
    dfs(start, next)
  [Previous line repeated 994 more times]
  File "input/code.py", line 7, in dfs
    if next in unvisited:
RecursionError: maximum recursion depth exceeded in comparison

We can fix this by increasing the recursion limit:

import sys
sys.setrecursionlimit(1000000)

although we still get TLE on two test cases.

This section is not complete.

Any help would be appreciated! Just submit a Pull Request on Github.

move this to BFS section?

To resolve this, we can implement a non-recursive solution (which turns out to be slightly faster). You can imagine adding to to_visit as calling the function recursively and popping from to_visit as starting the execution of the recursive function (though they are not exactly equivalent in the strictest sense).

def solve(n, adj):
unvisited = set(range(1, n + 1))
starts = set()
def dfs(start):
to_visit = [start]
while to_visit:
current = to_visit.pop()
for next in adj[current]:
if next in unvisited:

Recursion errors also come up frequently in flood fill problems.

Optional: Adjacency List Without an Array of Vectors

See here.

BFS Solution

This section is not complete.

Any help would be appreciated! Just submit a Pull Request on Github.

Problems

StatusSourceProblem NameDifficultyTags
SilverEasy
Show TagsConnected Components
SilverEasy
SilverEasy
Show TagsConnected Components
KattisEasy
Show TagsConnected Components
DMOJEasy
Show TagsDFS
CSESNormal
GoldNormal
Show TagsBinary Search, Connected Components
SilverNormal
Show TagsBinary Search, Connected Components
SilverNormal
Show Tags2P, Binary Search, Connected Components
SilverNormal
Show TagsDFS
CFHard
Show TagsDFS, Sorted Set
KattisVery Hard
Show TagsBinary Search, Connected Components
SilverVery Hard
Show TagsConstructive, Cycles, Spanning Tree

Solution - Building Teams

Resources
CPH

Brief solution sketch with diagrams.

IUSACO

Uses BFS, but DFS accomplishes the same task.

cp-algo

Uses BFS, but DFS accomplishes the same task.

CP2

DFS Solution

The idea is that we can arbitrarily label a node and then run DFS. Every time we visit a new (unvisited) node, we set its color based on the edge rule. When we visit a previously visited node, check to see whether its color matches the edge rule.

C++

#include <cstdio>
#include <vector>
const int MN = 1e5 + 10;
int N, M;
bool bad, vis[MN], group[MN];
std::vector<int> a[MN];
void dfs(int n = 1, bool g = 0) {

Java

Warning!

Because Java is so slow, an adjacency list using lists/arraylists results in TLE. Instead, the Java sample code uses the edge representation mentioned in the optional block above.

import java.io.*;
import java.util.*;
public class BuildingTeams {
static InputReader in = new InputReader(System.in);
static PrintWriter out = new PrintWriter(System.out);
public static final int MN = 100010;
public static final int MM = 200010;

BFS Solution

This section is not complete.

Any help would be appreciated! Just submit a Pull Request on Github.

Problems

StatusSourceProblem NameDifficultyTags
CFEasy
Show TagsBipartite
SilverEasy
Show TagsBipartite
CFEasy
Show TagsBipartite
Baltic OIHard
Show TagsDFS, Median
CCHard
Show TagsBipartite, DFS
APIOVery Hard
Show TagsBipartite

Module Progress:

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!

PrevNext