June 7, 2011, 1:18 p.m.

posted by bruce

## Graph SearchingMany problems can be represented as a graph, which is a set of states with transitions ("arcs") that lead from one state to another. For example, planning a route for a trip is really a graph search problem in disguisethe states are places you'd like to visit, and the arcs are the transportation links between them. This section presents simple Python programs that search through a directed, cyclic graph to find the paths between a start state and a goal. Graphs can be more general than trees because links may point at arbitrary nodeseven ones already searched (hence the word cyclic). The graph used to test searchers in this section is sketched in Figure. Arrows at the end of arcs indicate valid paths (e.g., A leads to B, E, and G). The search algorithms will traverse this graph in a depth-first fashion, and they will trap cycles in order to avoid looping. If you pretend that this is a map, where nodes represent cities and arcs represent roads, this example will probably seem a bit more meaningful. ## A directed graphThe first thing we need to do is choose a way to represent this graph in a Python script. One approach is to use built-in datatypes and searcher functions. The file in Figure builds the test graph as a simple dictionary: each state is a dictionary key, with a list of keys of nodes it leads to (i.e., its arcs). This file also defines a function that we'll use to run a few searches in the graph. ## PP3E\Dstruct\Classics\gtestfunc.py
Now, let's code two modules that implement the actual search algorithms. They are both independent of the graph to be searched (it is passed in as an argument). The first searcher, in Figure, uses recursion to walk through the graph. ## PP3E\Dstruct\Classics\gsearch1.py
The second searcher, in Figure, uses an explicit stack of paths to be expanded using the tuple-tree stack representation we explored earlier in this chapter. ## PP3E\Dstruct\Classics\gsearch2.py
To avoid cycles, both searchers keep track of nodes visited along a path. If an extension is already on the current path, it is a loop. The resulting solutions list is sorted by increasing lengths using the list C:\...\PP3E\Dstruct\Classics> This output shows lists of possible paths through the test graph; I added two line breaks to make it more readable. Notice that both searchers find the same paths in all tests, but the order in which they find those solutions may differ. The ## Moving Graphs to ClassesUsing dictionaries to represent graphs is efficient: connected nodes are located by a fast hashing operation. But depending on the application, other representations might make more sense. For instance, classes can be used to model nodes in a network too, much like the binary tree example earlier. As classes, nodes may contain content useful for more sophisticated searches. To illustrate, Figure shows an alternative coding for our graph searcher; its algorithm is closest to ## PP3E\Dstruct\Classics\graph.py
In this version, graphs are represented as a network of embedded class instance objects. Each node in the graph contains a list of the node objects it leads to ( To test, the module in Figure builds the test graph again, this time using linked instances of the ## PP3E\Dstruct\Classics\gtestobj1.py
Run this test by running the C:\...\PP3E\Dstruct\Classics> The results are the same as for the functions, but node name labels are not quoted: nodes on path lists here are ## PP3E\Dstruct\Classics\gtestobj2.py
This test finds three paths in its graph between nodes S and M. If you'd like to see more Python graph code, check out the files in the directory MoreGraphs in this book's examples distribution. These are roughly the same as the ones listed here, but they add user interaction as each solution is found. In addition, we've really only scratched the surface of this domain here; see other books for additional topics (e.g., breadth- and best-first search): C:\...\PP3E\Dstruct\Classics> |

- Comment