Graphs: CS 302 - Data Structures Section 9.3
Graphs: CS 302 - Data Structures Section 9.3
1 2
3 4
Applications
Hypertext Circuits
Formal definition of graphs
• A graph G is defined as follows:
G=(V,E)
V: a finite, nonempty set of vertices
E: a set of edges (pairs of vertices)
Undirected graphs
• When the edges in a graph have no
direction, the graph is called undirected
undirected graph
I
Graph terminology
• Adjacent nodes: two nodes are adjacent if
they are connected by an edge
7 is adjacent from 5
5 7 or
5 is adjacent to 7
7 is adjacent from/to 5
5 7 or
5 is adjacent from/to 7
Graph terminology
• Path: a sequence of vertices that connect
two nodes in a graph.
• The length of a path is the number of edges
in the path.
1 2
3 4
Graph terminology
• Complete graph: a graph in which every
vertex is directly connected to every other
vertex
Graph terminology (cont.)
• What is the number of edges E in a
complete directed graph with V vertices?
E=V * (V-1)
or E=O(V2)
Graph terminology (cont.)
• What is the number of edges E in a
complete undirected graph with V vertices?
E=V* (V-1) / 2
or E=O(V2)
Graph terminology (cont.)
• Weighted graph: a graph in which each edge
carries a value
Graph Implementation
• Array-based
• Linked-list-based
Array-based implementation
• Use a 1D array to represent the vertices
• Use a 2D array (i.e., adjacency matrix) to
represent the edges
Array-based implementation (cont’d)
Array-Based Implementation (cont.)
• Memory required
– O(V+V2)=O(V2)
• Preferred when
– The graph is dense: E = O(V2)
• Advantage
– Can quickly determine
if there is an edge between two vertices
• Disadvantage
– No quick way to determine the vertices adjacent
from another vertex
x ?
Linked-list-based implementation
• Use a 1D array to represent the vertices
• Use a list for each vertex v which contains the
vertices which are adjacent from v (adjacency
list)
Linked-list-based implementation (cont’d)
Link-List-based Implementation (cont.)
• Memory required O(V) for sparse graphs since E=O(V)
– O(V + E)
O(V2) for dense graphs since E=O(V2)
• Preferred when
– for sparse graphs: E = O(V)
• Disadvantage
– No quick way to determine whether
there is an edge between vertices u and v
• Advantage
– Can quickly determine the
vertices adjacent from a given vertex
x ?
Graph specification based on
adjacency matrix representation
const int NULL_EDGE = 0;
private:
template<class VertexType> int numVertices;
class GraphType { int maxVertices;
public: VertexType* vertices;
GraphType(int); int **edges;
~GraphType(); bool* marks;
void MakeEmpty(); };
bool IsEmpty() const;
bool IsFull() const;
void AddVertex(VertexType);
void AddEdge(VertexType, VertexType, int);
int WeightIs(VertexType, VertexType);
void GetToVertices(VertexType, QueType<VertexType>&);
void ClearMarks();
void MarkVertex(VertexType);
bool IsMarked(VertexType) const;
template<class VertexType>
GraphType<VertexType>::GraphType(int maxV)
{
numVertices = 0;
maxVertices = maxV;
delete [] marks;
}
void GraphType<VertexType>::AddVertex(VertexType
vertex)
{
vertices[numVertices] = vertex;
numVertices++;
}
template<class VertexType>
void GraphType<VertexType>::AddEdge(VertexType
fromVertex, VertexType toVertex, int weight)
{
int row;
int column;
IF(!found)
Write "Path does not exist"
startVertex endVertex
(initialization)
template <class VertexType>
void DepthFirstSearch(GraphType<VertexType> graph,
VertexType startVertex, VertexType endVertex)
{
StackType<VertexType> stack;
QueType<VertexType> vertexQ;
graph.ClearMarks();
stack.Push(startVertex);
do {
stack.Pop(vertex);
if(vertex == endVertex)
found = true;
(continues)
else
if(!graph.IsMarked(vertex)) {
graph.MarkVertex(vertex);
graph.GetToVertices(vertex, vertexQ);
while(!vertexQ.IsEmpty()) {
vertexQ.Dequeue(item);
if(!graph.IsMarked(item))
stack.Push(item);
}
}
if(!found)
cout << "Path not found" << endl;
}
Breadth-First-Searching (BFS)
• Main idea:
– Look at all possible paths at the same depth
before you go at a deeper level
– Back up as far as possible when you reach a
"dead end" (i.e., next vertex has been "marked"
or there is no next vertex)
• BFS uses a queue !
startVertex endVertex
IF(!found)
Write "Path does not exist"
startVertex endVertex
(initialization)
Duplicates: should we
mark a vertex when it is
Enqueued
or when it is Dequeued ?
....
template<class VertexType>
void BreadthFirtsSearch(GraphType<VertexType> graph,
VertexType startVertex, VertexType endVertex);
{
QueType<VertexType> queue;
QueType<VertexType> vertexQ;
graph.ClearMarks();
queue.Enqueue(startVertex);
do {
queue.Dequeue(vertex);
if(vertex == endVertex)
found = true;
(continues)
else
“mark” when dequeue a vertex
if(!graph.IsMarked(vertex)) {
allow duplicates!
graph.MarkVertex(vertex);
graph.GetToVertices(vertex, vertexQ);
while(!vertxQ.IsEmpty()) {
vertexQ.Dequeue(item);
if(!graph.IsMarked(item))
queue.Enqueue(item);
}
}
if(!found)
cout << "Path not found" << endl;
}
Time Analysis
template<class VertexType>
void BreadthFirtsSearch(GraphType<VertexType> graph,
VertexType startVertex, VertexType endVertex);
{
QueType<VertexType> queue;
QueType<VertexType> vertexQ;
graph.ClearMarks();
O(V)
queue.Enqueue(startVertex);
do {
queue.Dequeue(vertex); O(V) times
if(vertex == endVertex)
found = true;
(continues)
else { O(V) – arrays
if(!graph.IsMarked(vertex)) { O(Evi) – linked lists
graph.MarkVertex(vertex);
graph.GetToVertices(vertex, vertexQ);
while(!vertxQ.IsEmpty()) {
vertexQ.Dequeue(item);
if(!graph.IsMarked(item))
O(EVi) times
queue.Enqueue(item);
}
}
}
} while (!queue.IsEmpty() && !found);
if(!found)
cout << "Path not found" << endl;
}
Arrays: O(V+V2+Ev1+Ev2+…)=O(V2+E)=O(V2)
else { O(V) - arrays
if(!graph.IsMarked(vertex)) { O(Evi) – linked lists
graph.MarkVertex(vertex);
graph.GetToVertices(vertex, vertexQ);
while(!vertxQ.IsEmpty()) {
vertexQ.Dequeue(item);
if(!graph.IsMarked(item))
O(EVi) times
queue.Enqueue(item);
}
}
}
} while (!queue.IsEmpty() && !found);
if(!found)
cout << "Path not found" << endl;
}
O(V2) dense
Linked Lists: O(V+2Ev1+2Ev2+…)=O(V+E)
O(V) sparse
Shortest-path problem
• There might be multiple paths from a source
vertex to a destination vertex
• Shortest path: the path whose total weight
(i.e., sum of edge weights) is minimum
AustinHoustonAtlantaWashington:
1560 miles
AustinDallasDenverAtlantaWashington:
2980 miles
Variants of Shortest Path
• Single-pair shortest path
– Find a shortest path from u to v for given vertices u
and v
• All-pairs shortest-paths
– Find a shortest path from u to v for every pair of
vertices u and v
Notation
t x
• Weight of path p = v0, v1, . . . , vk
6
3 9
3
4
2 1
k s 0
w( p ) w( vi 1 , vi )
2 7
5 3
i 1 5 11
6
y z
negative-weight cycles. 3 4
c 6 d g
5 8
s 0
• If negative cycles are reachable 2
y
3
-3
7
• Positive-weight cycles:
– By removing the cycle, we can get a shorter path
• Zero-weight cycles
– No reason to use them
– Can remove them to obtain a path with same weight
Shortest-path algorithms
• Solving the shortest path problem in a brute-force
way requires enumerating all possible paths.
– There are O(V!) paths between a pair of vertices in a
acyclic graph containing V nodes.
t 5 x
6 -2
-3
8 7
s 0
-4
7 2
9
Relaxation Step
• Relaxing an edge (u, v) implies testing whether we can
improve the shortest path to v found so far by going
through u:
If d[v] > d[u] + w(u, v)
we can improve the shortest path to v
d[v]=d[u]+w(u,v)
s s
u v u v
2 2
5 9 5 6
RELAX(u, v, w) RELAX(u, v, w)
u v u v
2 2
5 7 5 6 no change
Bellman-Ford Algorithm
• Can handle negative weights.
• Detects negative cycles reachable from the source.
(t, x), (t, y), (t, z), (x, t), (y, x), (y, z), (z, x), (z, s), (s, t), (s, y)
t 5 x
6 -2
-3
8 7
s 0
-4
7 2
9
y z
Example
t 5 x t 5 x
6
6 -2 6 -2
-3 -3
8 7 8 7
s 0 Pass 1 s 0
-4 -4
7 2 7 2
7
9 9
y z y z
E: (t, x), (t, y), (t, z), (x, t), (y, x), (y, z), (z, x), (z, s), (s, t), (s, y)
Example (t, x), (t, y), (t, z), (x, t), (y, x), (y, z), (z, x), (z, s), (s, t), (s, y)
t 5 x t x
Pass 1 5
Pass 2
(from
6 6
4
11
6 -2 6 -2
previous -3 -3
slide) s 0 8 7 8 7
s 0
-4 -4
7 2 7 2
7 7
2
9 9
y z y z
Pass 3 t 5 x Pass 4 t 5 x
2
6
4
11 2
6
4
11
6 -2 6 -2
-3 -3
8 7 8 7
s 0 s 0
-4 -4
7 2 7 2
7
2 7 2
-2
9 9
y z y z
Detecting Negative Cycles:
needs an extra iteration
s b
for each edge (u, v) E do 2
0
if d[v] > d[u] + w(u, v)
then return FALSE -8 3
return TRUE
c
1st pass 2nd pass
s b s b
Consider edge (s, b):
2 2
0
-3
2 -6
-3 -1
2
d[b] = -1
-8 3 -8 3 d[s] + w(s, b) = -4
5 5
2
c c d[b] > d[s] + w(s, b)
(s,b) (b,c) (c,s) d[b]=-4
(d[b] keeps changing!)
BELLMAN-FORD Algorithm
1. INITIALIZE-SINGLE-SOURCE(V, s) O(V)
2. for i ← 1 to |V| - 1 O(V)
O(VE)
3. do for each edge (u, v) E O(E)
4. do RELAX(u, v, w)
5. for each edge (u, v) E O(E)
6. do if d[v] > d[u] + w(u, v)
7. then return FALSE
8. return TRUE
Time: O(V+VE+E)=O(VE)
Dijkstra’s Algorithm
• Cannot handle negative-weights!
– w(u, v) > 0, (u, v) E
S V-S
d[v]=δ (v) d[v]>δ (v)
Initially, S is empty
Dijkstra’s Algorithm (cont.)
• Vertices in V–S reside in a min-priority queue Q
– Priority of u determined by d[u]
– The “highest” priority vertex will be the one having the
smallest d[u] value.
Dijkstra (G, w, s)
S=<> Q=<s,t,x,z,y> S=<s> Q=<y,t,x,z>
Initialization
t 1 x t 1 x
10
10 9 10 9
2 3 4 6 2 3 4 6
s 0 s 0
5 7 5 7
5
2 2
y z y z
Example (cont.)
S=<s,y> Q=<z,t,x> S=<s,y,z> Q=<t,x>
t 1 x t 1 x
8
10
14 8 13
14
10 9 10 9
2 3 4 6 2 3 4 6
s 0 s 0
5 7 5 7
5
7 5 7
2 2
y z y z
Example (cont.)
t x t 1 x
1
8 13
9 8 9
10 9 10 9
2 4 2 3 4 6
s 0 3 6 s 0
7 5 7
5
5 7 5 7
2 2
y z y z