COMP2521

Data Structures & Algorithms

Week 5.4

Graph Algorithms

 

Author: Hayden Smith 2021

In this lecture

Why?

  • There are a range of further algorithms that can be used to explore graphs

What?

  • Cycle Checking
  • Connected Components
  • Hamiltonian Path and Circuit
  • Euler Path and Circuit

 

Cycle Checking

A graph has a cycle if, at any point in the graph:

  • It has a path of length > 2,
  • Where start vertex = end vertex, and
  • Without using any edge more than once

Cycle Checking

This graph has 3 distinct cycles: 0-1-2-0, 2-3-0-2, 0-1-2-3-0

Cycle Checking

This graph has MANY cycles: 0-4-3-0, 2-4-5-2, 0-1-2-5-4-6-3-0, etc...

Cycle Checking

Cycle checking involves two stages: The actual checking a cycle between two vertices, and the looping over all possible vertex combinations.

Implementation in C:

Vertex *visited;

bool hasCycle(Graph g, Vertex s) {
   bool result = false;
   visited = calloc(g->nV,sizeof(int));
   for (int v = 0; v < g->nV; v++) {
      for (int i = 0; i < g->nV; i++)
         visited[i] = -1;
      if (dfsCycleCheck(g, v, v)) {
         result = true;
         break;
      }
   }
   free(visited);
   return result;
}
bool dfsCycleCheck(Graph g, Vertex v, Vertex u) {
   visited[v] = true;
   for (Vertex w = 0; w < g->nV; w++) {
      if (adjacent(g, v, w)) {
         if (!visited[w]) {
            if (dfsCycleCheck(g, w, v))
               return true;
         }
         else if (w != u)
            return true;
      }
   }
   return false;
}

Connected Components

For a given graph (which may be disjoint), we want to find out: How many connected subgraphs are there?

 

Easiest solution: Create a componentOf[] array, where each index refers to a vertex, and the value at that index refers to which graph it's in.

Connected Components

For a given graph (which may be disjoint), we want to find out: How many connected subgraphs are there?

 

Easiest solution: Create a componentOf[] array, where each index refers to a vertex, and the value at that index refers to which graph it's in.

Algorithm

components(G):
  for all vertices v ∈ G:
    componentOf[v] = -1
  compID = 0  // component ID
  for all vertices v ∈ G:
    if componentOf[v] == -1:
      dfsComponent(G, v, compID)
      compID = compID + 1

dfsComponent(G, v, id):
  componentOf[v] = id
  for each (v,w) ∈ edges(G):
    if componentOf[w] == -1:
      dfsComponent(G, w, id)

Caching Info

For applications where connectivity is critical, we can't afford to run components() each time. But we can cache some information by adding information to our graph implementation struct. Adding or removing of edges may increase or decrease nC.

 

Another speedup: Ensure v, w are in the same component before starting a search

typedef struct GraphRep *Graph;

struct GraphRep {
   ...
   int nC;  // # connected components
   int *cc; // which component each vertex is contained in
   ...      // i.e. array [0..nV-1] of 0..nC-1
}

Hamiltonian Path & Circuit

Hamiltonian path problem: A path connecting two vertices (v, w) in graph G such that the path includes each vertex exactly once.

 

Hamiltonian Circuit: A Hamiltonian path where the starting vertex = ending vertex

 

Real world applications include:

  • Traveling Salesman
  • Bus routes

Hamiltonian Path Examples

Hamiltonian Path Search

Approach:

  • Generate all possible simple paths (e.g. using DFS)
  • Keep a counter of vertices visited in current path
  • Stop when find a path containing V vertices

 

Expressed via a recursive DFS algorithm. Very similar to path finding, except we track path lengths.

 

Algorithm

hasHamiltonianPath(G,src,dest):
  visited[] // keep track of visited vertices
  for all vertices v ∈ G:
    visited[v] = false
  return hamiltonR(G, src, dest, #vertices(G) - 1)

hamiltonR(G,v,dest,d):
  if v = dest:
    return d == 0
  else:
    visited[v] = true
    for each (v,w) ∈ edges(G) where not visited[w]:
      if hamiltonR(G, w, dest, d - 1):
        return true
  visited[v] = false // reset visited mark
  return false

Analysis

Worst case: O((V - 1)!)

  • As there are (V-1)! paths to be examined - as we have to consider every possible path.
  • There is no known simpler algorithm for this task, which makes it NP-hard.

 

Euler Path & Circuit

Euler path problem: Find a path connecting two vertices (v, w) in graph G such that the path includes each edge exactly once.

 

Euler Circuit: A Euler path where the starting vertex = ending vertex

 

Real world applications include:

  • Post delivery
  • Garbage truck pickup

Euler Path & Circuit

Origins

Origins are from Leonhard Euler who was trying to find a way to cross all the bridges of Konigsberg exactly once on a walk through the town.

 

Finding a Euler path/circuit

A brute force (do many checks, would be O(n!)) would be to find every possible path and check if it's a Euler path. But we have a shortcut:

 

1. A graph has a Euler circuit if and only if it is connected and all vertices have even degree

 

2. A graph has a Euler path (non-circuit) if and only if it is connected and exactly two vertices had odd degree

Algorithm: Euler path/circuit

hasEulerPath(G, src, dest):
  if src != dest:
    if degree(G, src) is even and degree(G,dest) is even:
      return false
  for all vertices v ∈ G:
     if v != src and  v != dest and degree(G,v) is odd:
        return false
  return true

If we assume connectivity is already checked:

  • O(V) if degree is O(1) lookup
    • Just a loop through all vertices
  • O(V^2) if degree needs to be calculated
    • To find degree need to look through V vertexes

 

Feedback

COMP2521 21T2 - 5.4 - Graph Algorithms

By haydensmith

COMP2521 21T2 - 5.4 - Graph Algorithms

  • 1,490