# Graphs ## Directed graph ```plaintext G = (V,E) where * V is a set of vertices * E is a set of ordered pairs of vertices called edges ``` An edge `(i, j)` is directed from `i` to `j`; * `i` is the source * `j` is the target A `path` in `G` is a sequence of vertices. Typical operations: * `addEdge(i,j)`: Add the edge (i, j) to E. `O(1)` time. * `removeEdge(i,j)`: Remove the edge `(i, j)` from E. `O(1)` time. * `hasEdge(i, j)`: Check if the edge `(i, j)` is an element of E. `O(1)` time. * `outEdges(i)`: Return a `List` of all ints `j` such that `(i,j)` is an element of E. `O(n)` time. * `inEdges(i)`: Return a `List` of all ints `j` such that `(j,i)` is an element of E. `O(n)` time. `O(n^2)` space. ### AdjacencyMatrix: Representing a Graph by a Matrix Is a way of representing an `n` vertex graph `G = (V,E)` by an `n x m` matrix, `a`, whose entries are boolean values. ```java int n; boolean[][] a; AdjacencyMatrix(int nO) { n = nO; a = new boolean[n][n]; } ``` The matrix entry `a[i][j]` is defined as: ```plaintext a[i][j] = { true if (i, j) element of E otherwise false ``` ```java void addEdge(int i, int j) { a[i][j] = true; } void removeEdge(int i, int j) { a[i][j] = false; } boolean hasEdge(int i, int j) { return a[i][j]; } List outEdges(int i) { List edges = new ArrayList(); for (int j = 0; j < n; j++) if (a[i][j]) edges.add(j); return edges; } List inEdges(int i) { List edges = new ArrayList(); for (int j = 0; j < n; j++) if (a[j][i]) edges.add(j); return edges; } ``` It is large. It stores `n x n` boolean matrix, so it requires at least `n^2` bits of memory. ### AdjacencyLists: A Graph as a Collection of Lists representations of graphs take a more vertex-centric approach. There are many possible implementations of adjacency lists. Operations: * `addEdge(i, j)`: `O(1)` time * `removeEdge(i, j)`: `O(deg(1))` time * `hasEdge(i, j)`: `O(deg(1))` time * `outEdges(i)`: in `O(1)` time * `inEdges(i)`: in `O(n + m)` time `O(n + m)` space. ```java int n; List[] adj; AdjacencyLists(int nO) { n = nO; adj = (List[])new List[n]; for (int i = 0; i < n; i++) adj[i] = new ArrayStack(); } void addEdge(int i, int j) { adj[i].add(j); } void removeEdge(int i, int j) { Iterator it = adj[i].iterator(); while (it.hasNext()) { if (it.next() == j) { it.remove(); return; } } } boolean hasEdge(int i, int j) { return adj[i].contains(j); } List outEdges(int i) { return adj[i]; } List inEdges(int i) { List edges = new ArrayStack(); for (int j = 0; j < n; j++) if (adj[j].contains(i)) edges.add(j); return edges; } ``` ## Graph Traversal ### Breadth-First Search starts at a vertex `i` and visits, first the neighbours of `i`, then the neighbours of the neighbours of `i`, then the neighbours of the neighbours of the neighbours of `i`, and so on. It uses a `queue` and makes sure that the same vertex is not added more than once. It does this be keep track of each vertex that has been visited. ```java void bfs(Graph g, int r) { boolean[] seen = new boolean[g.nVertices()]; Queue q = new SLList(); q.add(r); seen[r] = true; while (!q.isEmpty()) { int i = q.remove(); for (Integer j : g.outEdges(i)) { if (!seen[j]) { q.add(j); seen[j] = true; } } } } ``` time complexity: when using an adjacency list `O(n+m)`. BFS is useful for computing shortest paths. ### Depth-First Search is similar to the standard algorithm for traversing binary trees. It fully explores one subtree before returning to the current node and then exploring the other subtree. Another way to think of depth-first search is by saying that it is similar to breadth-first search except that it uses a stack instead of a queue. Each node has a state: * not visited * visited * visiting Think of it as a recursive algorithm. ```java void dfs(Graph g, int r) { byte[] c = new byte[g.nVertices()]; dfs(g, r, c); } // uses recursive stack void dfs(Graph g, int i, byte[] c) { c[i] = grey; for (Integer j : g.outEdges(i)) { if (c[j] == white) { c[j] = grey; dfs(g, j, c); } } c[i] = black; } void dfs2(Graph g, int r) { byte[] c = new byte[g.nVertices()]; Stack s = new Stack(); s.push(r); while (!s.isEmpty()) { int i = s.pop(); if (c[i] == white) { c[i] = grey; for (int j : g.outEdges(i)) s.push(j); } } } ``` time complexity: when using an adjacency list `O(n+m)` time.