BlockLune's Blog

Home Tags About |

Notes for Algorithms, Part II: Undirected Graphs

Published at 2023-07-27
Last update over 365 days ago Licensed under CC BY-NC-SA 4.0 algorithmdata-structurejavacourseragraph-theorynote

This is a note for 4.1 Undirected Graphs, Algorithms, Part II.

Introduction

Some graph-processing problems

  • Path. Is there a path between and ?
  • Shortest path. What is the shortest path between and ?
  • Cycle. Is there a cycle in the graph?
  • Euler tour. Is there a cycle that uses each edge exactly once?
  • Hamilton tour. Is there a cycle that uses each vertex exactly once? (classical NP-complete problem)
  • Connectivity. Is there a way to connect all of the vertices?
  • MST (Minimal Spanning Tree, 最小生成树). What is the best way to connect all of the vertices?
  • Biconnectivity (双连通性). Is there a vertex whose removal disconnects the graph? (DFS)
  • Planarity. Can you draw the graph in the plane with no crossing edges? (linear-time DFS-based planarity algorithm discovered by Tarjan in 1970s)
  • Graph isomorphism (同构). Do two adjacency lists represent the same graph? (graph isomorphism is longstanding open problem, 长期悬而未决)

Graph API

\< is only supported in math mode\begin{aligned} \text{public class } &\text{Graph} \\\\ &\text{Graph(int V)} &\text{create an empty graph with V vertices}\\\\ &\text{Graph(In in)} &\text{create a graph from input stream}\\\\ \text{void } &\text{addEdge(int v, int w)} &\text{add an edge v-w}\\\\ \text{Iterable\<Integer\> } &\text{adj(int v)} &\text{vertices adjacent to v}\\\\ \text{int } &\text{V()} &\text{number of vertices}\\\\ \text{int } &\text{E()} &\text{number of edges}\\\\ \text{String } &\text{toString()} &\text{string representation}\\\\ \end{aligned}

Typical graph-processing code

  • compute the degree of
public static in degree(Graph G, int v) {
    int degree = 0;
    for (int w : G.adj(v)) degree++;
    return degree;
}
  • compute maximum degree
public static int maxDegree(Graph G) {
    int max = 0;
    for (int v = 0; v < G.V(); v++) {
        if (degree(G, v) > max) {
            max = degree(G, v);
        }
    }
    return max;
}
  • compute average degree
public static double averageDegree(Graph G) {
    return 2.0 * G.E() / G.V();
}
  • compute self-loops
public static int numberOfSelfLoops(Graph G) {
    int count = 0;
    for (int v = 0; v < G.V(); v++) {
        for (int w : G.adj(v)) {
            if (v == w) {
                count++;
            }
        }
    }
    return count / 2;  // each edge counted twice
}

Representations

  • Set-of-edges graph representation
  • Adjacency-matrix graph representation
  • Adjacency-list graph representation
representationspaceadd edgeedge between v and w?iterate over vertices adjacent to v?
list of edgesE1EE
adjacency matrixV^21*1V
adjacency listsE+V1degree(v)degree(v)

* disallows parallel edges

Adjacency-list graph representation: Java implementation

public class Graph {
    private final int V;
    private Bag<Integer>[] adj;  // adjacency lists (using Bag data type)

    public Graph(int V) {
        this.V = V;
        adj = (Bag<Integer>[]) new Bag[V];  // create empty graph with V vertices
        for (int v = 0; v < V; v++) {
            adj[v] = new Bag<Integer>();
        }
    }

    public void addEdge(int v, int w) {
        adj[v].add(w);  // add edge v-w
        adj[w].add(v);  // (parallel edges and self-loops allowed)
    }

    public Iterable<Integer> adj(int v) {
        return adj[v];
    }
}

DFS (to visit a vertex ):

  • Mark as visited
  • Recursively visit all unmarked vertices adjacent to
public class DepthFirstPaths {
    private boolean[] marked;  // marked[v] = true if v connected to s
    private int[] edgeTo;      // edgeTo[v] = previous vertex on path from s to v
    private int s;

    public DepthFirstPaths(Graph G, int s) {
        ...                    // initialize data structures
        dfs(G, s);             // find vertices connected to s
    }

    private void dfs(Graph G, int v) {
        marked[v] = true;
        for (int w : G.adj(v)) {
            if (!marked[w]) {
                dfs(G, w);
                edgeTo[w] = v;
            }
        }
    }
}

DFS marks all vertices connected to in time proportional to the sum of their degrees.

After DFS, can find vertices connected to in constant time and can find a pth to (if one exists) in time proportional to its length.

public boolean hasPathTo(int v) {
    return marked[v];
}

public Iterable<Integer> pathTo(int v) {
    if (!hasPathTo(v)) return null;
    Stack<Integer> path = new Stack<Integer>();
    for (int x = v; x != s; x = edgeTo[x])
        path.push(x);
    path.push(s);
    return path;
}

BFS (from source vertex ):

  • Put onto a FIFO queue, and mark as visited
  • Repeat until the queue is empty:
    • remove the least recently added vertex
    • add each of ‘s unvisited neighbors to the queue, and mark them as visited

BFS computes shortest paths from to all other vertices in a graph in time proportional to

public class BreadthFirstPaths {
    private boolean[] marked;
    private int[] edgeTo;
    ...

    private void bfs(Graph G, int s) {
        Queue<Integer> q = new Queue<Integer>();
        q.enqueue(s);
        marked[s] = true;
        while (!q.isEmpty()) {
            int v = q.dequeue();
            for (int w : G.adj(v)) {
                if (!marked[w]) {
                    q.enqueue(w);
                    marked[w] = true;
                    edgeTo[w] = v;
                }
            }
        }
    }
}

Connected components

Connected components:

  • Initialize all vertices as unmarked
  • For each unmarked vertex , run DFS to identify all vertices discovered as part of the same component
public class CC {
    private boolean[] marked;
    private int[] id;   // id[v] = id of component containing v
    private int count;  // number of components

    public CC(Graph G) {
        marked = new boolean[G.V()];
        id = new int[G.V()];
        for (int v = 0; v < G.V(); v++) {
            if (!marked[v]) {
                dfs(G, v);
                count++;
            }
        }
    }

    public int count() {
        return count;
    }

    public int id(int v) {
        return id[v];
    }

    private void dfs(Graph G, int v) {
        marked[v] = true;
        id[v] = count;
        for (int w : G.adj(v)) {
            if (!marked[w]) {
                dfs(G, w);
            }
        }
    }
}