ANALYSIS OF ALGORITHMS

ANALYSIS OF ALGORITHMS
Graph Theory
Concepts
Definition 1- A graph G=(V,E) , is an order pair, where V is a finite set whose elements are called
vertices, and where E is a set of unordered pairs of distinct vertices of V, called edges.
v1
v2
V={v1, v2, v3, v4, v5 }
G1:
E={( v1, v3), ( v3, v4), ( v2, v4), ( v2, v5) ,( v4, v5)}
v3
v4
edges
v5
vertices
The above graph (G1) is called an undirected “simple” graph.
Also it is possible that we may have more than 1 edge “incident” at the same pair of vertices
(parallel edges).
v1
v2
G2:
V={v1, v2, v3, v4, v5 }
E={( v1, v3), ( v1, v3), ( v3, v4), ( v2, v4), ( v2, v5) ,
( v4, v5) ,( v4, v5)}
v3
v4
v5
The graph G2 is called a multigraph, because it has parallel edges.
Definition 2. A subgraph G’=(V’,E’) of a graph G=(V,E), is a graph such that
V’  V, and E’ E.
Example 1: Consider the following subgraphs of the graph G1:
v1
v2
G1:
v3
v4
v5
v1
v1
v2
v3
v3
v4
v1
v3
v4
v1
v4
v5
v5
v5
v3
v2
v4
v5
Definition 3 - Given a graph G=(V,E), the degree of a vertex v V, and denoted as d(v),
is the number of edges incident at v.
Example. In graph G1, d(v1)=1, d(v2 )=2, d(v3 )=2, d(v4)=3, d(v5 )=2.
Theorem 1. The sum of the degrees of the vertices of a graph G=(V,E) is twice the number of edges.
Proof. Consider a graph G=(V,E). Let n=|V|, and e=|E| (for example in G1, e=5, and n=5).
Consider the sum  d (v) , but every edge contributes 2 to this sum, thus
 d (v)  2e .
v V
v V
Example. Consider Graph G1, thus
 d (v)  1  2  2  3  2  10  2. e. , as e=5 (G1 has 5 edges).
