Graph Algorithms

Graph Basics

Graphs are made up of nodes and edges, where nodes are connected by edges. Graphs can have either weighted edges, in which each edge has a certain length, or unweighted, in which case all edges have the same length. Edges are either directed, which means they can be traversed only in one direction, or undirected, which means that they can be traversed in both directions.

Trees

A tree is a special type of graph satisfying two constraints: it is acyclic, meaning there are no cycles, and the number of edges is one less than the number of nodes. Trees satisfy the property that for any two nodes A and B, there is exactly one way to travel between A and B.

Graph Representations

Graphs can be represented in three ways:

  • Adjacency List most frequently used when nodes are large
  • Adjacency Matrix when nodes are small
  • Edge List

Adjacency List for Un-Weighted Graph

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int MX = 1e5; // max num of nodes
vector<int> adj[MX]; // adjacency list where MX is max possible # of nodes
bool visited[MX];    // visited array of size MX as well  
int group[MX];       // group that this node belongs to
bool bad = false;

void addEdge(int a, int b) {
  adj[a].push_back(b);

  // omit this line if the graph is directed
  adj[b].push_back(a);
}

void dfs(int n, int g)
{
    visited[n]=1;
    group[n]=g;
    for(int u:adj[n])
        if(visited[u])
        {
            // process groups, colors etc
            // or terminate the BFS by setting bad=1;
        }
        else
            dfs(u, g);
}

int main(){
  cin >> n; // reads in number of nodes
  cin >> m; // reads in number of edges
  for(int i = 0; i < m; i++){ // reading in each of the m edges
    int a, b;
    cin >> a >> b;
    addEdge(a,b);
  }

  int g = 0;
  for(int i=1;!bad && i<=N;++i)
        if(!vis[i])
            dfs(i, ++g);

  return 0;
}

Adjacency List for Weighted Graph

We use c++ pair<int, int> to store the destination node ID and weight.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int MX = 1e5; // max num of nodes
vector<pair<int, int>> adj[MX];  
bool visited[MX]; // visited array of size MX as well  

void addEdge(int a, int b, int weight) {
  adj[a].push_back(make_pair(b, weight));
  adj[b].push_back(make_pair(a, weight));
}

int main(){
  cin >> n; // reads in number of nodes
  cin >> m; // reads in number of edges
  for(int i = 0; i < m; i++){ // reading in each of the m edges
    int a, b, w;
    cin >> a >> b >> w;
    addEdge(a,b, w);
  }
  return 0;
}
  • Adjacency Matrix

When Graph nodes are small, we can use a 2D array to store the graph.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int MX = 1e5; // max num of nodes
int adj[MX][MX];

void addEdge(int a, int b, int weight = 1) {
  adj[a][b] = weight;
  adj[b][a] = weight;
}

int main(){
  int n, m; // number of nodes and edges
  cin >> n; // reads in number of nodes
  cin >> m; // reads in number of edges
  for(int i = 0; i < m; i++){ // reading in each of the m edges
    int a, b;
    cin >> a >> b;
    addEdge(a,b);
  }
  return 0;
}

Graph Traversal Algorithms

Graph traversal is the process of visiting or checking each vertex in a graph. This is useful when we want to determine which vertices can be visited, whether there exists a path from one vertex to another, and so forth. There are two algorithms for graph traversal, namely depth-first search (DFS) and breadth-first search (BFS).

Breadth-first search visits nodes in order of distance away from the starting node; it first visits all nodes that are one edge away, then all nodes that are two edges away, and so on.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//Global variables

 // max num of nodes
int MX = 1e5;
int adj[MX][MX];
bool visited[MX];
int dist[MX];


void addEdge(int a, int b, int weight = 1) {
  adj[a][b] = weight;
  adj[b][a] = weight;
}

//recursive BFS
void bfs(int node){
  //step1: boundary processing
  cout << "visiting node " << node;

  //step2: check visited or not
  if (!visited[node]) {
    visited[node] = true;
  }

  //step3: loop through neighbors
  for(int next : adj[node]){
    if(!visited[next]){
      bfs(next);
    }
  }
}

//Queue based BFS

void BFS(int start){
  // fill distance array with -1's
  memset(dist, -1, sizeof(dist));

  queue<int> q;
  dist[start] = 0;

  q.push(start);
  while(!q.empty()){
    int node = q.front();
    q.pop();

    for(int next : adj[node]){
      if(dist[next] == -1){
        dist[next] = dist[node] + 1;
        q.push(next);
        }
      }
    }
}


int main(){
  int n, m; // number of nodes and edges
  cin >> n; // reads in number of nodes
  cin >> m; // reads in number of edges
  for(int i = 0; i < m; i++){ // reading in each of the m edges
    int a, b;
    cin >> a >> b;
    addEdge(a,b);
  }

  BFS(0);

  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//Global variables

 // max num of nodes
int MX = 1e5;
int adj[MX][MX];
bool visited[MX];
int dist[MX];


void addEdge(int a, int b, int weight = 1) {
  adj[a][b] = weight;
  adj[b][a] = weight;
}

//recursive DFS
void dfs(int node){
  //step1: boundary processing
  cout << "visiting node " << node;

  //step2: check visited or not
  if (!visited[node]) {
    visited[node] = true;
  }

  //step3: loop through neighbors
  for(int next : adj[node]){
    if(!visited[next]){
      dfs(next);
    }
  }
}

//Stack based DFS
void DFS(int start){
  // fill distance array with -1's
  memset(dist, -1, sizeof(dist));

  stack<int> st;
  dist[start] = 0;

  st.push(start);

  while(!st.empty()){

    int node = st.top();
    st.pop();

    for(int next : adj[node]){
      if(dist[next] == -1){
        dist[next] = dist[node] + 1;
        st.push(next);
        }
      }
    }
}

int main(){
  int n, m; // number of nodes and edges
  cin >> n; // reads in number of nodes
  cin >> m; // reads in number of edges
  for(int i = 0; i < m; i++){ // reading in each of the m edges
    int a, b;
    cin >> a >> b;
    addEdge(a,b);
  }

  dfs(0);

  return 0;
}

Finding Components (Undirected Graph)

Counting Connected Components

1
2
3
4
5
6
7
8
9
10
11
int components = 0;
vector<bool> visited(N, false);

F0R(i, N) {
  if (!visited[i]) {
    components++;
    dfs(i, components);
  }
}

cout << "Total " << components << " components" << endl;  

Graph Coloring using DFS or BFS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
vector<int> adj[MX];
int coloring[MX];

string cowType;

bool flag(int current, int end) {
    if (current == end) {
        return true;
    }
    return false;
}

void DFS(int start, int color){
    // fill distance array with -1's

    vector<bool> visited(cowType.size(), false);
    stack<int> st;

    st.push(start);
    while(!st.empty()){
        int node = st.top();
        coloring[node] = color;
        visited[node] = true;
        st.pop();

        for(int next : adj[node]){
            if(! visited[next] && cowType[next] == cowType[node]){
                st.push(next);
            }
        }
    }
}

void DFSColoring(int N) {
    // read in N
    int color = 1;
    F0R(i, N) {
        if (coloring[i] == 0) {
            DFS(i, color);
            color++;
        }
    }
}