Instead of trying to solve your problem specifically for expression trees, let me describe for you some general methods for working with poorly managed trees.
You can start by reading my series of articles on solving your problem: how to determine the depth of a tree without using recursion?
http://blogs.msdn.com/b/ericlippert/archive/2005/07/27/recursion-part-one-recursive-data-structures-and-functions.aspx
These articles were written when I was working on JScript, so the examples are in JScript. It's not too hard to understand how to apply these concepts to C #.
Let me give you a small example of a toy in C # on how to do an operation on a recursive data structure without doing a full recursion. Suppose we have the following binary tree: (Assume WOLOG that binary tree nodes have either zero or two children, not one).
class Node { public Node Left { get; private set; } public Node Right { get; private set; } public string Value { get; private set; } public Node(string value) : this(null, null, value) {} public Node(Node left, Node right, string value) { this.Left = left; this.Right = right; this.Value = value; } } ... Node n1 = new Node("1"); Node n2 = new Node("2"); Node n3 = new Node("3"); Node n3 = new Node("4"); Node n5 = new Node("5"); Node p1 = new Node(n1, n2, "+"); Node p2 = new Node(p1, n3, "*"); Node p3 = new Node(n4, n5, "+"); Node p4 = new Node(p2, p3, "-");
So we have the p4 tree:
- / \ * + / \ / \ + 3 4 5 / \ 1 2
and we want to print p4 as an expression in brackets
(((1+2)*3)-(4+5))
The recursive solution is simple:
static void RecursiveToString(Node node, StringBuilder sb) { // Again, assuming either zero or two children. if (node.Left != null) sb.Append(node.Value); else { sb.Append("("); RecursiveToString(node.Left, sb); sb.Append(node.Value); RecursiveToString(node.Right, sb); sb.Append(")"); } }
Now suppose we know that the tree is probably βdeepβ on the left, but βshallowβ on the right. Is it possible to exclude recursion on the left?
static void RightRecursiveToString(Node node, StringBuilder sb) { // Again, assuming either zero or two children. var stack = new Stack<Node>(); stack.Push(node); while(stack.Peek().Left != null) { sb.Append("("); stack.Push(stack.Peek().Left); } while(stack.Count != 0) { Node current = stack.Pop(); sb.Append(current.Value); if (current.Right != null) RightRecursiveToString(current.Right, sb); sb.Append(")"); } } }
The review of the right only the version, of course, is much more difficult to read and much more difficult to reason, but it does not hit the glass.
Let's move on to our example.
push p4 push p2 output ( push p1 output ( push n1 output ( loop condition is met pop n1 output 1 go back to the top of the loop pop p1 output + recurse on n2 -- this outputs 2 output ) go back to the top of the loop pop p2 output * recurse on n3 -- this outputs 3 output ) go back to the top of the loop pop p4 output - recurse on p3 push p3 push n4 output ( loop condition is met pop n4 output 4 go back to the top of the loop pop p3 output + recurse on n5 -- this outputs 5 output ) loop condition is not met; return. output ) loop condition is not met, return.
And what do we infer? (((1+2)*3)-(4+5)) if required.
So, you saw here that I can go from two recursions to one. We can use similar methods to go from one recursion to one. To make this algorithm completely iterative - so that it does not repeat either on the left or on the right - is left as an exercise.
(And by the way: I ask a variant of this problem as an interview question, so if you ever intend to interview me, you now have an unfair advantage!)