One fact that seems to be left out of all the explanations:
Fact #1: In a depth first search spanning tree (DFSST), every backedge connects a vertex to one of its ancestors.
This is essential for the algorithm to work, it is why an arbitrary spanning tree won't work for the algorithm. It is also the reason why the root is an articulation point iff it has more than 1 child: there cannot be a backedge between the subtrees rooted at the children of the spanning tree's root.
A proof of the statement is, let (u, v) be a backedge where u is not an ancestor of v, and (WLOG) u is visited before v in the DFS. Let p be the deepest ancestor of both u and v. Then the DFS would have to visit p, then u, then somehow revisit p again before visiting v. But it isn't possible to revisit p before visiting v because there is an edge between u and v.
Call V(c) the set of vertices in the subtree rooted at c in the DFSST
Call N(c) the set of vertices for which that have a neighbor in V(c) (by edge or by backedge)
Fact #2:
For a non root node u,
If u has a child c such that N(c) ⊆ V(c) ∪ {u} then u is an articulation point.
Reason: for every vertex w in V(c), every path from the root to w must contain u. If not, such a path would have to contain a back edge that connects an ancestor of u to a descendant of u due to Fact #1, making N(c) larger than V(c).
Fact #3:
The converse of fact #2 is also true.
Reason: Every descendant of u has a path to the root that doesn't pass through u.
A descendant in V(c) can bypass u with a path through a backedge that connects V(c) to N(c)/V(c).
So for the algorithm, you only need to know 2 things about each non-root vertex u:
- The depth of the vertex, say D(u)
- The minimum depth of N(u), also called the lowpoint, lets say L(u)
So if a vertex u has a child c, and L(c) is less than D(u), then that mean the subtree rooted at c has a backedge that reaches out to an ancestor of u which makes it not an articulation point by Fact #3. Conversely also by Fact #2.