< Prev PageHomeNext Page >
 
1.0 Tree Searches

In this section we present some methods for searching trees and graphs.   All coding examples are in C#. 

Consider the following.  We have a tree data structure shown in Figure 1 with one or more nodes interconnected by undirected edges such that

(a) one node R is designated the root of the tree, and

(b) the remaining nodes excluding the root are partitioned into disjoint sets and each of these sets is a tree, and

(c) one node G is designated a Goal node.
Tree Nodes
Figure 1-1 Tree Data Structure

Our problem is to find the Goal node, if it exists, and determine a path from the Root to the Goal. Initially, we know nothing of the structure of the tree, whether the Goal node is present, or if the tree has a finite number of nodes.  Although our node numbering follows a discernible pattern here of left-to-right, top-to-bottom, we should not infer that higher node numbers are further right and further down.  The numbers could have been randomly generated. 

What is needed is a method for examining each node to determine if it is the Goal node.  We shall further specify that a node may be examined only once. 

An uninformed search proceeds blindly from the Root and stops either when the Goal node has been found or after each node has been inspected.  One method follows each leg in the structure, for instance R-1-2,  until it finds a node with no successors (Node 2), and continues the search beginning at the most recent junction (Node 1) where it examines 3-4 and then continues at 5-6-7-8.  On the next leg of the search beginning at Node 9, the Goal node is found and the search stops.  This is an example of a Depth First Search (DFS). 

Alternately we could examine each of the nearest neighbors of the Root, nodes 1, 5, and 9, and then examine each of their nearest neighbors, nodes 2, 6, 10 and 11 which are each two nodes distance from the Root and so forth.  The search ends in the third iteration where nodes 4-7-G-12 are visited.  The Nth iteration examines nodes at a distance N from the Root.  This is a Breadth First Search (BFS).

In both kinds of searches we started with a child node on the left.   The decision is arbitrary.  We could have started with a child on the right or one that is neither on  the left nor right.    

Our approach consists of three parts.  First, we define a node class and an abstract class for a tree structure, and then a specific implementation.

The class Node defined below has an integer value and a templated List collection of neighboring nodes both exposed as public properties, and a Boolean property Visited used for indicating that the node has been examined in a tree search.   Neighbor nodes are added as an array through the public method AddNodes( ).  The node's value is set in its constructor.

Listing 1-1 CLASS Node DEFINITION    (Show Code ...)

Listing 1-1 Node Class Definition
     public class Node
     {
         private int _nodeval;
         public List<Node> _neighbornodes;
         private bool _visited;
 
         public Node(int nodeval)
         {
             _nodeval = nodeval;
             _neighbornodes = new List<Node>();
         }
 
         // Methods
 
         public void AddNodes(Node[] neighbors)
         {
 
             for (int i = 0; i < neighbors.Length; i++)
                 this.NeighborNodes.Add(neighbors[i]);
         }
 
   
         // Properties
         public int NodeValue
         {
             get { return _nodeval; }
             set { _nodeval = value; }
         }
         public List<Node> NeighborNodes
         {
             get { return _neighbornodes; }
             set { _neighbornodes = value; }
         }
         public bool Visited
         {
             get { return _visited; }
             set { _visited = value; }
         }
 
     } // end class Node
 
 
To allocate a new node called myNode with value 2 and neighboring nodes node2 and node3 which are both assumed to exist, write


   Node myNode = new Node(2);
   myNode.AddNodes ( new Node[] {node2, node3} );



Search methods are incorporated into a tree-search class which together with the Node class allow for traversing a tree with prescribed structure.  We will study different methods for  performing searches and different ways for specifying the tree's structure.  In order to generalize this process, we first propose an abstract tree-search class, defined in the listing below for class AbsTreeSearch

While the class does not specify a search method (this may change as this project progresses), it specifies the signature for building an adjacency list and defines two important helper methods which we don't wish to repeat with every derived class.  The adjacency list defines the structure of the tree.  It consists of node creation and calls to the Node class method AddNodes( ).  The adjacency list, which is a list of nodes and their neighbors, occupies a templated collection, _nList.  It is created in the abstract class.  This and two other Node variables _root and _goal appear in all implementations of tree searches and are defined here with protected access level.  

The helper methods that are defined here (so that they don't have to be redfined in derived classes) are  ResetVisitedStates( ) and ShowNodes( ).  The first sets the Visited property of each node in the node list to false and should be run between searches.  The second displays the node list on the standard output which normally will be the client Console window.  Good programming practice would normally test nList to ensure that it exists (i.e., is not a null object) before iterating through its members.  However, nList has already been instantiated in the abstract class constructor making this step unnecessary.

Listing 1-2 ABSTRACT CLASS AbsTreeSearch DEFINITION    (Show Code ...)

Listing 1_2 AbstractTreeSearch Class Definition
     abstract public class AbsTreeSearch
     {
         protected List<Node> _nLst;
         protected Node _root;
         protected Node _goal; // Note: there may be more than one 
                               // target state, but only one may be 
                               // designated as a goal.
 
         public AbsTreeSearch() 
         {
             _nLst = new List<Node>();
 
         }
 
         abstract public void BuildAdjacencyList();
  
         public void ResetVisitedStates()
         {
             if (_nLst != null)
                 foreach (Node node in _nLst)
                     node.Visited = false;
         }
 
         public void ShowNodes()
         {
             for (int i = 0; i < _nLst.Count; i++)
             {
 
                 string decoration;
                 if (_nLst[i] == _root)
                     decoration = "(ROOT)";
                 else
                     if (_nLst[i] == _goal)
                         decoration = "(GOAL)";
                     else
                         decoration = "";
 
                 Console.WriteLine("Node {0}  {1} \n--------", i, decoration);
                 Console.Write("Neighbors: ");
                 int neighborcount = _nLst[i].NeighborNodes.Count;
                 if (neighborcount == 0)
                     Console.WriteLine("none \n");
                 else
                 {
                     for (int j = 0; j < neighborcount; j++)
                         if (j != neighborcount - 1)
                             Console.Write("{0}, ", 
                               _nLst[i].NeighborNodes[j].NodeValue.ToString());
                         else
                             Console.WriteLine("{0} \n", 
                               _nLst[i].NeighborNodes[j].NodeValue.ToString());
                 }
 
             }
         }
  
    }  // end class AbsTreeSearch
     
  
On the next page, we present a "simple" impelmentation of the TreeSearch class derived from the abstract class with implementations of search functions.  Two of these are Depth First Search (DFS) methods; one is recursive.  The third search function is a non-recursive Breadth First Search (BFS).  These functions are discussed on the next page.  Following this discussion, we will give some examples of the use of the search functions, and then turn our attention to heuristic methods that improve performance.

< Prev PageHomeNext Page >