Lecture 14: Graphs and Distances

COSC 311 Algorithms, Fall 2022

$ \def\compare{ {\mathrm{compare}} } \def\swap{ {\mathrm{swap}} } \def\sort{ {\mathrm{sort}} } \def\insert{ {\mathrm{insert}} } \def\true{ {\mathrm{true}} } \def\false{ {\mathrm{false}} } \def\BubbleSort{ {\mathrm{BubbleSort}} } \def\SelectionSort{ {\mathrm{SelectionSort}} } \def\Merge{ {\mathrm{Merge}} } \def\MergeSort{ {\mathrm{MergeSort}} } \def\QuickSort{ {\mathrm{QuickSort}} } \def\Split{ {\mathrm{Split}} } \def\Multiply{ {\mathrm{Multiply}} } \def\Add{ {\mathrm{Add}} } \def\cur{ {\mathrm{cur}} } $

Announcements

  1. Midterm on Friday
    • Makeup date following week
    • Accommodations
  2. Study guide posted
    • Solutions to come (tomorrow?)

Overview

  1. Eulerian Graphs
  2. Graph Exploration

Last Time

Question. Is it possible to walk around Königsberg, cross every bridge exactly once, and return to where you started?

BoK as a Graph Problem

Original Question. Is it possible to walk around Königsberg, cross every bridge exactly once, and return to where you started?

Rephrasing as Graph Problem. Given a graph $G = (V, E)$, is there a circuit that contains every edge $e \in E$ exactly once?

  • A graph with this property is called Eulerian.

Theorem (Euler 1736). $G$ is Eulerian if and only if $G$ is connected and every vertex has even degree.

  • Showed “$\implies$” direction last time

Finding Eulerian Circuits

Assume:

  • $G$ is connected and all vertices have even degrees

Strategy:

  • Starting from vertex $v$, walk in any direction
    • cross any edge from current location
    • remove edge from future consideration
    • repeat
  • When stuck, reassess

Example

Finding a Circuit

Input:

  • graph $G$
    • set $V$ of vertices
    • set $E$ of edges
  • starting vertex $v \in V$
  • assume all vertices have even degree ($G$ is even)

Output:

  • a circuit $P = v_0 e_1 v_1 e_2 v_2 \cdots e_k v_k$ with $v_0 = v_k = v$
  • every edge $e$ incident to $v$ is contained in $P$

FindCircuit Subroutine

  FindCircuit(V, E, v):
    cur <- v
    P <- v
    while deg(cur) > 0
      e <- any edge in E incident to cur
      (prev, cur) <- e
      append e, cur to P
      remove e from E
      if deg(prev) = 0 then remove prev from V
    endwhile
    remove cur from V
    return P

Circuit Finding

Claim 1. If every vertex in $G = (V, E)$ has even degree, then FindCircuit(V, E, v) returns a circuit beginning and ending at $v$.

  • Loop invariant. If $\cur \neq v$, then $\cur$ and $v$ have odd degress, while all other vertices have even degrees.
  • Consequence. If $\deg(\cur) = 0$, then $\cur = v$.
    • $\implies$ can only get “stuck” at starting point!

Circuit Removal

$C = $ circuit found, removed after FindCircuit(V, E, v)

Question. What can we say about remaining graph?

Claim 2. Remaining graph $G - C$ has all even degrees.

Why?

  • vertices in $G$ originally have even degree
  • vertices in $C$ have even degrees
  • each vertex in $G$ has even number of edges removed
  • even - even = even

Finding Eulerian Circuits

Strategy.

  1. Apply FindCircuit to find a circuit $P = v_0 e_1 v_1\cdots v_k$
  2. Traverse $P$
    • if a vertex $v_i$ with $\deg(v_i) > 0$ is encountered,
      1. apply FindCircuit to $v_i$ to get a circuit $Q$
      2. splice $Q$ into $P$ at $v_i$
    • continue traversing $P$ (with $Q$ spliced in)

Example

Eulerian Circuit Pseudocode

  EulerCircuit(V, E, v):
    P <- FindCircuit(V, E, v)
    for each edge e = (u, w) in P do
       if deg(w) > 0 then
         Q <- EulerianCircuit(V, E, w)
         Splice(P, Q, w)
       endif
    endfor

