Week 4: Graph Data Structures

COMP9024 26T1 ♢ Graph Data Structures ♢ [0/110]


❖ Week 4

Things to Note …

In This Lecture …

Coming Up …

COMP9024 26T1 ♢ Graph Data Structures ♢ [1/110]


❖ Nerds You Should Know

The next in a series on famous computer scientists …

What he invented affects your life every single day …

COMP9024 26T1 ♢ Graph Data Structures ♢ [2/110]


❖ Nerds You Should Know (cont)

Sir Tim Berners-Lee

 
  • Oxford CS Graduate (1976)
  • Software engineer at CERN
  • Founder/Director of W3C at MIT (1994)
  • Inventing the Web …
    • distributed hypertext
    • linking heterogeneous documents
    • universal naming scheme (URL)
    • transfer protocol (http)
    • later apologised for initial pair of slashes ('//') in a web address
    • also thinks he should have defined web addresses the other way round (au.edu.unsw.cse)
  • Winner of the Turing Award in 2016

COMP9024 26T1 ♢ Graph Data Structures ♢ [3/110]


❖ Nerds You Should Know (cont)

Tim Berners-Lee's original diagram of the "Web"
 

 
(from his proposal document, 1989)
Berners-Lee wrote: A web of notes with links between them.

From that idea came the basic components still used today:

The diagram was the conceptual model for the entire web.
COMP9024 26T1 ♢ Graph Data Structures ♢ [4/110]


❖ Graph Definitions

COMP9024 26T1 ♢ Graph Data Structures ♢ [5/110]


❖ Graphs

Many applications require

Examples: Collection types you're familiar with Graphs are more general … allow arbitrary connections
COMP9024 26T1 ♢ Graph Data Structures ♢ [6/110]


❖ Graphs (cont)

A graph G = (V,E)

Example:

