Retrieving Hierarchically Recursive Data Iteratively

Many objects are recursive hierarchies (you can say, like the root system of a tree where roots have roots). The directories of a file system are recursive hierarchies since a directory can contain one or more subdirectories that can also contain one or more subdirectories. Windows of a GUI are recursive hierarchies since a window can contain client windows or controls (controls are windows) that can contain controls or other windows. Engineering and manufacturing parts lists (such as for refrigerators and spacecraft) are recursive hierarchies. Organizational structures (employees) are recursive hierarchies.

Recursively hierarchical data can be retrieved and processed recursively. For each parent item, such as a directory or a window, the function processing the structure can call itself for each child. It is very easy to design a way to retrieve hierarchical data recursively.

I have always wanted to process hierarchical data iteratively instead of recursively. Now that I have done it, I am surprised how easy it is. I won't explain the advantages and disadvantages of either. The purpose of this article is to explain how to iteratively process recursively hierarchical data and provide an example.

When processing data recursively, recursion uses a stack. Each time a function is called (recursively or not), the data for the calling function is pushed onto the stack. For a recursive call, part of that data is usually the parent item, such as the directory or window.

The trick to processing data iteratively (not recursively) is to simply use a ("to do") stack for the data that has not yet been processed but that needs to be processed. During the processing of a parent item, the children (that are then to be done) are stacked (using a Last-In-First-Out (LIFO) collection). The .Net Stack class can be used for that. Objects are pushed onto the stack and popped off the stack until all data is processed. The data is processed in reverse, in the sense that if "1", 2"", "3" and "4" are pushed onto the stack in that order then they are processed as "4", "3", "2" and "1".

See Processing Folders Iteratively for a sample of  processing folders iteratively.

Let us use the following data for the sample here.

  • Top
    • First
      • First-1
        • John
        • Mike
        • Steve
      • First-2
        • Susan
        • Julie
        • Kara
      • First-3
        • Kim
        • Kelly
        • Pat
    • Second
      • Second-1  
      • Second-2  
      • Second-3 

When processing the data iteratively, the stack would be as follows, one line per iteration:

  1. Top  
  2. First Second   
  3. First-1 First-2 First-3 Second   
  4. John Mike Steve First-2 First-3 Second   
  5. Mike Steve First-2 First-3 Second   
  6. Steve First-2 First-3 Second   
  7. First-2 First-3 Second   
  8. Susan Julie Kara First-3 Second
  9. Julie Kara First-3 Second
  10. Kara First-3 Second
  11. First-3 Second
  12. Kim Kelly Pat Second
  13. Kelly Pat Second
  14. Pat Second
  15. Second
  16. Second-1 Second-2 Second-3
  17. Second-2 Second-3
  18. Second-3

In other words, we begin by pushing "Top" onto the stack. We then iterate (loop) by popping that off the stack and processing it. Processing includes pushing the children onto the stack. So the iteration continues until there are no more children to be processed; in other words, there is nothing more to process. Note that, as each item is processed, it is effectively replaced by its children, if any.

The sample program processes items of a class called "Hierarchy" that consists of:

  1. class Hierarchy  
  2. {  
  3.     public string Name;  
  4.     public List<Hierarchy> Children = new List<Hierarchy>();  
  6.     public Hierarchy Parent = null;  
  7.         public Hierarchy(string Name, Hierarchy Parent)  
  8.     {  
  9.         this.Name = Name;  
  10.         this.Parent = Parent;  
  11.     }  
  12. }   
The stack of items to be processed could then be: 
  1. Stack<Hierarchy> ToDo = new Stack<Hierarchy>();   
The data could be processed iteratively using: 
  1. ToDo.Push(Top);  
  2. while (ToDo.Count > 0)  
  3. {  
  4.     Hierarchy h = ToDo.Pop();  
  5.     Put(h, ToDo);  
  6. }  
Where the following is the "Put" method: 
  1. private void Put(Hierarchy h, Stack<Hierarchy> ToDo)  
  2. {  
  3.     string tabs = new string('\t', Level);  
  4.     Console.WriteLine("{0}{1}", tabs, h.Name);  
  5.     // store the children in the to do stack  
  6.     if (h.Children == null)  
  7.         return;  
  8.     // since the stack is LIFO, the objects will be popped off in  
  9.     // reverse order from what they are pushed, so we will push  
  10.     // in reverse order   
  11.     for (int i = h.Children.Count - 1; i >= 0; --i)  
  12.         ToDo.Push(h.Children[i]);  
  13. }  
See the sample code to see how "Level" is determined.

The following is an equivalent method for processing the data recursively:

  1. private static void PutRecursively(Hierarchy h)  
  2. {  
  3.     string tabs = new string('\t', Level);  
  4.     Console.WriteLine("{0}{1}", tabs, h.Name);  
  5.     // store the children in the to do stack  
  6.     if (h.Children == null)  
  7.         return;  
  8.     foreach (Hierarchy child in h.Children)  
  9.         PutRecursively(child);  
  10. }