Commit 78f5d8b

mo khan <mo.khan@gmail.com>
2020-09-08 00:13:22
docs: add notes on graphs
1 parent 4d6aa98
Changed files (1)
doc
unit
doc/unit/12/README.md
@@ -0,0 +1,225 @@
+# 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<Integer> outEdges(int i) {
+  List<Integer> edges = new ArrayList<Integer>();
+  for (int j = 0; j < n; j++)
+    if (a[i][j])
+      edges.add(j);
+  return edges;
+}
+
+List<Integer> inEdges(int i) {
+  List<Integer> edges = new ArrayList<Integer>();
+  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<Integer>[] adj;
+
+AdjacencyLists(int nO) {
+  n = nO;
+  adj = (List<Integer>[])new List[n];
+  for (int i = 0; i < n; i++)
+    adj[i] = new ArrayStack<Integer>();
+}
+
+void addEdge(int i, int j) {
+  adj[i].add(j);
+}
+
+void removeEdge(int i, int j) {
+  Iterator<Integer> 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<Integer> outEdges(int i) {
+  return adj[i];
+}
+
+List<Integer> inEdges(int i) {
+  List<Integer> edges = new ArrayStack<Integer>();
+  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<Integer> q = new SLList<Integer>();
+  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<Integer> s = new Stack<Integer>();
+  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.