Vous êtes sur la page 1sur 8

Depth First Search

The strategy followed by depth-first search is, as its name implies, to


search deeper in the graph whenever possible. Depth-first search explores
edges out of the most recently discovered vertex v that still has unexplored
edges leaving it. Once all of vs edges have been explored, the search
backtracks to explore edges leaving the vertex from which v was
discovered. This process continues until we have discovered all the
vertices that are reachable from the original source vertex. If any
undiscovered vertices remain, then depth-first search selects one of them
as a new source, and it repeats the search from that source. The algorithm
repeats this entire process until it has discovered every vertex.
As in breadth-first search, whenever depth-first search discovers a
vertex v during a scan of the adjacency list of an already discovered vertex
u, it records this event by setting vs predecessor attribute v. to u. Unlike
breadth-first search, whose predecessor sub graph forms a tree, the
predecessor sub graph produced by a depth-first search may be composed
of several trees, because the search may repeat from multiple sources. The
predecessor subgraph of a depth-first search forms a depth-first forest
comprising several depth-first trees.
As in breadth-first search, depth-first search colors vertices during the
search to indicate their state. Each vertex is initially white, is grayed when
it is discovered in the search, and is blackened when it is finished, that is,
when its adjacency list has been examined completely. This technique
guarantees that each vertex ends up in exactly one depth-first tree, so that
these trees are disjoint. Besides creating a depth-first forest, depth-first
search also timestamps each vertex. Each vertex v has two timestamps:
the first timestamp v.d records when v is first discovered (and grayed),
and the second timestamp v.f records when the search finishes examining
vs adjacency list (and blackens v). These timestamps provide important
information about the structure of the graph and are generally helpful in
reasoning about the behavior of depth-first search.

The following pseudo code is the basic depth-first-search algorithm. The


input graph G may be undirected or directed. The variable time is a global
variable that we use for time stamping.
DFS(G)
1 for each vertex u G.V
2 u.color = WHITE
3 u. = NIL
4 time =0
5 for each vertex u G.V
6 if u.color == WHITE
7 DFS-VISIT(G, u)
DFS-VISIT (G, u)
1 time = time + 1

// white vertex u has just been discovered

2 u.d = time
3 u.color = GRAY
4 for each v G.Adj [u]

// explore edge (u,v)

5 if v.color == WHITE
6 v. = u
7 DFS-VISIT (G,v)
8 u.color = BLACK

// blacken u; it is finished

9 time = time + 1
10 u.f = time

The procedure DFS above records when it discovers vertex u in the


attribute u.d and when it finishes vertex u in the attribute u.f .
Procedure DFS works as follows. Lines 13 paint all vertices white and
initialize their attributes to NIL. Line 4 resets the global time counter.

Lines 57 check each vertex in V in turn and, when a white vertex is


found, visit it using DFS-VISIT. Every time DFS-VISIT(G, u) is called in
line 7, vertex u becomes the root of a new tree in the depth-first forest.
When DFS returns, every vertex u has been assigned a discovery time u.d
and a finishing time u.f . In each call DFS-VISIT(G, u), vertex u is initially
white. Line 1 increments the global variable time, line 2 records the new
value of time as the discovery time u.d, and line 3 paints u gray. Lines 4
7 examine each vertex v adjacent to u and recursively visit v if it is white.
As each vertex v Adj [u] is considered in line 4, we say that edge (u,v)
is explored by the depth-first search. Finally, after every edge leaving u
has been explored, lines 810 paint u black, increment time, and record
the finishing time in u.f . Here is an example to understand the algorithm
better. The figure is self explanatory.

Prims algorithm using Priority Queue


Priority Queue:
Keys or key-value pairs are stored in a data structure.
The operations usually supported are:
Insert: insert a new key-value pair.
Delete: delete a key-value pair.
Search: given a key, find the corresponding value.
Find minimum, maximum among the keys.
A priority queue with the decreaseKey operation:
For convenience, we will combine minimum and remove (delete) from a
priority into a single operation as extractMin
When we like to change a key while it's in the data structure, its location
in data structure also needs to be changed.
If the data structure is

Sorted linked list:


o Potentially O(n) time for decreaseKey
o O(1) time for extractMin
Unsorted array:
o Potentially O(n) time for extractMin
o O(1) time for decreaseKey.

If we use min heap,


Height of heap is O(log(n)) always.Each extractMin results in
changes along one tree path.
=> O(log(n)) worst-case.
Also for decreasekey operation it is O(log(n)) .

So it is better. Now we see the pseudo code for decreasekey operation.


Algorithm: decreaseKey (heapIndex, newKey)
Input: index in heap-array where key is located, new key value
// Assume: key-value pairs are stored in an array called
data[].
1.

// Change to new key.


data[heapIndex] = newKey

