Oct 7, 2022
Ok, we have a graph, now what do we do with it?
Given graph $G$:
Ok... back to the question: Connectivity!
The maze is a graph!
Primary Goals
object VertexLabel extends Enumeration
{ val UNEXPLORED, VISITED = Value }
object EdgeLabel extends Enumeration
{ val UNEXPLORED, SPANNING, BACK = Value }
def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
for(v <- graph.vertices) { v.setLabel(VertexLabel.UNEXPLORED) }
for(e <- graph.edges) { e.setLabel(EdgeLabel.UNEXPLORED) }
for(v <- graph.vertices) {
if(v.label == VertexLabel.UNEXPLORED){
DFSOne(graph, v)
}
}
}
def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
v.setLabel(VertexLabel.VISITED)
for(e <- v.incident) {
if(e.label == EdgeLabel.UNEXPLORED){
val w = e.getOpposite(v)
if(w.label == VertexLabel.UNEXPLORED){
e.setLabel(EdgeLabel.SPANNING)
DFSOne(graph, w)
} else {
e.setLabel(EdgeLabel.BACK)
}
}
}
}
The DFS algorithm is like our stack-based maze solver
What's the complexity?
def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
for(v <- graph.vertices) { v.setLabel(VertexLabel.UNEXPLORED) }
for(e <- graph.edges) { e.setLabel(EdgeLabel.UNEXPLORED) }
for(v <- graph.vertices) {
if(v.label == VertexLabel.UNEXPLORED){
DFSOne(graph, v)
}
}
}
def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
/* O(|V|) */
for(e <- graph.edges) { e.setLabel(EdgeLabel.UNEXPLORED) }
for(v <- graph.vertices) {
if(v.label == VertexLabel.UNEXPLORED){
DFSOne(graph, v)
}
}
}
def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
/* O(|V|) */
/* O(|E|) */
for(v <- graph.vertices) {
if(v.label == VertexLabel.UNEXPLORED){
DFSOne(graph, v)
}
}
}
def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
/* O(|V|) */
/* O(|E|) */
/* O(|V|) Times */ {
if(v.label == VertexLabel.UNEXPLORED){
DFSOne(graph, v)
}
}
}
def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
/* O(|V|) */
/* O(|E|) */
/* O(|V|) Times */ {
if(v.label == VertexLabel.UNEXPLORED){
/* ??? */
}
}
}
def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
v.setLabel(VertexLabel.VISITED)
for(e <- v.incident) {
if(e.label == EdgeLabel.UNEXPLORED){
val w = e.getOpposite(v)
if(w.label == VertexLabel.UNEXPLORED){
e.setLabel(EdgeLabel.SPANNING)
DFSOne(graph, w)
} else {
e.setLabel(EdgeLabel.BACK)
}
}
}
}
def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
/* O(1) */
for(e <- v.incident) {
if(e.label == EdgeLabel.UNEXPLORED){
val w = e.getOpposite(v)
if(w.label == VertexLabel.UNEXPLORED){
e.setLabel(EdgeLabel.SPANNING)
DFSOne(graph, w)
} else {
e.setLabel(EdgeLabel.BACK)
}
}
}
}
def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
/* O(1) */
/* O(deg(v)) times */ {
if(e.label == EdgeLabel.UNEXPLORED){
val w = e.getOpposite(v)
if(w.label == VertexLabel.UNEXPLORED){
e.setLabel(EdgeLabel.SPANNING)
DFSOne(graph, w)
} else {
e.setLabel(EdgeLabel.BACK)
}
}
}
}
def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
/* O(1) */
/* O(deg(v)) times */ {
/* O(1) */ {
/* O(1) */
/* O(1) */ {
/* O(1) */
DFSOne(graph, w)
} else {
/* O(1) */
}
}
}
}
def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
/* O(1) */
/* O(deg(v)) times */ {
/* O(1) */ {
/* O(1) */
/* O(1) */ {
/* O(1) */
/* ??? */
} else {
/* O(1) */
}
}
}
}
Observation: DFSOne is called on each vertex at most once.
$O(|V|)$ calls to DFSOne
What's the runtime of DFSOne excluding recursive calls?
def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
/* O(1) */
/* O(deg(v)) times */ {
/* O(1) */ {
/* O(1) */
/* O(1) */ {
/* O(1) */
DFSOne(graph, w)
} else {
/* O(1) */
}
}
}
}
What's the runtime of DFSOne excluding recursive calls?
$$O(deg(v))$$What's the sum over all calls to DFSOne?
$\sum_{v \in V} O(deg(v))$
$ = O(\sum_{v \in V} deg(v))$
$ = O(2 |E|)$ (by rule)
$ = O(|E|)$
Summing up...
Mark Vertices UNVISITED | $O(|V|)$ |
Mark Edges UNVISITED | $O(|E|)$ |
DFS Vertex Loop | $O(|V|)$ |
All Calls to DFSOne | $O(|E|)$ |
$O(|V| + |E|)$ |