516 lines
13 KiB
Plaintext
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> |