traverse.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. """
  2. Tree traversal routines.
  3. Created by Sander Mathijs Van Veen <smvv@kompiler.org>, 2012.
  4. """
  5. from node import Leaf
  6. def traverse_depth_first(root):
  7. """
  8. Traverse the given root of a tree in depth-first order. This is an
  9. iterative instead of the ordinary recursive implementation. It uses
  10. iterators to reduce memory usage and, since it is iterative, the depth of
  11. the given tree does not hit Python's recursion limit.
  12. This implementation uses two stacks. One contains the path from the root
  13. node to the current visited node / leaf using child node indices. The other
  14. one contains pointers to the nodes which are not yet visited (aka still on
  15. the "to do" list). Thus, traversal using this implementation has a worst
  16. case space complexity of O(2 * log N), where N is the number of nodes in
  17. the tree.
  18. >>> from node import Node as N, Leaf as L
  19. >>> root = N('*', L(4), L('a'))
  20. >>> print map(lambda n: n.title(), traverse_depth_first(root))
  21. ['4', 'a', '*']
  22. >>> root = N('+', N('/', L(1), L(2)), N('*', L(3), L(4)))
  23. >>> print map(lambda n: n.title(), traverse_depth_first(root))
  24. ['1', '2', '/', '3', '4', '*', '+']
  25. >>> root = N('+', N('/', L(1), N('-', L(2), N('^', L(3), L(4)))), \
  26. N('*', L(5), L(6)))
  27. >>> print map(lambda n: n.title(), traverse_depth_first(root))
  28. ['1', '2', '3', '4', '^', '-', '/', '5', '6', '*', '+']
  29. """
  30. if not root:
  31. raise StopIteration()
  32. # List of nodes which are not yet visited (the "to do" list).
  33. stack = []
  34. # List of child node indices which indicates the path to the current
  35. # visited node or leaf.
  36. path = []
  37. node = root
  38. while True:
  39. while not node.is_leaf:
  40. # Traverse left and save the path
  41. stack.append(node)
  42. path.append(0)
  43. node = node[0]
  44. yield node
  45. if not stack:
  46. break
  47. while stack:
  48. # Set pointer to parent node and increase child node position.
  49. node = stack[-1]
  50. path[-1] += 1
  51. pos = path[-1]
  52. if pos >= len(node):
  53. # Visit the parent node, since all children are visited.
  54. yield node
  55. # Remove the node and move up in the tree.
  56. stack.pop()
  57. path.pop()
  58. if not stack:
  59. raise StopIteration()
  60. else:
  61. # This child node is not visited.
  62. node = node[pos]
  63. break
  64. raise StopIteration()
  65. if __name__ == '__main__':
  66. import doctest
  67. doctest.testmod()