Website/src/teaching/cse-250/2022fa/slide/16b-GraphExploration.erb

516 lines
13 KiB
Plaintext

---
template: templates/cse250_2022_slides.erb
title: Graph Exploration
date: Oct 7, 2022
textbook: Ch. 15.3
cat: graphics/16b/cat.png
attribution: Based on slides by Tamassa Goodrich
---
<section>
<h4 class="slide_title">Edge List Summary</h4>
<ul>
<li>addEdge, addVertex: <b>$O(1)$</b></li>
<li>removeEdge: <b>$O(1)$</b></li>
<li>removeVertex: <b>$O(m)$</b></li>
<li>vertex.incidentEdges: <b>$O(m)$</b></li>
<li>vertex.edgeTo: <b>$O(m)$</b></li>
<li><b>Space Used</b>: $O(n) + O(m)$</li>
</ul>
</section>
<section>
<h4 class="slide_title">Adjacency List Summary</h4>
<ul>
<li>addEdge, addVertex: <b>$O(1)$</b></li>
<li>removeEdge: <b>$O(1)$</b></li>
<li>vertex.incidentEdges: <b>$O(deg(vertex))$</b></li>
<li>removeVertex: <b>$O(deg(vertex))$</b></li>
<li>vertex.edgeTo: <b>$O(deg(vertex))$</b></li>
<li><b>Space Used</b>: $O(n) + O(m)$</li>
</ul>
</section>
<section>
<h4 class="slide_title">Adjacency Matrix Summary</h4>
<ul>
<li>addEdge, removeEdge: <b>$O(1)$</b></li>
<li>addVertex, removeVertex: <b>$O(n^2)$</b></li>
<li>vertex.incidentEdges: <b>$O(n)$</b></li>
<li>vertex.edgeTo: <b>$O(1)$</b></li>
<li><b>Space Used</b>: $O(n^2)$</li>
</ul>
</section>
<section>
<p>Ok, we have a graph, now what do we do with it?</p>
</section>
<section>
<h4 class="slide_title">Connectivity Problems</h4>
<p>Given graph $G$:</p>
<ul>
<li class="fragment">Is vertex $u$ <b>adjacent</b> to $v$?</li>
<li class="fragment">Is vertex $u$ <b>connected</b> to $v$ via some path?</li>
<li class="fragment">Which vertices are connected to vertex $v$?</li>
<li class="fragment">What is the shortest path from vertex $u$ to $v$?</li>
</ul>
</section>
<section>
<h4 class="slide_title">A few more definitions...</h4>
<svg data-src="graphics/16b/subgraph-and-spanning.svg" width="200px" style="float: right"/>
<dl style="width: 700px">
<dt>A <u>subgraph</u> $S$ of a graph $G$ is a graph where...</dt>
<dd>$S$'s vertices are a subset of $G$'s vertices</dd>
<dd>$S$'s edges are a subset of $G$'s edges</dd>
<div class="fragment">
<dt>A <u>spanning subgraph</u> of $G$...</dt>
<dd>Is a subgraph of $G$</dd>
<dd>Contains all of $G$'s vertices.</dd>
</div>
</dl>
</section>
<section>
<h4 class="slide_title">A few more definitions...</h4>
<svg data-src="graphics/16b/dis-connected-graph.svg" width="200px" style="float: right"/>
<dl style="width: 700px">
<dt>A graph is <u>connected</u>...</dt>
<dd>If there is a path between every pair of vertices</dd>
<div class="fragment">
<dt>A <u>connected component</u> of $G$...</dt>
<dd>Is a maximal <i>connected</i> subgraph of $G$ <ul>
<li style="font-size: 80%">"maximal" means you can't add any new vertex without breaking the property</li>
<li style="font-size: 80%">Any subset of $G$'s edges that connects the subgraph is fine.</li>
</ul></dd>
</div>
</dl>
</section>
<section>
<h4 class="slide_title">A few more definitions...</h4>
<dl style="font-size: 90%">
<dt>A <u>free tree</u> is an <i>un</i>directed graph $T$ such that:</dt>
<dd>There is exactly one simple path between any two nodes <ul>
<li>T is connected</li>
<li>T has no cycles</li>
</ul></dd>
<div class="fragment">
<dt>A <u>rooted tree</u> is a directed graph $T$ such that:</dt>
<dd>One vertex of $T$ is the <u>root</u></dd>
<dd>There is exactly one simple path from the root to every other vertex in the graph.</dd>
</div>
<div class="fragment">
<dt>A (free/rooted) <u>forest</u> is a graph $F$ such that</dt>
<dd>Every connected component is a tree.</dd>
</div>
</dl>
</section>
<section>
<h4 class="slide_title">A few more definitions...</h4>
<dl>
<dt>A <u>spanning tree</u> of a connected graph...</dt>
<dd>Is a spanning subgraph that is a tree.</dd>
<dd>Is <i>not</i> unique unless the graph is a tree.</dd>
</dl>
<svg data-src="graphics/16b/spanning-tree.svg" />
</section>
<section>
<p>Ok... back to the question: Connectivity!</p>
</section>
<section>
<p>The maze is a graph!</p>
<svg data-src="graphics/16b/maze-is-graph.svg" />
</section>
<section>
<h4 class="slide_title">Recall</h4>
<dl>
<dt>Searching the maze with a stack <span class="fragment">(Depth-First Search)</span></dt>
<dd>Try out every path, one at a time to the end...</dd>
<dd>... then backtrack and try another</dd>
<dt>Searching the maze with a queue <span class="fragment">(Breadth-First Search)</span></dt>
<dd>Try out every path in parallel...</dd>
<dd>... keep picking a path to extend by one step.</dd>
</dl>
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<p><u>Primary Goals</u></p>
<ul>
<li>Visit every vertex in graph $G = (V, E)$</li>
<li>Construct a spanning tree for every connected component <ul style="font-size: 80%">
<li class="fragment"><b>Side Effect:</b> Compute connected components</li>
<li class="fragment"><b>Side Effect:</b> Compute a paths between all connected vertices</li>
<li class="fragment"><b>Side Effect:</b> Determine if the graph is connected</li>
<li class="fragment"><b>Side Effect:</b> Identify Cycles</li>
</ul></li>
<li class="fragment">Complete in time $O(|V| + |E|)$</li>
</ul>
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<dl>
<dt>DFS</dt>
<dd><b>Input:</b> Graph $G$</dd>
<dd><b>Output:</b> Label every edge as: <ul>
<li><u>Spanning Edge</u>: Part of the spanning tree</li>
<li><u>Back Edge</u>: Part of a cycle</li>
</ul></dd>
<div class="fragment">
<dt>DFSOne</dt>
<dd><b>Input:</b> Graph $G = (V, E)$, Start Vertex $v \in V$</dd>
<dd><b>Output:</b> Label every edge in $v$'s connected component</dd>
</div>
</dl>
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<svg data-src="graphics/16b/dfs-one-intuition.svg" />
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<pre><code class="scala">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)
}
}
} </code></pre>
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<pre><code class="scala">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)
}
}
}
} </code></pre>
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<svg data-src="graphics/16b/dfs-one-example.svg" />
</section>
<section>
<h4 class="slide_title">DFS vs Mazes</h4>
<p>The DFS algorithm is like our stack-based maze solver</p>
<ul>
<li>Mark each grid square with <b>VISITED</b>.</li>
<li>Mark each path with <b>SPANNING</b>.</li>
<li>Only visit each vertex once.<ul>
<li class="fragment">DFS won't necessarily find the shortest paths.</li>
</ul></li>
</ul>
</section>
<section>
<p>What's the complexity?</p>
</section>
<section>
<pre><code class="scala">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)
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">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)
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
/* O(|V|) */
/* O(|E|) */
for(v <- graph.vertices) {
if(v.label == VertexLabel.UNEXPLORED){
DFSOne(graph, v)
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
/* O(|V|) */
/* O(|E|) */
/* O(|V|) Times */ {
if(v.label == VertexLabel.UNEXPLORED){
DFSOne(graph, v)
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">def DFS(graph: Graph[VertexLabel.Value, EdgeLabel.Value])
{
/* O(|V|) */
/* O(|E|) */
/* O(|V|) Times */ {
if(v.label == VertexLabel.UNEXPLORED){
/* ??? */
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">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)
}
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">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)
}
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">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)
}
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">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) */
}
}
}
} </code></pre>
</section>
<section>
<pre><code class="scala">def DFSOne(graph: Graph[…], v: Graph[…]#Vertex)
{
/* O(1) */
/* O(deg(v)) times */ {
/* O(1) */ {
/* O(1) */
/* O(1) */ {
/* O(1) */
/* ??? */
} else {
/* O(1) */
}
}
}
} </code></pre>
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<p><b>Observation: </b> <tt>DFSOne</tt> is called on each vertex <i>at most</i> once.</p>
<ul>
<li>If <tt>v.label == VISITED</tt>, both <tt>DFS</tt>, <tt>DFSOne</tt> skip it.</li>
</ul>
<p class="fragment">$O(|V|)$ calls to <tt>DFSOne</tt></p>
<p class="fragment">What's the runtime of <tt>DFSOne</tt> <b>excluding recursive calls</b>?</p>
</section>
<section>
<pre><code class="scala">
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) */
}
}
}
} </code></pre>
</section>
<section>
<p>What's the runtime of <tt>DFSOne</tt> <b>excluding recursive calls</b>?</p>
$$O(deg(v))$$
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<p>
What's the sum over all calls to <tt>DFSOne</tt>?
</p>
<p class="fragment">
$\sum_{v \in V} O(deg(v))$
</p>
<p class="fragment">
$ = O(\sum_{v \in V} deg(v))$
</p>
<p class="fragment">
$ = O(2 |E|)$ (by rule)
</p>
<p class="fragment">
$ = O(|E|)$
</p>
</section>
<section>
<h4 class="slide_title">Depth-First Search</h4>
<p>Summing up...</p>
<table>
<tr class="fragment">
<td>Mark Vertices <b>UNVISITED</b></td>
<td class="fragment">$O(|V|)$</td>
</tr>
<tr class="fragment">
<td>Mark Edges <b>UNVISITED</b></td>
<td class="fragment">$O(|E|)$</td>
</tr>
<tr class="fragment">
<td><tt>DFS</tt> Vertex Loop</td>
<td class="fragment">$O(|V|)$</td>
</tr>
<tr class="fragment">
<td>All Calls to <tt>DFSOne</tt></td>
<td class="fragment">$O(|E|)$</td>
</tr>
<tr>
<td></td>
<td class="fragment"><span style="border-top: solid 1px black; padding-top: 15px">$O(|V| + |E|)$</span></td>
</tr>
</table>
</section>