Author: Hayden Smith 2021
Why?
What?
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:
Thinking about these things may help us decide on an appropriate data structure
There are many ways to represent the same graph. E.G. The same graph could be represented these ways:
There are 3 key different graph implementations we will discuss:
Array of Edges
Explicit representation as list of (v,w) vertex pairs
Adjacency Matrix
Edges defined by presence value in V*V matrix
Adjacency List
Edges defined by entries in array of V lists
Stored as an array of Edges (i.e. array of vertex pairs)
For directed graphs, the ordering of vertices within an edge pair denote direction
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;newGraph(V):
g = new Graph with V verticies
g.nV = V // #vertices (numbered 0..V-1)
g.nE = 0 // #edges
allocate enough memory for g.edges[]
return ginsertEdge(g,(v,w)):
i=0
while i < g.nE ∧ g.edges[i] ≠ (v,w):
i = i + 1
if i = g.nE: // (v,w) not found
g.edges[i] = (v,w)
g.nE = g.nE + 1removeEdge(g,(v,w)):
i = 0
while i < g.nE ∧ g.edges[i] ≠ (v,w):
i = i + 1
if i < g.nE: // (v,w) found
g.edges[i] = g.edges[g.nE-1]
g.nE = g.nE - 1showEdges(g):
for all i=0 to g.nE-1:
(v,w) = g.edges[i]
print v"—"w
Full implementation in Graph-array-edges.c
Stored as a 2D array (i.e. table) of edges. Easy to implement. Not memory efficient for sparse graphs.
For undirected graphs, the table is a mirror about the diagonal
typedef struct GraphRep {
int **edges; // adjacency matrix
int nV; // #vertices
int nE; // #edges
} GraphRep;newGraph(V):
g.nV = V // #vertices (numbered 0..V-1)
g.nE = 0 // #edges
allocate memory for g.edges[][]
for all i,j=0..V-1:
g.edges[i][j] = 0 // false
return ginsertEdge(g,(v,w)):
if g.edges[v][w] = 0: // (v,w) not in graph
g.edges[v][w]=1 // set to true
g.edges[w][v]=1
g.nE=g.nE+1
removeEdge(g,(v,w)):
if g.edges[v][w] ≠ 0: // (v,w) in graph
g.edges[v][w]=0 // set to false
g.edges[w][v]=0
g.nE=g.nE - 1showEdges(g):
for all i=0 to g.nV-1:
for all j=i+1 to g.nV-1:
if g.edges[i][j] ≠ 0 then
print i"—"j
Full implementation in Graph-adj-matrix.c
Stored as an array of linked lists. Each element of the array corresponds to a vertex, each node on that linked list is an edge connected to that vertex.
typedef struct GraphRep {
Node **edges; // array of lists
int nV; // #vertices
int nE; // #edges
} GraphRep;
// Assumes we also have
typedef struct Node {
Vertex v;
struct Node *next;
} Node;
// with operations inLL, insertLL, deleteLL, freeLLnewGraph(V):
g.nV = V // #vertices (numbered 0..V-1)
g.nE = 0 // #edges
allocate memory for g.edges[]
for all i=0..V-1:
g.edges[i]=newList() // empty list
return ginsertEdge(g,(v,w)):
if not ListMember(g.edges[v],w):
// (v,w) not in graph
ListInsert(g.edges[v],w)
ListInsert(g.edges[w],v)
g.nE=g.nE+1
removeEdge(g,(v,w)):
if ListMember(g.edges[v],w):
// (v,w) in graph
ListDelete(g.edges[v],w)
ListDelete(g.edges[w],v)
g.nE=g.nE-1showEdges(g):
for all i=0 to g.nV-1:
for all v in g.edges[i]:
if i < v:
print i"—"v
Full implementation in Graph-adj-list.c
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
| Array of edges | Adjacency Matrix | Adjacency List | |
|---|---|---|---|
| Space usage | E | V^2 | V + E |
| Initialise | 1 | V^2 | V |
| Insert edge | E | 1 | V |
| Remove edge | E | 1 | V |
| isPath(x,y) | E*log(V) | V^2 | V + E |
| Copy graph | E | V^2 | V + E |
| Destroy Graph | 1 | V | V + E |
| Search | E | 1 | V |