Abstract
Explore is the same algorithm as Graph Search where is a Stack. The main difference is that Graph Search is a Mid-Level Implementation while Explore is a Low-Level Implementation of Graph Search.
- Category: Graph Traversal (single-component reachability)
- Input: Graph , source vertex
- Output:
visitedarray; optionallyprev,pre,postfor path/timestamp tracking- Paradigm: Recursion (implicit stack, FILO order)
- Typical use cases: the recursive building block used inside Depth First Search (DFS); single-source reachability queries
Core Logic: Recursive Stack-Based Search
Explore vs. DFS
The explore algorithm uses the same underlying logic as Depth First Search (DFS) and undergoes a FILO scheme. The difference lies in Explore will not visit any nodes that are disconnected from the starting vertex; whereas DFS continues to search in disconnected vertices after fully exploring the current sub-graph.
- Mark the current vertex
sas visited. - For each edge
(s, u)out ofs, ifuis unvisited, recursively callexploreonu. - Once every neighbor has been checked (and their subtrees fully explored), the call returns — this is the “leaving the stack” moment.
Key Idea
exploreis the recursion, and the call stack is the stackFfrom Graph Search — there’s no separate data structure to manage, the language runtime does it for you.
Pseudocode (Mid-Level Implementation)
Graph Search (general form, for comparison)
Algorithm 31 Graph Search
procedure GraphSearch()
= empty, = ,
while is not empty do
Pick in
for all neighbors of do
if or then
move from to
move from to
return X
Explore (low-level implementation of Graph Search, = stack)
Algorithm 32 Explore
procedure explore()
visited() = true
for each edge do
if not visited() then
explore()
The output is an array visited such that visited(u) is true if and only if is reachable from , for all vertices .
Important
This implementation only gives information about whether there is a path from to another vertex. However, sometimes it is helpful to know what those paths are.
Variables & Data Structures
| Name | Type | Purpose |
|---|---|---|
visited | Boolean array | Marks whether a vertex has been discovered, preventing infinite loops on cycles |
| (implicit) | Call stack | Plays the role of F from Graph Search — FILO order gives depth-first behavior |
prev | Array (vertex → vertex) | (path-tracking version) Records the discovery edge for each vertex |
pre, post | Array (vertex → int) | (path-tracking version) Timestamps for when a vertex enters/leaves the stack |
clock | Integer counter | (path-tracking version) Global tick used to generate pre/post values |
Helper Functions / Operations Used
explore(G, u)— recursive call; the “push” onto the stack happens implicitly via the function callprevisit(v)— called whenventers the stack; setspre(v) = clockand incrementsclockpostvisit(v)— called whenvleaves the stack; setspost(v) = clockand incrementsclock
Optional Low-Level Implementation: Keep Track of Paths
We can include another array of information. Set prev(u) to be the “parent” of u in the DFS output tree. By also tracking when a node enters (pre) and leaves (post) the stack, we can know the connected structure of directed graphs.
Algorithm 33 Explore with Path Record
procedure explore()
visited() = true
previsit()
for each edge do
if not visited() then
prev() =
explore()
postvisit()
Algorithm 34 previsit
procedure previsit()//when vertex enters the stack
pre() = clock
clock++
Algorithm 35 postvisit
procedure postvisit()//when vertex leaves the stack
post() = clock
clock++
DFS Output Tree
A DFS output tree is the tree structure formed by the edges in the
prevarray after Explore has been performed on a graph. Whenexplorediscovers an unvisited neighborufrom vertexs, it recordsprev(u) = s— “I reacheduby traveling the edge(s, u).” Collecting all such discovery edges across the entire Explore gives the output tree — it contains only the edges Explore actually used to find new vertices. Any edge that led to an already-visited vertex is discarded.
- Connected graph → a single DFS output tree, rooted at the first vertex explored
- Disconnected graph → a DFS Output Forest, one tree per connected component, each rooted at the vertex that triggered
cc++Example: If DFS visits A → B → D → C, the output tree contains edges A–B, B–D, and D–C. Any edge encountered along the way that pointed to an already-visited vertex is not included.
Proof of Correctness
Claim: Upon termination of explore(G, s), visited(u) = True if and only if u is reachable from s (i.e. visited correctly computes the connected component containing s).
() Nothing unreachable is ever marked visited: explore only ever recurses along an actual edge (s, u) \in E. By induction on the recursion depth, every call explore(G, v) is only ever reached by following a chain of real edges starting from s, so every vertex marked visited is reachable from s.
() Everything reachable is eventually marked visited: Suppose for contradiction some vertex u reachable from s is never visited. Take a shortest path . Let be the first vertex on this path that is never visited. Since is visited (by minimality of ), and explore(G, v_{i-1}) iterates over every edge out of , it must check whether is visited — and since it isn’t, explore recurses into it, marking it visited. This contradicts the assumption that is never visited.
Termination: Each vertex is marked visited at most once (the if not visited(u) guard prevents re-entering an already-visited vertex), so the recursion depth and total number of calls are both bounded by , and the algorithm terminates in finite time on a finite graph.
Note
This proof only establishes correctness for the connected component containing
s—exploresays nothing about vertices outside that component by design (see Drawbacks below).
Time & Space Complexity Analysis
General Case
Let be the connected component containing s (i.e. the set of vertices reachable from s), with vertices and edges.
| Complexity | Notes | |
|---|---|---|
| Time | Each reachable vertex is visited once; each of its outgoing edges is examined once | |
| Space | worst case | Recursion (call stack) depth can be as large as the component itself on a narrow/deep path |
Note this is not over the whole graph — explore only touches the component reachable from s. To cover an entire (possibly disconnected) graph, you need the outer loop shown in Depth First Search (DFS), which restarts explore on every still-unvisited vertex.
Implementation-Dependent Variations
| Data Structure Choice | Impact on Time | Impact on Space | Notes |
|---|---|---|---|
| Recursive (implicit stack) vs. explicit stack | Same asymptotically | Recursion adds call-stack overhead; risk of stack overflow on deep/skinny components | The pasted algorithm here is the recursive form |
| Adjacency list vs. matrix | vs | vs | Matrix wastes time/space scanning non-edges |
visited: boolean array vs. hash set | either way | either way | Array needs vertices indexable by small integers |
Best / Worst / Average Case
- Best case:
shas no unvisited neighbors — beyond the initial call. - Worst case: Full connected component must be traversed — .
- Average case: Same order as worst case; no probabilistic behavior to average over.
Drawbacks / Constraints
- Only reaches one connected component.
exploreonly reaches the set of vertices reachable froms— it will never touch vertices in a different connected component, even if they exist in the same graph object. - To cover a disconnected graph, you must restart
exploreon a vertex that has not yet been visited — this is exactly what the outer loop in Depth First Search (DFS) does. - Reachability only, by default. The base version only tells you whether a path exists (
visited(u)), not what the path is — use the path-record version (prev,pre,post) if you need the actual route or timing structure. - Recursion depth risk. Since
exploreis naturally recursive, a very deep/narrow component can exhaust the call stack; an explicit-stack iterative rewrite avoids this.
Definition — Connected Undirected Graph An undirected graph is connected if for every pair of vertices in , there exists a path from to .