Graph Algorithms
Post By kanra
Blogs Graph Algorithms

Depth First Search (DFS) – Travel

 

  • A traveral fnc that go through all nodes
  • each node visit Once
  • each edge examine Twice
  • Time: O (|Vertices| + |Edges|)

 

Code:

# go through all nodes
# using recursion
# - each node visit Once
# - each edge examine Twice
def DFS_Recursion(graph, start):
    seen = {start}
    for adjacent_vertex in graph[start]:
        if adjacent_vertex not in seen:
            DFS_Recursion(graph,adjacent_vertex)

 

 

Depth First Search (DFS) – Finding Target

 

Throughout these algorithms I’m using a structure (path length, previous element, vertex) to represent the path. This has the effect of making a liked list of vertices in reverse order. This lets me display the path after running the algorithm. The important part is that the first argument is the path length upto that point, and the last argument is the vertex we’re looking at.

 

Code:

# Using Iteration
# input graph: the graph (can be weighted or unweighted)
# input start: the source vertex
# input end: the target vertex
# output a path from start to end
# struct push into the stack is path = (path length, previous element, vertex) 
def DFS_Iteration(graph, start, end):
    # We need a stack for DFS, so we'll use pythons double ended queue
    stack = deque()
    stack.append((0, None, start))

    # keep track of all of the vertices we've seen (avoids cycles)
    seen = {start}

    # look at the top of the stack
    # loop through all of the neighbors, and add them to the stack
    # repeat until we find the target
    while len(stack) > 0:
        path = stack.pop()
        (pathLen, prevVer, ver) = path

        # we found the target so return
        if ver == end:
            return path
		
		# for every adjacent vertex u
        for adjVer in graph[ver]:
            # if we haven't seen the vertex, then add it to the stack
            # update total path length
            # add to the seen list			
            if adjVer not in seen:
                stack.append((pathLen + graph[ver][adjVer], path, adjVer))
                seen.add(adjVer)

    # well, we failed, Let's return a null pointer
    # that always ends well
    return None

 

 

Breadth First Search (BFS) – Travel

 

  • A traveral fnc that go through all nodes
  • each node visit Once
  • each edge examine Twice
  • Time: O (|Vertices| + |Edges|)

 

Code:

def BFS2(graph, start):
    # now we'll treat the deque like a queue
    queue = deque()
    queue.append(start)

    seen = {start}

    # exactly the same as DFS, but we get each element from the bottom of the deque
    # instead of the top
    while len(queue) > 0:
        current_vertex = queue.popleft()

        for adjacent_vertex in graph[current_vertex]:
            # again don't add a vertex we've already seen
            if adjacent_vertex not in seen:
                queue.append( adjacent_vertex )
                seen.add(adjacent_vertex)

    return seen


# Another bfs, visit vertex in 1st loop, slower than the one above
# Difference: not adding first vertex to visited before loop
#
def BFS3(graph, start):
    visited = []                    # breadth first order list of visited vertices
    queue   = [start]               # a queue of vertices that we are waiting to visit
    while len(queue)>0:             # so long as there are vertices to visit ...
        v = queue.pop(0)            # ... take a vertex from front of the queue
        visited.append(v)           # ... add it to the visited list
        for w in graph[v]:          # ... add its unvisited neighbors to end of queue
            if w not in visited and w not in queue:
                queue.append(w)
    return visited

 

 

Breadth First Search (BFS) – Finding Target

 

Input taking a start vertex and a end vertex, then find a path from start to the end.

 

Code:

# input graph: the graph (can be weighted or unweighted)
# input start: the source vertex
# input end: the target vertex
# output a path from start to end that is the shortest length
# 
def BFS(graph, start, end):
    # now we'll treat the deque like a queue
    queue = deque()
    queue.append((0, None, start))

    seen = {start}

    # exactly the same as DFS, but we get each element from the bottom of the deque
    # instead of the top
    while len(queue) > 0:
        current_path = queue.popleft()
        (pathLen, last_path, current_vertex) = current_path

        # we found the end, return
        if current_vertex == end:
            return current_path

        for adjacent_vertex in graph[current_vertex]:
            # again don't add a vertex we've already seen
            if adjacent_vertex not in seen:
                queue.append((pathLen + graph[current_vertex][adjacent_vertex], current_path, adjacent_vertex))
                seen.add(adjacent_vertex)

    return None

 

 

Minimum Spanning Tree

 

Using Prim’s algorithm to find the minimum spanning tree in a graph.
Time: O ( |V|^3 )

 

Prim’s algorithm:
Prim’s algorithm is a method for finding the minimum spanning tree.
we take a graph, and a start vertex.
The goal is to “Grow” the tree from the start vertex.
To do this, we look at the neighbors of the start vertex, and pick the shortest edge.
Then we look at all of the neighbors of those two vertices, and take the shortest edge.
We keep doing this until we have a spanning tree.
We need to make sure we never make a cycle.

 

Code:

# Prim's Algorithm
# 
# input graph: a weighted graph
# input start: the vertex to start the tree at
# output: the edges of a minimum spanning tree
def prim(graph, start):
    path = []
    vertex_count = 1
    visited = [start]
    while vertex_count < graph.order():
        least_edge = None
        # find a least edge
        for a_visited in visited:                           # loop every vetex in visited vertices
            for neighbor in graph[a_visited]:               # loop every neighbor for a vertex
                weight = graph[a_visited][neighbor]
                if (neighbor not in visited) and ((not least_edge) or (weight <least_edge[2])):
                    least_edge = (a_visited, neighbor, weight)
                    
        visited.append(least_edge[1])
        path.append(least_edge)
        vertex_count += 1
    
    return path

 

 

Finding Shortest Path

 

Using Dijkstra’s algorithm to find the shortest path from a start vertex to an end vertex in a graph.

 

Code:

# input graph: the graph (must be weighted)
# input start: the source vertex
# input end: the target vertex
# output a path from start to end that is the shortest length
# 
def dijkstra(graph, start, end):
    # if the graph isn't weighted, then use BFS, it's faster
    if not graph.weighted:
        return BFS(graph, start, end)

    # python has heappush and heappop
    # which will treat an array like a heap
    # so, we basically get a priority queue for free
    heap = []
    heappush(heap, (0, None, start))

    # now, not only do we need to know if we've seen a vertex, but we need
    # to know the shortest path from s to t
    seen = {}
    seen[start] = 0

    # just like before, we keep removing vertices from the heap
    # we keep going until we find the target vertex
    while len(heap) > 0:
        path = heappop(heap)
        (pathLen, t, v) = path

        # we hit the end, so return
        if v == end:
            return path

        # for neighbor of v we need to check if we've seen it,
        # or, if we have seen it, we need to check if
        # we've found a shorter path
        #
        # pathLen + graph[v][u] is the new path from s -> u
        # seen[u] is the old path from s -> u
        for u in graph[v]:
            if u not in seen or pathLen + graph[v][u] < seen[u]:
                heappush(heap, (pathLen + graph[v][u], path, u))
                seen[u] = pathLen + graph[v][u] 

    # well, we failed, null pointer time!
    return None

 



AUTHOR : kanra
EMAIL : karawakara@gmail.com
Working on Networking, Web Development, Software Development, Server-side deployment. Having fun on doing experiments on new technologies.