Correctness

Claim. If $G$ is even and connected, then EulerCircuit returns an Eulerian circuit.

Argue by induction on $m = $ number of edges in $G$.

Base Case, $m = 0$. If $G$ is connected and has no edges, then $G$ has only one vertex, so EulerCircuit correctly outputs an Eulerian circuit (of length $0$)

Inductive Step

Suppose EulerCircuit finds an Eulerian circuit on all connected, even graphs with fewer than $m$ edges. Then:

  1. After removing $P$ from $G$, $G$ has fewer than $m$ edges
  2. $G$ is still even
  3. $G$ may be disconnected, but all components touch $P$
  4. By inductive hypothesis, EulerCircuit finds Eulerian circuit in each component
  5. Splicing together circuits gives Eulerian circuit for whole graph

Conclusion

$G$ is Eulerian if and only if $G$ is even and connected.

If $G$ is Eulerian, then an Eulerian circuit can be found by greedily traversing the graph, and recursively finding Eulerian circuits on remaining components.

More Bridges

Gephyrophobia = fear of bridges

Question. How to get from one landmass to another, crossing the fewest possible?

Strategy

Find shortest (fewest hops) route by:

  1. find all vertices reachable in 1 hop
  2. find all vertices reachable in 2 hops
  3. find all vertices reachable in 3 hops

Continue until destination is found

Illustration

Single Source Shortest Paths (SSSP)

Unweighted version

Input.

  • a Graph $G = (V, E)$
  • an initial vertex $u \in V$
  • each vertex $v \in V$ has associated adjacency list
    • list of $v$’s neighbors

Output.

  • A map $d: V \to \{-1, 0, 1, 2, \ldots\}$ such that $d(v)$ is length of shortest path (fewest hops) from $u$ to $v$

    • $d[v] = -1$ indicates no path from $u$ to $v$

Example

BFS Solution

Breadth-First Search

  1. start at $u$
  2. examine $u$’s neighbors, at distance $1$
  3. examine $u$’s neighbors’ neighbors, at distance $2$
  4. $\vdots$

Greedily examine closest vertices that have not yet been examined…

Queues

Abstract data type (ADT)

  • stores elements
  • two basic operations
    • enqueue(x) adds element x to queue
    • dequeue() removes and returns element
  • FIFO: first in, first out

BFS Pseudocode

  BFS(V, E, u):
    intialize d[v] <- -1 for all v
    d[u] <- 0
    queue.enqueue(u)
    while queue is not empty do
      v <- queue.dequeue()
      for each neighbor w of v do
        if d[w] = -1 then
          d[w] <- d[v] + 1
          queue.enqueue(w)
    return d

BFS Correctness

Theorem. When BFS(V, E, u) terminates, for every vertex $v \in V$, $d[v]$ stores the distance (minimum number of hops) from $u$ to $v$.

Analysis. Break $V$ into layers

  • $L_0$ contains only $u$
  • $L_1$ contains neighbors of $u$
  • $L_2$ contains neighbors of neighbors of $u$
  • $\vdots$
  • $L_k$ contains vertices not in $L_0, \ldots, L_{k-1}$ but with at least one neighbor in $L_{k-1}$

Layered Illustration

Layered Claims

Claim 1. A vertex $v$ is in $L_k$ if and only if the shortest path from $u$ to $v$ has length $k$.

Claim 2. In an execution of BFS the following hold for every $k$:

  1. If $v \in L_k$, then $v$ is added to the queue in some iteration
  2. when $v$ is added to the queue, $d[v] <- k$
  3. $v$ is added to the queue before any vertex $w \in L_\ell$ with $\ell > k$

Proofs: use induction on $k$ (details in text)

Conclusion. BFS correctly computes $d[v]$ for every v.

Summary

To find shortest paths (fewest hops) from $u$ to all other vertices:

  • greedily examine nearest vertices
  • added neighbors of nearest vertices to be examined next
    • use a queue to store pending vertices

Next Time

  • Weighted graphs
  • Priority queues