[Diagram:Pic/graph1.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [7/110]


❖ Graphs (cont)

A real example: Australian road distances

Distance Adelaide Brisbane Canberra Darwin Melbourne Perth     Sydney
Adelaide -2055-30517322716-
Brisbane 2055--34291671-982
Canberra ----658-309
Darwin 30513429---4049-
Melbourne 7321671658---873
Perth 2716--4049---
Sydney -982309-873--

Notes: vertices are cities, edges are distance between cities, symmetric

COMP9024 26T1 ♢ Graph Data Structures ♢ [8/110]


❖ Graphs (cont)

Alternative representation of above:

[Diagram:Pic/ausroads.png]

The table is good for storing data systematically. The graph diagram is good for seeing structure quickly:

COMP9024 26T1 ♢ Graph Data Structures ♢ [9/110]


❖ Graphs (cont)

Questions we might ask about a graph:

Graph algorithms are generally more complex than tree/list ones:
COMP9024 26T1 ♢ Graph Data Structures ♢ [10/110]


❖ Properties of Graphs

For now, consider a graph with no (a,a) and no direction, no weight

Terminology: |V| and |E| (cardinality) normally written just as V and E.

A graph with V vertices has at most V(V-1)/2 edges.

The ratio E:V can vary considerably.

Knowing whether a graph is sparse or dense is important

COMP9024 26T1 ♢ Graph Data Structures ♢ [11/110]


❖ Exercise: Number of Edges

The edges in a graph represent pairs of connected vertices. A graph with V vertices has V2 such pairs.

Consider V = {1,2,3,4,5}   with all possible pairs:

E = { (1,1), (1,2), (1,3), (1,4), (1,5), (2,1), (2,2), …, (4,5), (5,5) }

Why do we say that the maximum #edges is V(V-1)/2?

COMP9024 26T1 ♢ Graph Data Structures ♢ [12/110]


… because
COMP9024 26T1 ♢ Graph Data Structures ♢ [13/110]


❖ Graph Terminology

For an edge e that connects vertices v and w

Degree of a vertex v Synonyms:
COMP9024 26T1 ♢ Graph Data Structures ♢ [14/110]


❖ Graph Terminology (cont)

Path: a sequence of vertices where

Simple path: a path where Cycle: a path Length of path or cycle:

[Diagram:Pic/pc-graphs.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [15/110]


❖ Graph Terminology (cont)

Connected graph

Complete graph KV

[Diagram:Pic/complete.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [16/110]


❖ Graph Terminology (cont)

Tree: connected (sub)graph with no cycles

Spanning tree: tree containing all vertices

Clique: complete subgraph

Consider the following single graph:

[Diagram:Pic/graph2.png]

This graph has 26 vertices, 33 edges, and 4 connected components

Note: The entire graph has no spanning tree; what is shown in green is a spanning tree of the third connected component

COMP9024 26T1 ♢ Graph Data Structures ♢ [17/110]


❖ Graph Terminology (cont)

A spanning tree of connected graph G = (V,E)

A spanning forest of non-connected graph G = (V,E)
COMP9024 26T1 ♢ Graph Data Structures ♢ [18/110]


❖ Exercise: Spanning Tree

[Diagram:Pic/graph1.png]


  1. How many edges need to be removed to obtain a spanning tree? (A spanning tree on V vertices always has exactly V-1)
  2. How many different spanning trees are there?
COMP9024 26T1 ♢ Graph Data Structures ♢ [19/110]


❖ Exercise: Spanning Tree (cont)

  1. 2
  2. `(5*4)/2-2 = 8` spanning trees   (no spanning tree if we remove {e1,e2} or {e3,e4})
COMP9024 26T1 ♢ Graph Data Structures ♢ [20/110]


❖ Graph Terminology

Undirected graph

Directed graph Examples:

[Diagram:Pic/ud-graphs.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [21/110]


❖ Graph Terminology (cont)

Other types of graphs …

Weighted graph

Multi-graph
COMP9024 26T1 ♢ Graph Data Structures ♢ [22/110]


❖ Graph Data Structures

COMP9024 26T1 ♢ Graph Data Structures ♢ [23/110]


❖ Graph Representations

Defining graphs:

E.g. four representations of the same graph:

[Diagram:Pic/graph-reps.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [24/110]


❖ Graph Representations (cont)

We will discuss three different graph data structures:

  1. Array of edges
  2. Adjacency matrix
  3. Adjacency list
COMP9024 26T1 ♢ Graph Data Structures ♢ [25/110]


❖ Array-of-edges Representation

Edges are represented as an array of Edge values (= pairs of vertices)

[Diagram:Pic/graph-array-edges.png]

For simplicity, we always assume vertices to be numbered 0..V-1

COMP9024 26T1 ♢ Graph Data Structures ♢ [26/110]


❖ Array-of-edges Representation (cont)

Graph initialisation (non-directed graph)

newGraph(V):
|  Input  number of nodes V
|  Output new empty graph
|
|  g.nV = V   // #vertices (numbered 0..V-1)
|  g.nE = 0   // #edges
|  allocate enough memory for g.edges[]
|  return g

How much is enough? … No more than V(V-1)/2 … Much less in practice (sparse graph)

COMP9024 26T1 ♢ Graph Data Structures ♢ [27/110]


❖ Array-of-edges Representation (cont)

Edge insertion

insertEdge(g,(v,w)):
|  Input graph g, edge (v,w)  // assumption: (v,w) not in g
|
|  g.edges[g.nE]=(v,w)
|  g.nE=g.nE+1

COMP9024 26T1 ♢ Graph Data Structures ♢ [28/110]


❖ Array-of-edges Representation (cont)

Edge removal

removeEdge(g,(v,w)):
|  Input graph g, edge (v,w)  // assumption: (v,w) in g
|
|  i=0
|  while (v,w)≠g.edges[i] do
|     i=i+1
|  end while
|  g.edges[i]=g.edges[g.nE-1] // replace (v,w) by last edge in array
|  g.nE=g.nE-1

COMP9024 26T1 ♢ Graph Data Structures ♢ [29/110]


❖ Cost Analysis

Storage cost: O(E) (big enough for # of edges)

Cost of operations:

If array is full on insert If we maintain edges in order
COMP9024 26T1 ♢ Graph Data Structures ♢ [30/110]


❖ Exercise: Array-of-edges Representation

Assuming an array-of-edges representation …

Write an algorithm to output all edges of the graph

COMP9024 26T1 ♢ Graph Data Structures ♢ [31/110]


show(g):
|  Input graph g
|
|  for all i=0 to g.nE-1 do
|     print g.edges[i]
|  end for

Time complexity: O(E)

COMP9024 26T1 ♢ Graph Data Structures ♢ [32/110]


❖ Adjacency Matrix Representation

Edges represented by a V × V matrix

[Diagram:Pic/adjmatrix.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [33/110]


❖ Adjacency Matrix Representation (cont)

Advantages

Disadvantages:
COMP9024 26T1 ♢ Graph Data Structures ♢ [34/110]


❖ Adjacency Matrix Representation (cont)

Graph initialisation

newGraph(V):
|  Input  number of nodes V
|  Output new empty graph
|
|  g.nV = V   // #vertices (numbered 0..V-1)
|  g.nE = 0   // #edges
|  allocate memory for g.edges[][]
|  for all i,j=0..V-1 do
|     g.edges[i][j]=0   // false
|  end for
|  return g

COMP9024 26T1 ♢ Graph Data Structures ♢ [35/110]


❖ Adjacency Matrix Representation (cont)

Edge insertion

insertEdge(g,(v,w)):
|  Input graph g, edge (v,w)
|
|  if g.edges[v][w]=0 then  // (v,w) not in graph
|     g.edges[v][w]=1       // set to true
|     g.edges[w][v]=1
|     g.nE=g.nE+1
|  end if

COMP9024 26T1 ♢ Graph Data Structures ♢ [36/110]


❖ Adjacency Matrix Representation (cont)

Edge removal

removeEdge(g,(v,w)):
|  Input graph g, edge (v,w)
|
|  if g.edges[v][w]≠0 then  // (v,w) in graph
|     g.edges[v][w]=0       // set to false
|     g.edges[w][v]=0
|     g.nE=g.nE-1
|  end if

COMP9024 26T1 ♢ Graph Data Structures ♢ [37/110]


❖ Exercise: Show Graph

Assuming an adjacency matrix representation (undirected graph) …

Write an algorithm to output all edges of the graph (no duplicates!)

COMP9024 26T1 ♢ Graph Data Structures ♢ [38/110]


❖ Exercise: Show Graph (cont)

show(g):
|  Input graph g
|
|  for all i=0 to g.nV-2 do
|  |  for all j=i+1 to g.nV-1 do
|  |     if g.edges[i][j] then
|  |        print i"—"j
|  |     end if
|  |  end for
|  end for

Time complexity: O(V2)

COMP9024 26T1 ♢ Graph Data Structures ♢ [39/110]


❖ Exercise: Adjacency Matrix Cost Analysis

Analyse storage cost and time complexity of adjacency matrix representation

COMP9024 26T1 ♢ Graph Data Structures ♢ [40/110]


❖ Exercise: Adjacency Matrix Cost Analysis (cont)

Analyse storage cost and time complexity of adjacency matrix representation

Storage cost: O(V2)

If the graph is sparse, most storage is wasted.

Cost of operations:

COMP9024 26T1 ♢ Graph Data Structures ♢ [41/110]


❖ Exercise: Adjacency Matrix Cost Analysis (cont)

A storage optimisation: store only top-right part of matrix.

[Diagram:Pic/graphRep2.png]

New storage cost: V-1 int ptrs + V(V-1)/2 ints   (but still O(V2))

Requires us to always use edges (v,w) such that v < w.

COMP9024 26T1 ♢ Graph Data Structures ♢ [42/110]


❖ Adjacency List Representation

For each vertex, store linked list of adjacent vertices:

[Diagram:Pic/adjlists.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [43/110]


❖ Adjacency List Representation (cont)

Advantages

Disadvantages:
COMP9024 26T1 ♢ Graph Data Structures ♢ [44/110]


❖ Adjacency List Representation (cont)

Graph initialisation

newGraph(V):
|  Input  number of nodes V
|  Output new empty graph
|
|  g.nV = V   // #vertices (numbered 0..V-1)
|  g.nE = 0   // #edges
|  allocate memory for g.edges[]
|  for all i=0..V-1 do
|     g.edges[i]=NULL   // empty list
|  end for
|  return g

COMP9024 26T1 ♢ Graph Data Structures ♢ [45/110]


❖ Adjacency List Representation (cont)

Edge insertion:

insertEdge(g,(v,w)):
|  Input graph g, edge (v,w)
|
|  insertLL(g.edges[v],w)
|  insertLL(g.edges[w],v)
|  g.nE=g.nE+1

COMP9024 26T1 ♢ Graph Data Structures ♢ [46/110]


❖ Adjacency List Representation (cont)

Edge removal:

removeEdge(g,(v,w)):
|  Input graph g, edge (v,w)
|
|  deleteLL(g.edges[v],w)
|  deleteLL(g.edges[w],v)
|  g.nE=g.nE-1

COMP9024 26T1 ♢ Graph Data Structures ♢ [47/110]


❖ Exercise: Adjacency List Cost Analysis

Analyse storage cost and time complexity of adjacency list representation

COMP9024 26T1 ♢ Graph Data Structures ♢ [48/110]


❖ Exercise: Adjacency List Cost Analysis (cont)

Analyse storage cost and time complexity of adjacency list representation

Storage cost: O(V+E)   (V   list pointers, total of 2·E   list elements)

Cost of operations:

If vertex lists are sorted
COMP9024 26T1 ♢ Graph Data Structures ♢ [49/110]


❖ Comparison of Graph Representations

 array
of edges
adjacency
matrix
adjacency
list
space usageEV2V+E
initialise1V2V
insert edge111
find/delete edgeE1V

Other operations:

 array
of edges
adjacency
matrix
adjacency
list
disconnected(v)?EV1
isPath(x,y)?E·log VV2V+E
copy graphEV2V+E
destroy graph1VV+E

COMP9024 26T1 ♢ Graph Data Structures ♢ [50/110]


❖ Graph Abstract Data Type

COMP9024 26T1 ♢ Graph Data Structures ♢ [51/110]


❖ Graph ADT

Data:

Operations: Things to note:
COMP9024 26T1 ♢ Graph Data Structures ♢ [52/110]


❖ Graph ADT (cont)

Graph ADT interface Graph.h

// graph representation is hidden
typedef struct GraphRep *Graph;

// vertices denoted by integers 0..N-1
typedef int Vertex;

// edges are pairs of vertices (end-points)
typedef struct Edge { Vertex v; Vertex w; } Edge;

// operations on graphs
Graph newGraph(int V);                 // new graph with V vertices
int   numOfVertices(Graph);            // get number of vertices in a graph
void  insertEdge(Graph, Edge);
void  removeEdge(Graph, Edge);
bool  adjacent(Graph, Vertex, Vertex); /* is there an edge
                                          between two vertices */
void  showGraph(Graph);                // print all edges in a graph
void  freeGraph(Graph);

COMP9024 26T1 ♢ Graph Data Structures ♢ [53/110]


❖ Exercise: Graph ADT Client

Write a program that uses the graph ADT to

[Diagram:Pic/graph4.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [54/110]


#include <stdio.h>
#include "Graph.h"

#define NODES 4
#define NODE_OF_INTEREST 1

int main(void) {
   Graph g = newGraph(NODES);

   Edge e;
   e.v = 0; e.w = 1; insertEdge(g,e);
   e.v = 0; e.w = 3; insertEdge(g,e);
   e.v = 1; e.w = 3; insertEdge(g,e);
   e.v = 3; e.w = 2; insertEdge(g,e);

   int v;
   for (v = 0; v < NODES; v++) {
      if (adjacent(g, v, NODE_OF_INTEREST))
         printf("%d\n", v);
   }

   freeGraph(g);
   return 0;
}

COMP9024 26T1 ♢ Graph Data Structures ♢ [55/110]


❖ Graph ADT (Array of Edges)

Implementation of GraphRep (array-of-edges representation)

typedef struct GraphRep {
   Edge *edges; // array of edges
   int   nV;    // #vertices (numbered 0..nV-1)
   int   nE;    // #edges
   int   n;     // size of edge array
} GraphRep;

[Diagram:Pic/graph-array-edges-rep.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [56/110]


❖ Graph ADT (Adjacency Matrix)

Implementation of GraphRep (adjacency-matrix representation)

typedef struct GraphRep {
   int  **edges; // adjacency matrix
   int    nV;    // #vertices
   int    nE;    // #edges
} GraphRep;


[Diagram:Pic/graphRep.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [57/110]


❖ Graph ADT (Adjacency Matrix) (cont)

Implementation of graph initialisation (adjacency-matrix representation)


Graph newGraph(int V) {
   assert(V >= 0);
   int i;

   Graph g = malloc(sizeof(GraphRep));      assert(g != NULL);
   g->nV = V;  g->nE = 0;

   // allocate memory for each row
   g->edges = malloc(V * sizeof(int *));    assert(g->edges != NULL);
   // allocate memory for each column and initialise with 0
   for (i = 0; i < V; i++) {                                            
      g->edges[i] = calloc(V, sizeof(int)); assert(g->edges[i] != NULL);
   }
   return g;
}

standard library function calloc(size_t nelems, size_t nbytes)

COMP9024 26T1 ♢ Graph Data Structures ♢ [58/110]


❖ Graph ADT (Adjacency Matrix) (cont)

Implementation of edge insertion/removal (adjacency-matrix representation)


// check if vertex is valid in a graph
bool validV(Graph g, Vertex v) {
   return (g != NULL && v >= 0 && v < g->nV);
}

void insertEdge(Graph g, Edge e) {
   assert(g != NULL && validV(g,e.v) && validV(g,e.w));

   if (!g->edges[e.v][e.w]) {  // edge e not in graph
      g->edges[e.v][e.w] = 1;
      g->edges[e.w][e.v] = 1;
      g->nE++;
   }
}

void removeEdge(Graph g, Edge e) {
   assert(g != NULL && validV(g,e.v) && validV(g,e.w));

   if (g->edges[e.v][e.w]) {   // edge e in graph
      g->edges[e.v][e.w] = 0;
      g->edges[e.w][e.v] = 0;
      g->nE--;
   }
}

COMP9024 26T1 ♢ Graph Data Structures ♢ [59/110]


❖ Exercise: Checking Neighbours (i)

Assuming an adjacency-matrix representation …

Implement a function to check whether two vertices are directly connected by an edge

bool adjacent(Graph g, Vertex x, Vertex y) { … }

COMP9024 26T1 ♢ Graph Data Structures ♢ [60/110]


bool adjacent(Graph g, Vertex x, Vertex y) {
   assert(g != NULL && validV(g,x) && validV(g,y));

   return (g->edges[x][y] != 0);
}

COMP9024 26T1 ♢ Graph Data Structures ♢ [61/110]


❖ Graph ADT (Adjacency List)

Implementation of GraphRep (adjacency-list representation)

typedef struct GraphRep {
   Node **edges;  // array of lists
   int    nV;     // #vertices
   int    nE;     // #edges
} GraphRep;

typedef struct Node {
   Vertex       v;
   struct Node *next; 
} Node;


[Diagram:Pic/graphRep3.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [62/110]


❖ Exercise: Checking Neighbours (ii)

Assuming an adjacency list representation …

Implement a function to check whether two vertices are directly connected by an edge

bool adjacent(Graph g, Vertex x, Vertex y) { … }

COMP9024 26T1 ♢ Graph Data Structures ♢ [63/110]


bool adjacent(Graph g, Vertex x, Vertex y) {
   assert(g != NULL && validV(g,x));
   
   return inLL(g->edges[x], y);
}

inLL() checks if linked list contains an element

COMP9024 26T1 ♢ Graph Data Structures ♢ [64/110]


❖ Problems on Graphs

COMP9024 26T1 ♢ Graph Data Structures ♢ [65/110]


❖ Problems on Graphs

What kind of problems do we want to solve on/via graphs?

COMP9024 26T1 ♢ Graph Data Structures ♢ [66/110]


❖ Graph Algorithms

In this course we examine algorithms for

COMP9024 26T1 ♢ Graph Data Structures ♢ [67/110]


❖ Graph Traversal

COMP9024 26T1 ♢ Graph Data Structures ♢ [68/110]


❖ Finding a Path

Questions on paths:

Approach to solving problem: Two strategies for graph traversal/search:   depth-first,   breadth-first
COMP9024 26T1 ♢ Graph Data Structures ♢ [69/110]


❖ Finding a Path (cont)

Comparison of BFS/DFS search for checking if there is a path from a to h

[Diagram:Pic/graph-search-bfs-dfs.png]

Both approaches ignore some edges by remembering previously visited vertices.

COMP9024 26T1 ♢ Graph Data Structures ♢ [70/110]


❖ Depth-first Search

Depth-first search can be described recursively as

depthFirst(G,v):

  1. mark v as visited
  2. for each (v,w)edges(G) do
       if w has not been visited then
          depthFirst(w)

The recursion induces backtracking

COMP9024 26T1 ♢ Graph Data Structures ♢ [71/110]


❖ Depth-first Search (cont)

Recursive DFS path checking

hasPath(G,src,dest):
|  Input  graph G, vertices src,dest
|  Output true if there is a path from src to dest in G,
|         false otherwise
|
|  mark all vertices in G as unvisited
|  return dfsPathCheck(G,src,dest)

dfsPathCheck(G,v,dest):
|  mark v as visited
|  if v=dest then            // found dest
|     return true
|  else
|  |  for all (v,w)∈edges(G) do
|  |  |  if w has not been visited then
|  |  |  |  if dfsPathCheck(G,w,dest) then
|  |  |  |     return true   // found path via w to dest
|  |  |  |  end if
|  |  |  end if
|  |  end for
|  end if
|  return false              // no path from v to dest

COMP9024 26T1 ♢ Graph Data Structures ♢ [72/110]


❖ Exercise: Depth-first Traversal (i)

Trace the execution of dfsPathCheck(G,0,5) on:

[Diagram:Pic/graph3.png]

Consider neighbours in ascending order

COMP9024 26T1 ♢ Graph Data Structures ♢ [73/110]



Answer:

0 - 1 - 2 - 3 - 4 - 5

COMP9024 26T1 ♢ Graph Data Structures ♢ [74/110]


❖ Exercise: Depth-first Traversal (i) (cont)

Cost analysis:

Time complexity of DFS: O(V+E)   (adjacency list representation)
COMP9024 26T1 ♢ Graph Data Structures ♢ [75/110]


❖ Exercise: Depth-first Traversal (i) (cont)

Note how different graph data structures affect cost:

In case of Adjacency List:

For dense graphsE ≅ V2O(V+E) = O(V2)
For sparse graphsE ≅ VO(V+E) = O(E)

COMP9024 26T1 ♢ Graph Data Structures ♢ [76/110]


❖ Exercise: Depth-first Traversal (i) (cont)

Knowing whether a path exists can be useful

Knowing what the path is even more useful
⇒ record the previously visited node as we search through the graph    (so that we can then trace path through graph)

Make use of global variable:

COMP9024 26T1 ♢ Graph Data Structures ♢ [77/110]


❖ Exercise: Depth-first Traversal (i) (cont)

visited[] // store previously visited node, for each vertex 0..nV-1

findPath(G,src,dest):
|  Input graph G, vertices src,dest
|
|  for all vertices v∈G do
|     visited[v]=-1
|  end for
|  visited[src]=src                  // starting node of the path
|  if dfsPathCheck(G,src,dest) then  // show path in dest..src order
|  |  v=dest
|  |  while v≠src do
|  |     print v"-"
|  |     v=visited[v]
|  |  end while
|  |  print src
|  end if

dfsPathCheck(G,v,dest):
|  if v=dest then               // found edge from v to dest
|     return true
|  else
|  |  for all (v,w)∈edges(G) do
|  |  |  if visited[w]=-1 then
|  |  |  |  visited[w]=v
|  |  |  |  if dfsPathCheck(G,w,dest) then
|  |  |  |     return true      // found path via w to dest
|  |  |  |  end if
|  |  |  end if
|  |  end for
|  end if
|  return false                 // no path from v to dest

COMP9024 26T1 ♢ Graph Data Structures ♢ [78/110]


❖ Exercise: Depth-first Traversal (ii)

Show the DFS order in which we visit vertices in this graph when searching for a path from 0 to 6:

[Diagram:Pic/traversal.png]

Consider neighbours in ascending order

COMP9024 26T1 ♢ Graph Data Structures ♢ [79/110]


0 0 3 5 3 1 5 4 7 8
  [0]     [1]     [2]     [3]     [4]     [5]     [6]     [7]     [8]     [9]  

Path: 6-5-1-0

COMP9024 26T1 ♢ Graph Data Structures ♢ [80/110]


❖ Exercise: Depth-first Traversal (ii) (cont)

DFS can also be described non-recursively (via a stack):

hasPath(G,src,dest):
|  Input  graph G, vertices src,dest
|  Output true if there is a path from src to dest in G,
|         false otherwise
|
|  mark all vertices in G as unvisited
|  push src onto new stack s
|  found=false
|  while not found and s is not empty do
|  |  pop v from s
|  |  mark v as visited
|  |  if v=dest then
|  |     found=true
|  |  else
|  |  |  for each (v,w)∈edges(G) such that w has not been visited
|  |  |     push w onto s
|  |  |  end for
|  |  end if
|  end while
|  return found

Uses standard stack operations   (push, pop, check if empty)

Time complexity is the same: O(V+E)

COMP9024 26T1 ♢ Graph Data Structures ♢ [81/110]


❖ Exercise: Depth-first Traversal (iii)

Show how the stack evolves when executing findPathDFS(g,0,5) on:

[Diagram:Pic/graph3.png]

Push neighbours in descending order … so they get popped in ascending order

COMP9024 26T1 ♢ Graph Data Structures ♢ [82/110]


45
3555
124444
444444
(empty)   →   0   →   5   →   5   →   5   →   5   →   5   →   5
COMP9024 26T1 ♢ Graph Data Structures ♢ [83/110]


❖ Breadth-first Search

Basic approach to breadth-first search (BFS):

Notes:
COMP9024 26T1 ♢ Graph Data Structures ♢ [84/110]


❖ Breadth-first Search (cont)

BFS algorithm (records visiting order, marks vertices as visited when put on queue):

visited[] // array of visiting orders, indexed by vertex 0..nV-1

findPathBFS(G,src,dest):
|  Input  graph G, vertices src,dest
|
|  for all vertices v∈G do
|     visited[v]=-1
|  end for
|  enqueue src into new queue q
|  visited[src]=src
|  found=false
|  while not found and q is not empty do
|  |  dequeue v from q
|  |  if v=dest then
|  |     found=true
|  |  else
|  |  |  for each (v,w)∈edges(G) such that visited[w]=-1 do
|  |  |     enqueue w into q
|  |  |     visited[w]=v
|  |  |  end for
|  |  end if
|  end while
|  if found then
|     display path in dest..src order
|  end if

Uses standard queue operations   (enqueue, dequeue, check if empty)

COMP9024 26T1 ♢ Graph Data Structures ♢ [85/110]


❖ Exercise: Breadth-first Traversal

Show the BFS order in which we visit vertices in this graph when searching for a path from 0 to 6:

[Diagram:Pic/traversal.png]

Consider neighbours in ascending order

COMP9024 26T1 ♢ Graph Data Structures ♢ [86/110]


0 0 0 2 5 0 5 5 3 -1
  [0]     [1]     [2]     [3]     [4]     [5]     [6]     [7]     [8]     [9]  

Path: 6-5-0

COMP9024 26T1 ♢ Graph Data Structures ♢ [87/110]


❖ Exercise: Breadth-first Traversal (cont)

Time complexity of BFS: O(V+E)   (adjacency list representation, same as DFS)

BFS finds a "shortest" path

In many applications, edges are weighted and we want path

We discuss weighted/directed graphs later.
COMP9024 26T1 ♢ Graph Data Structures ♢ [88/110]


❖ Other DFS Examples

Other problems to solve via DFS graph search

[Diagram:Pic/example0.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [89/110]


❖ Exercise: Buggy Cycle Check

A graph has a cycle if

We are not required to give the path, just indicate its presence.

The following DFS cycle check has two bugs. Find them.

hasCycle(G):
|  Input  graph G
|  Output true if G has a cycle, false otherwise
|
|  choose any vertex v∈G
|  return dfsCycleCheck(G,v)

dfsCycleCheck(G,v):
|  mark v as visited
|  for each (v,w)∈edges(G) do
|  |  if w has been visited then   // found cycle
|  |     return true
|  |  else if dfsCycleCheck(G,w) then
|  |     return true
|  end for
|  return false                    // no cycle at v

COMP9024 26T1 ♢ Graph Data Structures ♢ [90/110]


  1. Only one connected component is checked.
  2. The loop

      for each (v,w)∈edges(G) do
    

    should exclude the neighbour of v from which you just came, so as to prevent a single edge w-v from being classified as a cycle.

dfsCycleCheck(G,v,parent):
|  mark v as visited
|  for each (v,w)∈edges(G) do
|  |  if w has been visited and w is not parent then   // found cycle
|  |     return true
|  |  else if dfsCycleCheck(G,w,parent) then
|  |     return true
|  end for
|  return false                    // no cycle at v

COMP9024 26T1 ♢ Graph Data Structures ♢ [91/110]


❖ Computing Connected Components

Problems:

Both of the above can be solved if we can
COMP9024 26T1 ♢ Graph Data Structures ♢ [92/110]


❖ Computing Connected Components (cont)

Algorithm to assign vertices to connected components:

components(G):
|  Input graph G
|
|  for all vertices v∈G do
|     componentOf[v]=-1
|  end for
|  compID=0
|  for all vertices v∈G do
|  |  if componentOf[v]=-1 then
|  |     dfsComponents(G,v,compID)
|  |     compID=compID+1
|  |  end if
|  end for

dfsComponents(G,v,id):
|  componentOf[v]=id
|  for all vertices w adjacent to v do
|     if componentOf[w]=-1 then
|        dfsComponents(G,w,id)
|     end if
|  end for

COMP9024 26T1 ♢ Graph Data Structures ♢ [93/110]


❖ Exercise: Connected components

Trace the execution of the algorithm

  1. on the graph shown below
  2. on the same graph but with the dotted edges added

[Diagram:Pic/component-maint.png]

Consider neighbours in ascending order

COMP9024 26T1 ♢ Graph Data Structures ♢ [94/110]


  1.   [0]     [1]     [2]     [3]     [4]     [5]     [6]     [7]  
    -1 -1 -1 -1 -1 -1 -1 -1
    0 -1 -1 -1 -1 -1 -1 -1
    0 -1 0 -1 -1 -1 -1 -1
    0 0 0 -1 -1 -1 -1 -1
    0 0 0 1 -1 -1 -1 -1
    0 0 0 1 1 2 2 2

  2.   [0]     [1]     [2]     [3]     [4]     [5]     [6]     [7]  
    -1 -1 -1 -1 -1 -1 -1 -1
    0 -1 -1 -1 -1 -1 -1 -1
    0 0 -1 -1 -1 -1 -1 -1
    0 0 0 -1 -1 -1 -1 -1
    0 0 0 0 0 1 1 1

COMP9024 26T1 ♢ Graph Data Structures ♢ [95/110]


❖ Hamiltonian and Euler Paths

COMP9024 26T1 ♢ Graph Data Structures ♢ [96/110]


❖ Hamiltonian Path and Circuit

Hamiltonian path problem:

If v = w, then we have a Hamiltonian circuit

Simple to state, but difficult to solve (NP-complete)

Many real-world applications require you to visit all vertices of a graph:

Problem named after Irish mathematician, physicist and astronomer Sir William Rowan Hamilton (1805 — 1865)
COMP9024 26T1 ♢ Graph Data Structures ♢ [97/110]


❖ Hamiltonian Path and Circuit (cont)

Graph and two possible Hamiltonian paths:

[Diagram:Pic/hamilton1.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [98/110]


❖ Hamiltonian Path and Circuit (cont)

Approach:

Can be expressed via a recursive DFS algorithm
COMP9024 26T1 ♢ Graph Data Structures ♢ [99/110]


❖ Hamiltonian Path and Circuit (cont)

Algorithm for finding Hamiltonian path:

visited[] // array [0..nV-1] to keep track of visited vertices

hasHamiltonianPath(G,src,dest):
|  for all vertices v∈G do
|     visited[v]=false
|  end for
|  return hamiltonR(G,src,dest,#vertices(G)-1)

hamiltonR(G,v,dest,d):
|  Input G    graph
|        v    current vertex considered
|        dest destination vertex
|        d    distance "remaining" until path found
|
|  if v=dest then
|     if d=0 then return true else return false
|  else
|  |  mark v as visited
|  |  for each unvisited neighbour w of v in G do
|  |     if hamiltonR(G,w,dest,d-1) then //recursively ask whether a valid Hamiltonian path 
|  |        return true // can be completed from w
|  |     end if
|  |  end for
|  end if
|  mark v as unvisited           // reset visited mark
|                       // no hamiltonian path from v in this consideration
|  return false

COMP9024 26T1 ♢ Graph Data Structures ♢ [100/110]


❖ Exercise: Hamiltonian Path

Trace the execution of the algorithm when searching for a Hamiltonian path from 1 to 6:

[Diagram:Pic/traversal3.png]

Consider neighbours in ascending order

COMP9024 26T1 ♢ Graph Data Structures ♢ [101/110]


1-0-2-3-4-5-6         d≠0
1-0-2-3-4-5-7-8-9no unvisited neighbour
1-0-2-3-4-5-7-9-8no unvisited neighbour
1-0-2-3-4-7-5-6d≠0
1-0-2-3-4-7-8-9no unvisited neighbour
1-0-2-3-4-7-9-8no unvisited neighbour
1-0-2-3-4-8-7-5-6d≠0
1-0-2-3-4-8-7-9no unvisited neighbour
1-0-2-3-4-8-9-7-5-6

Repeat on your own with src=0 and dest=6

COMP9024 26T1 ♢ Graph Data Structures ♢ [102/110]


❖ Exercise: Hamiltonian Path (cont)

Analysis: worst case requires (V-1)! paths to be examined

Consider a graph with isolated vertex and the rest fully-connected

[Diagram:Pic/hamilton-worst.png]

Checking hasHamiltonianPath(g,x,0) for any x

There is no known simpler algorithm for this task

Note, however, that the above case could be solved in constant time if
we had a fast check for 0 and x being in the same connected component

COMP9024 26T1 ♢ Graph Data Structures ♢ [103/110]


❖ Euler Path and Circuit

Euler path problem:

If v = w, the we have an Euler circuit

[Diagram:Pic/euler-path-tour.png]

Many real-world applications require you to visit all edges of a graph:

Problem named after Swiss mathematician, physicist, astronomer, logician and engineer Leonhard Euler (1707 — 1783)
COMP9024 26T1 ♢ Graph Data Structures ♢ [104/110]


❖ Euler Path and Circuit (cont)

One possible "brute-force" approach:

Can develop a better algorithm by exploiting:

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

Theorem.   A graph has a non-circuitous Euler path if and only if
   it is connected and exactly two vertices have odd degree

COMP9024 26T1 ♢ Graph Data Structures ♢ [105/110]


❖ Exercise: Euler Paths and Circuits

Which of these two graphs have an Euler path? an Euler circuit?


[Diagram:Pic/graph-euler1.png]

[Diagram:Pic/graph-euler2.png]

COMP9024 26T1 ♢ Graph Data Structures ♢ [106/110]


No Euler circuit

Only the second graph has an Euler path, e.g. 2-0-1-3-2-4-5

COMP9024 26T1 ♢ Graph Data Structures ♢ [107/110]


❖ Exercise: Euler Paths and Circuits (cont)

Assume the existence of degree(g,v) (degree of a vertex)

Algorithm to check whether a graph has an Euler path:

hasEulerPath(G,src,dest):
|  Input  graph G, vertices src,dest
|  Output true if G has Euler path from src to dest
|         false otherwise
|
|  if degree(G,src) is even or degree(G,dest) is even then
|     return false
|  end if
|  for all vertices v∈G do
|     if v≠src and v≠dest and degree(G,v) is odd then
|        return false
|     end if
|  end for
|  return true

The basic idea: ensure that all vertices have even degree except for the src and dest, which have odd degrees. All non-source/destination vertices with odd degrees must be connected in pairs to form an even degree.

COMP9024 26T1 ♢ Graph Data Structures ♢ [108/110]


❖ Exercise: Euler Paths and Circuits (cont)

Analysis of hasEulerPath algorithm:

If degree requires iteration over vertices ⇒ problem tractable, even for large graphs   (unlike Hamiltonian path problem)

For the keen, a linear-time (in the number of edges, E) algorithm to compute an Euler path is described in [Sedgewick] Ch.17.7.

COMP9024 26T1 ♢ Graph Data Structures ♢ [109/110]


❖ Summary


COMP9024 26T1 ♢ Graph Data Structures ♢ [110/110]



Produced: 16 Mar 2026