|  | 
|  | 1 | +# Copyright 2013, Michael H. Goldwasser | 
|  | 2 | +# | 
|  | 3 | +# Developed for use with the book: | 
|  | 4 | +# | 
|  | 5 | +#    Data Structures and Algorithms in Python | 
|  | 6 | +#    Michael T. Goodrich, Roberto Tamassia, and Michael H. Goldwasser | 
|  | 7 | +#    John Wiley & Sons, 2013 | 
|  | 8 | +# | 
|  | 9 | +# This program is free software: you can redistribute it and/or modify | 
|  | 10 | +# it under the terms of the GNU General Public License as published by | 
|  | 11 | +# the Free Software Foundation, either version 3 of the License, or | 
|  | 12 | +# (at your option) any later version. | 
|  | 13 | +# | 
|  | 14 | +# This program is distributed in the hope that it will be useful, | 
|  | 15 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 16 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 17 | +# GNU General Public License for more details. | 
|  | 18 | +# | 
|  | 19 | +# You should have received a copy of the GNU General Public License | 
|  | 20 | +# along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 21 | + | 
|  | 22 | +class EulerTour: | 
|  | 23 | +  """Abstract base class for performing Euler tour of a tree. | 
|  | 24 | +
 | 
|  | 25 | +  _hook_previsit and _hook_postvisit may be overridden by subclasses. | 
|  | 26 | +  """ | 
|  | 27 | +  def __init__(self, tree): | 
|  | 28 | +    """Prepare an Euler tour template for given tree.""" | 
|  | 29 | +    self._tree = tree | 
|  | 30 | + | 
|  | 31 | +  def tree(self): | 
|  | 32 | +    """Return reference to the tree being traversed.""" | 
|  | 33 | +    return self._tree | 
|  | 34 | + | 
|  | 35 | +  def execute(self): | 
|  | 36 | +    """Perform the tour and return any result from post visit of root.""" | 
|  | 37 | +    if len(self._tree) > 0: | 
|  | 38 | +      return self._tour(self._tree.root(), 0, [])    # start the recursion | 
|  | 39 | + | 
|  | 40 | +  def _tour(self, p, d, path): | 
|  | 41 | +    """Perform tour of subtree rooted at Position p. | 
|  | 42 | +
 | 
|  | 43 | +    p        Position of current node being visited | 
|  | 44 | +    d        depth of p in the tree | 
|  | 45 | +    path     list of indices of children on path from root to p | 
|  | 46 | +    """ | 
|  | 47 | +    self._hook_previsit(p, d, path)                       # "pre visit" p | 
|  | 48 | +    results = [] | 
|  | 49 | +    path.append(0)          # add new index to end of path before recursion | 
|  | 50 | +    for c in self._tree.children(p): | 
|  | 51 | +      results.append(self._tour(c, d+1, path))  # recur on child's subtree | 
|  | 52 | +      path[-1] += 1         # increment index | 
|  | 53 | +    path.pop()              # remove extraneous index from end of path | 
|  | 54 | +    answer = self._hook_postvisit(p, d, path, results)    # "post visit" p | 
|  | 55 | +    return answer | 
|  | 56 | + | 
|  | 57 | +  def _hook_previsit(self, p, d, path): | 
|  | 58 | +    """Visit Position p, before the tour of its children. | 
|  | 59 | +
 | 
|  | 60 | +    p        Position of current position being visited | 
|  | 61 | +    d        depth of p in the tree | 
|  | 62 | +    path     list of indices of children on path from root to p | 
|  | 63 | +    """ | 
|  | 64 | +    pass | 
|  | 65 | + | 
|  | 66 | +  def _hook_postvisit(self, p, d, path, results): | 
|  | 67 | +    """Visit Position p, after the tour of its children. | 
|  | 68 | +
 | 
|  | 69 | +    p        Position of current position being visited | 
|  | 70 | +    d        depth of p in the tree | 
|  | 71 | +    path     list of indices of children on path from root to p | 
|  | 72 | +    results  is a list of values returned by _hook_postvisit(c) | 
|  | 73 | +            for each child c of p. | 
|  | 74 | +    """ | 
|  | 75 | +    pass | 
|  | 76 | + | 
|  | 77 | +class PreorderPrintIndentedTour(EulerTour): | 
|  | 78 | +  def _hook_previsit(self, p, d, path): | 
|  | 79 | +   print(2*d*' ' + str(p.element())) | 
|  | 80 | + | 
|  | 81 | +class PreorderPrintIndentedLabeledTour(EulerTour): | 
|  | 82 | +  def _hook_previsit(self, p, d, path): | 
|  | 83 | +    label = '.'.join(str(j+1) for j in path)    # labels are one-indexed | 
|  | 84 | +    print(2*d*' ' + label, p.element()) | 
|  | 85 | + | 
|  | 86 | +class ParenthesizeTour(EulerTour): | 
|  | 87 | +  def _hook_previsit(self, p, d, path): | 
|  | 88 | +    if path and path[-1] > 0:                  # p follows a sibling | 
|  | 89 | +      print(', ', end='')                      # so preface with comma | 
|  | 90 | +    print(p.element(), end='')                 # then print element | 
|  | 91 | +    if not self.tree().is_leaf(p):             # if p has children | 
|  | 92 | +      print(' (', end='')                      # print opening parenthesis | 
|  | 93 | + | 
|  | 94 | +  def _hook_postvisit(self, p, d, path, results): | 
|  | 95 | +    if not self.tree().is_leaf(p):             # if p has children | 
|  | 96 | +      print(')', end='')                       # print closing parenthesis | 
|  | 97 | + | 
|  | 98 | +class DiskSpaceTour(EulerTour): | 
|  | 99 | +  def _hook_postvisit(self, p, d, path, results): | 
|  | 100 | +    # we simply add space associated with p to that of its subtrees | 
|  | 101 | +    return p.element().space() + sum(results)    | 
|  | 102 | + | 
|  | 103 | +class BinaryEulerTour(EulerTour): | 
|  | 104 | +  """Abstract base class for performing Euler tour of a binary tree. | 
|  | 105 | +
 | 
|  | 106 | +  This version includes an additional _hook_invisit that is called after the tour | 
|  | 107 | +  of the left subtree (if any), yet before the tour of the right subtree (if any). | 
|  | 108 | +
 | 
|  | 109 | +  Note: Right child is always assigned index 1 in path, even if no left sibling. | 
|  | 110 | +  """ | 
|  | 111 | +  def _tour(self, p, d, path): | 
|  | 112 | +    results = [None, None]          # will update with results of recursions | 
|  | 113 | +    self._hook_previsit(p, d, path)                  # "pre visit" for p | 
|  | 114 | +    if self._tree.left(p) is not None:               # consider left child | 
|  | 115 | +      path.append(0) | 
|  | 116 | +      results[0] = self._tour(self._tree.left(p), d+1, path) | 
|  | 117 | +      path.pop() | 
|  | 118 | +    self._hook_invisit(p, d, path)                   # "in visit" for p | 
|  | 119 | +    if self._tree.right(p) is not None:              # consider right child | 
|  | 120 | +      path.append(1) | 
|  | 121 | +      results[1] = self._tour(self._tree.right(p), d+1, path) | 
|  | 122 | +      path.pop() | 
|  | 123 | +    answer = self._hook_postvisit(p, d, path, results)    # "post visit" p | 
|  | 124 | +    return answer | 
|  | 125 | + | 
|  | 126 | +  def _hook_invisit(self, p, d, path): | 
|  | 127 | +    """Visit Position p, between tour of its left and right subtrees.""" | 
|  | 128 | +    pass | 
|  | 129 | + | 
|  | 130 | +class BinaryLayout(BinaryEulerTour): | 
|  | 131 | +  """Class for computing (x,y) coordinates for each node of a binary tree.""" | 
|  | 132 | +  def __init__(self, tree): | 
|  | 133 | +    super().__init__(tree)           # must call the parent constructor | 
|  | 134 | +    self._count = 0                  # initialize count of processed nodes | 
|  | 135 | + | 
|  | 136 | +  def _hook_invisit(self, p, d, path): | 
|  | 137 | +    p.element().setX(self._count)    # x-coordinate serialized by count | 
|  | 138 | +    p.element().setY(d)              # y-coordinate is depth | 
|  | 139 | +    self._count += 1                 # advance count of processed nodes | 
0 commit comments