views:

631

answers:

3

I need to find a path or paths down a complicated graph structure. The graph is built using something similar to this:

class Node
{
    public string Value { get; set;}
    public List<Node> Nodes { get; set;}

    public Node()
    {
        Nodes = new List<Node>();
    }
}

What makes this complicated is that the nodes can reference back to an earlier node. For example,

A -> C -> E -> A

What I need to do is get a list of stacks which represent paths through the Nodes until I get to a Node with a specific value. Since its possible there can be some very large paths available we can have a maximum Nodes to try.

List<Stack<Node>> paths = FindPaths(string ValueToFind, int MaxNumberNodes);

Does anyone have a way to build this (or something similar)? I've done recursion in the past but I'm having a total brain fart thinking about this for some reason. My question specified a lambda expression but using a lambda is not necessarily required. I'd be grateful for any solution.

Side note: I lifted the class from aku's excellent answer for this recursion question. While his elegant solution shown below traverses the tree structure it doesn't seem to allow enough flexibility to do what I need (for example, dismiss paths that are circular and track paths that are successful).

Action<Node> traverse = null;
traverse = (n) => { Console.WriteLine(n.Value); n.Nodes.ForEach(traverse);};
traverse(root);  // where root is the tree structure

Edit:

Based on input from the comments and answers below I found an excellent solution over in CodeProject. It uses the A* path finding algorithm. Here is the link.

A: 

I don't know exactly what you want to achieve, but this circular reference problem is usually solved by tagging already visited nodes. Just use a Dictionnary to keep track of the nodes which have already been visited so that you don't loop.

Example :

  public void ExploreGraph(TreeNode tn, Dictionary<TreeNode, bool> visitednodes)
        {

            foreach (Treenode childnode in tn.Nodes)
            {
                if (!visitedNodes.ContainsKey(childnode))
                {
                    visitednodes.Add(childnode);
                    ExploreGraph(childnode, visitednodes);
                }
            }
        }
Brann
why static? isn't it simpler to put all state on the stack? programs with this quality are generally more likely to be thread-safe as well.
Drew Noakes
Yes, you're definitely right. (I added an example without using anything static). Log of graph visiting algorithms are teached using statics for the sake of readability.
Brann
Also, there's a minor performance overhead when you pass the dictionary as an argument. Still, it's probably totally unnoticeable.
Brann
What I was trying to achieve is to provide some AI for a game. The object needs to move towards a goal but there are dozens of possible routes. I want to get all the successful routes and then have it take the shortest, or possibly another route based on other factors.
Sailing Judo
that's call pathfinding. I posted an answer below with references to common pathfinding algorithm which you should read to get you started. Good luck !
Brann
+3  A: 

If you're issue is related to Pathfinding, you may want to google for "A star" or "A*". Its a common and efficient pathfinding algorithm. See this article for an example directly related to your problem.

You may also want to look at the Dijsktra Algorithm

Brann
+1  A: 

I'm not sure whether your intended output is all paths to the goal, the best path to the goal (by some metric, e.g. path length), or just any path to the goal.

Assuming the latter, I'd start with the recursive strategy, including tracking of visited nodes as outlined by Brann, and make these changes:

  1. Add parameters to represent the goal being sought, the collection of successful paths, and the current path from the start.

  2. When entering a node that matches the goal, add the current path (plus the current node) to the list of successful paths.

  3. Extend the current path with the current node to create the path passed on any recursive calls.

  4. Invoke the initial ExploreGraph call with an empty path and an empty list of successful paths.

Upon completion, your algorithm will have traversed the entire graph, and distinct paths to the goal will have been captured.

That's just a quick sketch, but you should be able to flesh it out for your specific needs.

joel.neely