v V
Theorem 2. The sum of the degrees of the vertices of a graph G is even.
Proof. It follows directly from Theorem 2.
Definition 4. A spanning subgraph G’=(V’,E’) of a graph G=(V,E), is a subgraph of G, such that
such that V’= V (i.e., it contains all the original vertices of G).
Example. Consider the following subgraphs of G1
v1
v3
v2
v4
Spanning subgraph because it
contains all the vertices of G1
v1
v5
v3
v4
Not a Spanning subgraph because v2
is not included.
v5
Walks, Trails, and Paths
Definition 5. A walk of a graph G=(V,E), is a alternative sequence of vertices and edges
< v1, e1, v2, e2, v3, e3, ….., ek-1, vk > such that ej = (vj, vj+1 ) is an edge.
Example: Consider G3
e1
v1
v2
G3:
e2
e6
v3
e3
v4
e5
v5
e4
A walk could be for example: <v2, e3, v5, e4, v4, e5, v3, e6, v1, e1, v2, e3, v5 >
Thus in a walk we can repeat vertices and edges (i.e., v2, e3, and v5 ).
Definition 6. A trail is a walk with no repetition of edges.
Example: Consider G3, a possible trail is
<v2, e3, v5, e4, v4, e5, v3, e6, v1, e1, v2 > (please note that the vertices may repeat).
Definition 7. A closed trail (cycle) is trail where the only repeated vertices are the first and the last
one (i.e., v1,= vk ).
Example: Consider G3, a possible cycle is
< v4, e2, v2 , e3, v5, e4, v4 > (please note that the vertices may repeat).
Definition 8. A path is a walk with no repetition of vertices.
Example: Consider G3, a possible path is
<v2, e3, v5, e4, v4, e5, v3, e6, v1 > (please note that a path is also a trail), and in this case we say that there
exists a v2,v1-path.
Definition 9. A graphG=(V,E) is said to be connected if for every pair of vertices u,v  V, there
exists an u,v-path connecting u and v.
Example: Consider again G3, clearly there exists a v1,v2-path, a v1,v3-path, v1,v4-path, v1,v5-path, a
v2,v3-path, v2,v4-path, v2,v5-path, v3,v4-path, v3,v5-path, and v4,v5-path, thus G3 is a
connected graph.
Example: Consider the following graph:
v1
v2
G6:
v3
v4
v5
Since there is no v1,v2- path (actually there are more pairs of vertices that there are not paths
connecting them, but one is sufficient), then G6 is disconnected.
Definition 10. A graph G=(V,E) is a “tree” is it is connected and has no cycles.
Example:
v1
v2
G5:
e3
e6
v3
v4
e5
v5
e4
Definition 11. A Spanning-tree of a graph G=(V,E) is a “tree” T=(V’,E’) that contains all the
vertices of G ( i.e., V’=V).
Example: Consider the graph G1,
v1
v2
G1:
v3
v4
v5
It has three (3) distinct spanning trees.
v1
v2
v3
v1
v4
v5
v1
v3
v3
v2
v4
v5
v2
v4
v5
Remark: The spanning trees of a graph play a very important role since is the minimal connected
graph, that is a spanning tree is a subgraph of G that contains minimal number of edges and still
permit all the nodes to be connected via paths.
Properties of spanning trees:
1) It is a minimally connected graph, that is, if we delete any edge, the resulted graph becomes
disconnected.
2) The number of edges of a spanning tree is equal to the number of vertices – 1.
Graph Representation
Given a graph G=(V,E) there are basically two data structures to represent a graph:
1) Adjacency matrix.
2) Adjacency List.
Given a graph G with n nodes, for simplicity we’ll label the vertices with non-negative integers
{0,1,2,…..,n-1}.
Adjacency Matrix
Consider the graph G3. The nodes are re-labeled as {0,1,2,3,4}
e1
0
1
G3:
e2
e6
2
e3
3
e5
4
e4
We will consider a 5X5 matrix A={ai,j }, and entry ai,j = 1, if the (i,j) is an edge of G, otherwise
ai,j = 0.
0
1
2
3
4
0
0
1
1
0
0
1
1
0
0
1
1
2
1
0
0
1
0
3
0
1
1
0
1
4
0
1
0
1
0
We can simply represent this matrix by a two dimensional array A[5][5].
Adjacency List.
A more efficient data structure is to create for each node, a list of nodes that are adjacent to them
Consider again re-labeled G3:
We will use a struct call node:
struct node {
int vertex;
node * next;
}
0
1
2
1
0
3
2
0
3
3
1
2
4
1
3
We can define a class Graph as follows:
4
4
Class Graph {
private: int n; // number of vertices
int e; // number of edges
node * headnodes;
public: Graph ( int nodes ) // construtor
{ n=nodes;
headnodes= new node [n]; // headnodes is an array of nodes.
for (int i=0; i < n; i++)
{ headnodes[i].vertex=i;
headnodes[i].next=0;
}
} // end class
Traversing graphs
We would like to visit a graph (all the nodes and edges) in a efficient way.
One of the techniques is call Depth-First Search (or DFS for short).
Assuming that all the vertices of the graph are labeled in {0,1,2,….,n-1}, DFS uses a data structure to
determine if a vertex was previously visited, called Visited [0..n-1] which is a Boolean array.
A18:
int main ( )
{ int n=8;
bool * Visited;
Visited=new bool [n];
Graph G(n); // constructor
G.create( ); // to create graph, that is the linked-list
for (int =0; i <=n; i++)
{ Visited[i]=false;
for (int j=0; j<=n;j++)
{ if (!Visited[j])
DFS(j);
}
}
void DFS(int v)
{ Visited [v]=true;
node* adjnode=headnodes[v].next
while (!adjnode) // visit all vertices adjacent to v
(1)
{ if (!Visited[adjnode.vertex]) //if adjacent vertex to v was not visited previously
DFS(adjnode.vertex);
}
}
e1
Example:
0
6
1
e2
e6
2
e3
3
e5
4
e4
Visited 0 1 2 3 4 5 6
F F F F F F F
5
We call first
DFS(0)
Visited 0 1 2 3 4 5 6
T F F F F F F
Vertices adjacent to 0 are 1 and 2.
Vertex 1 was not previously visited thus we call
DFS(1)
Vertices adjacent to 1 are 0, 3, and 4
Vertex 0 was already visited, but vertex 3 was not thus we call
Visited 0 1 2 3 4 5 6
T T F F F F F
DFS(3)
Vertices adjacent to 3 are 1, 2, and 4
1 was already visited, thus we call
Visited 0 1 2 3 4 5 6
T T F T F F F
DFS(2)
Vertices adjacent to 2 are 0 and 5, 0 was already visited
So we call
Visited 0 1 2 3 4 5 6
T T T T F F F
DFS(5)
Vertices adjacent to 5 are 2 and 4, but 2 was already visited
Thus we call
Visited 0 1 2 3 4 5 6
T T T T F T F
DFS(4)
Vertices adjacent to 4 are 1, 3 , and 5
But all these vertices were already visited
Visited 0 1 2 3 4 5 6
T T T T T T F
Thus we return to the vertex the last vertex it call DFS(4), which is vertex 5, but we already visited all
the vertices adjacent to 5 so we return to the “father’ of 5 which is vertex 2.
When we get back to vertex 2, all the vertices adjacent to 2 were already visited, thus we return to the
“father” of 2 which is 3. Similarly there are not any vertices adjacent to 3 that weren’t visited so
we return to 0, the father of 3. As the only vertex left adjacent to 0 is 6,
we call DFS(6). As the only vertex adjacent to 6 is 0, we finally return to 0.
Visited 0 1 2 3 4 5 6
T T T T T T T
The DFS’s calls can be described by the following tree.
0
6
1
3
2
5
45
That describes a spanning tree of the original graph.
As with respect to the complexity, consider instruction (1) in Algorithm A18, the if statement (basic
operation) checks if a vertex w adjacent to v, was not visited, thus the question is how many
comparisons we make. Please note that when we try to determine if w was already visited, is
equivalent to check for the edge (v,w), but when we are visiting w, we also check if v was visited,
thus once again we are checking for the edge (v,w). Thus the number of comparisons (if
statements) is 2e, where e =|E|, that is twice the number of edges of the graph.
We can slightly modify A18 to count the number of connected components of a graph G.
A19:
int main ( )
{ int n=8;
int Nmbrcmpnts=0; //we initialize the counter for the number of components
bool * Visited;
Visited=new bool [n];
Graph G(n); // constructor
G.create( ); // to create graph, that is the linked-list
for (int =0; i <=n; i++)
{ Visited[i]=false;
(2) for (int j=0; j<=n;j++)
{ if (!Visited[j]) {
DFS(j);
Nmbrcmpnts++;
}
}
}
The idea is that when we call DFS for a vertex v, all the vertices in the same connected component of
v will be visited by DFS(v), thus the next vertex to call DFS, must be a vertex in a different
connected component.
Example:
0
1
e2
6
2
e3
3
e5
5
4
e4
Visited 0 1 2 3 4 5 6
F F F F F F F
Originally, we have Nmbrcmpnts=0, and
By looking at line (2) of algorithm A19, the first call me make is DFS(0)
0
Visited 0 1 2 3 4 5 6
T F F F F F T
6
When we return from DFS(0), 0 and 6 were visited, and we increment Nmbrcmpnts which
becomes 1. Next we increment j in line (2), and we check if vertex 1 was visited, and it was not
visited, so we call
Visited 0 1 2 3 4 5 6
T F F F F F T
DFS(1).
1
3
2
5
45
When we return from DFS(1) we have visited every vertex
Visited 0 1 2 3 4 5 6
T T T T T T T
and we increment Nmbrcmpnts, thus it becomes 2.
Next we increment j in line (2), thus j=2, but vertex 2 was already visited, thus we increment j to 3,
but vertex 3 was visited, and so on, thus
The number of connected components is Nmbrcmpnts=2.
Also we can modify A19 to determine if a graph has cycles.
A20:
int main ( )
{ int n=8;
int Nmbrcmpnts=0; //we initialize the counter for the number of components
bool cycles=False;// determine if a graph has cycles.
bool * Visited;
Visited=new bool [n];
Graph G(n); // constructor
G.create( ); // to create graph, that is the linked-list
for (int =0; i <=n; i++)
{ Visited[i]=false;
(2) for (int j=0; j<=n;j++)
{ if (!Visited[j]) {
DFS(0,j);
Nmbrcmpnts++;
}
}
}
void DFS(int father, int v)
{ Visited [v]=true;
node* adjnode=headnodes[v].next
while (!adjnode) // visit all vertices adjacent to v
(1)
{ if (!Visited[adjnode.vertex]) //if adjacent vertex to v was not visited previously
DFS(v,adjnode.vertex); // when we call DFS, we pass the father vertex of the call
else if (father !=adjnode.vertex) // if the vertex adjacent to v is not the father, we have a
//cycle
{ cycle=True;
}
}
}