// Check up the heap (since it's a decrease operation).


while heapIndex > 0
// Identify parent.
3.
parentIndex = parent (heapIndex)
4.
if data[parentIndex] > data[heapIndex]
// If parent is larger, swap up.
5.
swap (parentIndex, heapIndex)
6.
heapIndex = parentIndex
7.
else
// Otherwise we've found the right position, so
stop.
8.
heapIndex = -1
9.
endif
10. endwhile
2.

Knowing these basic operation of priority queue we move on to pseudo


code for prims algorithm for minimum spanning tree.
Algorithm: Prim-MST (G(V,E))
Input: Grapg G(V,E)
For all u V
1.
priority[u] = infinity
2.
priorityQueue = all vertices (and their priorities)
// Set the priority of a source vertex s to 0.
3.
decreaseKey (s, 0)
4.
5.
6.
7.

A= \\ MST initialize to null.


while priorityQueue.notEmpty()
// Extract best vertex.
v = priorityQueue.extractMin()
// Explore edges going out from v.
for each edge e=(v, u) in adj[v]
w = weight of edge e;
// If there's an edge and it's not a self-loop.

8.
9.
10.

if priority[u] > w
// New priority.
priorityQueue.decreaseKey (u, w)
predecessor[u] = v

11.

endif

12.
13.
14.

endfor
A=A U {v}
endwhile

Priority queue contains all the vertices priority pairs of the graph.
Initially all the priorities are set to infinity and a source is chosen at
random and its priority is set to zero. Here extractmin de-queues the
minimum key from the priority key .its complexity is O(log(n)).
Dijkstra's Algorithm for SPT using priority key :
It is similar to prim s algorithm with ony slight change in the pseudo
code . Here is the pseudo code :
Algorithm: Dijkstra-SPT (G, s)
Input: Graph G=(V,E) with edge weights and designated source
vertex s.
1.
2.

// Initialize priorities and place in priority queue.


Set priority[i] = infinity for each vertex i;
Insert vertices and priorities into priorityQueue;

// Source s has priority 0 and is placed in SPT


priorityQueue.decreaseKey (s, 0)
Predecessor=s
// Now process vertices one by one in order of priority
(which
// may change during processing)
4.
while priorityQueue.notEmpty()
// Get "best" vertex out of queue.
5.
v = priorityQueue.extractMin()
// Place in current SPT.
6.
Add v to SPT;
// Explore edges from v.
7.
for each neighbor u of v
8.
w = weight of edge (v, u);
3.

// If there's a better way to get to u (via v),


then update.
9.
if priority[u] > priority[v] + w
10.
priorityQueue.decreaseKey (u, priority[v] + w)
11.
predecessor=v;
12.
endif
13.
endfor
14. endwhile
15. return SPT

Here also the operations of priority key are same as that for prims
algorithm. Here, we see the only difference is in line 9 and 10 were we
compare the priority of vertex with edge weight plus the priority of the
neighbor and update accordingly. The resulting tree is shortest path
tree (SPT). This is Djikshtra s algorithm.
Complexity Analysis of prims, kruskals and Djikshtras algorithm:
E is the number of edges and V is the number of vertex in G(V,E).
Prims algorithm with priority Queue :

Each edge is processed once.


=> O(E)
=> O(E) decreaseKey operations (worst-case)
=> O(E log(V)) time in decreaseKey operations.
Loop is executed O(V) times
=> O(V) extractMin operations
=> O(V log(V)) cost.
Total time: O(E log(V)) + O(V log(V)) = O(E log(V)).

Prims algorithm without priority Queue :

Initializations: all are O(V).


There are V iterations of the while-loop.
Finding the lowest-priority: O(V) (scan of priority array).
Scan of neighbors: O(V)
Total work in while-loop: O(V2).
Total time: O(V2).

Kruskal s algorithm :

To place edges in list: simply scan each vertex list once.


=> O(E) time.
Sorting edges: O(E log(E)).
One union-find operation for each edge: O(E log(V)).
Total: O(E)+ O(E log(E)) + O(E log(V))
=> O(E log(E))
=> O(E log(V))

Djikshtras algorithm with priority Queue :

Each edge is processed just once (when explored):


=> O(E) decreaseKey operations (worst-case)
=> O(E log(V)) time for all decreaseKey operations.
Loop is executed O(V) times
=> O(V) extractMin operations
=> O(V log(V)) cost.
Total time: O(E log(V)) + O(V log(V)) = O(E log(V)).

Djikshtras algorithm without priority Queue :

Initializations: all are O(V).


There are V iterations of the while-loop.
Finding the lowest-priority: O(V) (scan of priority array).
Scan of neighbors: O(V)
Total work in while-loop: O(V2).
Total time: O(V2).

Reference :
Introduction to algorithm by Cormen

Vous aimerez peut-être aussi