Homework 8-7 = ###### tags: `Course - Algorithmics` ## Design 1. First, we need to find the depth first spanning tree of the connected undirected graph. 2. Define `disc(u)`: the DFS visit sequence of vertex u. For example, `disc(4) = 1` in the following picture ![](https://i.imgur.com/T2xBovS.png) 3. Define `low(u)`: the lowest `disc()` that we can reach from u using a path of descendants followed by at most one back edge (nontree edge). :::info $low(u) = min(disc(u),$ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$min_{w:u.child}(low(w)),$ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$min_{edge(u,w):u.backEdge}(disc(w)))$ P.S. * u.child is a set of u's children * u.backEdge is a set of back edges containing u Can be simplified as: $low(u) = min(disc(u),disc(w))$ Where w is an ancestor of u and there is a back edge from some descendant of u to w. ::: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;For example, `low(4) = 0` in the following picture &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(because vertex 1 is vertex 4's descendant and edge(1, 3) is a back edge) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;![](https://i.imgur.com/10McDfh.png) 4. Any vertex u is an articulation point if and only if: * Case 1: u is the root of the spanning tree and has two or more children * Case 2: u is not the root and has at least one child w such that we cannot reach an ancestor of u using a path that consists of only 1. w 2. descendants of w 3. a single back edge * thus, low(w) ≥ disc(u) * explain: ![](https://i.imgur.com/RHa1cBD.png) ## Pseudo Code Assume there are n vertices in the given connected undirected graph g. * Initialize ```python= def init(): for l in adj: for v in l: disc[v] = -1 low[v] = -1 ap[v] = False counter = 0 ``` * Calculate disc() and low() by recursion ```python= def calc_disc_low(u, v): # v is the parent of u (if any) disc[u] = counter low[u] = counter counter++ for w in adj[u]: # u's Adjacency list if disc[w] < 0: # w is u's child in DFS tree calc_disc_low(w, u) # get low[w] by recursion t = min(disc[u], low[w]) elif w != v: # edge(u, w) is a back edge in DFS tree t = min(disc[u], disc[w]) # else, do nothing if t < low[u]: # update low[u] low[u] = t ``` * Find articulation points by modifing calc_disc_low() ```python= def find_AP(u, v): # v is the parent of u (if any) child = 0 # how many children u have disc[u] = counter low[u] = counter counter++ for w in adj[u]: # u's Adjacency list if disc[w] < 0: # w is u's child in DFS tree child++ find_AP(w, u) # get low[w] by recursion t = min(disc[u], low[w]) if v == Null & child > 1: # articulation point case 1 ap[u] = True if low[w] >= disc[u]: # articulation point case 2 ap[u] = True elif w != v: # edge(u, w) is a back edge in DFS tree t = min(disc[u], disc[w]) # else, do nothing if t < low[u]: # update low[u] low[u] = t ``` * Main function ```python= input g create adj[] init() find_AP(adj.begin.begin, Null) print(ap) ``` ## Analysis Let n = |V|, m = |E| * Time complexity: Same as DFS for adjacency list representation of graph. $\Theta(n + m)$ * Space complexity: Size of disc[] and low[] are both n Size of adjacency list is (n + m) Therefore, it's $\Theta(n + m)$