|
| 1 | +"""Implementation of a sorted map using a Binary search tree""" |
| 2 | +from TreesChpt import LinkedBinaryTree |
| 3 | +from Maps_HashTables_SkipLists import MapBase |
| 4 | + |
| 5 | +class TreeMap(LinkedBinaryTree, MapBase): #Multiple inheritance |
| 6 | + |
| 7 | + class Position(LinkedBinaryTree.Position): #Inherits inner class of LinkedBinaryTree class |
| 8 | + def key(self): |
| 9 | + """return key of map's (key-value) pair""" |
| 10 | + return self.element()._key |
| 11 | + |
| 12 | + def value(self): |
| 13 | + """return value of map's (key-value) pair""" |
| 14 | + return self.element()._value |
| 15 | + |
| 16 | + # Private/ protected utilities |
| 17 | + def _subtree_search(self, p, k): |
| 18 | + """Return position of p's subtree having key k or last node searched""" |
| 19 | + |
| 20 | + if k == p.key(): |
| 21 | + return p |
| 22 | + elif k < p.key(): |
| 23 | + if self.left(p) is not None: |
| 24 | + return self._subtree_search(self.left(p), k) #recursively search left subtree |
| 25 | + else: |
| 26 | + if self.right(p) is not None: |
| 27 | + return self._subtree_search(self.right(p), k) #recursively search rigth subtree |
| 28 | + |
| 29 | + return p # last position searched from recursion if search not a success |
| 30 | + |
| 31 | + def _subtree_first_position(self, p): |
| 32 | + """Return Position of first item in subtree rooted at p |
| 33 | + position with the least key from (sub)tree rooted at p, which is leftmost node (item) in Tree """ |
| 34 | + walk = p |
| 35 | + while self.left(walk) is not None: |
| 36 | + walk= self.left(walk) |
| 37 | + |
| 38 | + return walk |
| 39 | + |
| 40 | + def _subtree_last_position(self, p): |
| 41 | + """Return position of last item in (sub)tree rooted at p |
| 42 | + which is the right most item in the (sub)tree, since is |
| 43 | + the position with largest key """ |
| 44 | + |
| 45 | + walk = p |
| 46 | + while self.right(walk) is not None: |
| 47 | + walk = self.right(walk) |
| 48 | + |
| 49 | + return walk |
| 50 | + |
| 51 | + def first(self): |
| 52 | + """Return the first position in the Tree or None if Tree is empty""" |
| 53 | + |
| 54 | + return self._subtree_first_position(self.root()) if len(self) > 0 else None |
| 55 | + |
| 56 | + def last(self): |
| 57 | + """Return the last position in the Tree or None if Tree is empty""" |
| 58 | + return self._subtree_last_position(self.root()) if len(self) > 0 else None |
| 59 | + |
| 60 | + def before(self, p): |
| 61 | + """Return the Position just before p in natural order |
| 62 | + return None if p does not have children (first position on subtree)""" |
| 63 | + |
| 64 | + self._validate(p) # inherited from LinkedBinaryTree, to validate if position p exists in Tree |
| 65 | + |
| 66 | + if self.left(p): |
| 67 | + # walk through left subtree of p until it recursively finds greatest key strictly less than p |
| 68 | + # since last key on left subtree of p have key with value right before key at p |
| 69 | + return self._subtree_last_position(self.left(p)) |
| 70 | + else: |
| 71 | + #else walk upwards as the last key with no parent is the greatest key less than key at position p |
| 72 | + walk = p |
| 73 | + above = self.parent(walk) |
| 74 | + while above is not None and walk == self.left(above): |
| 75 | + walk = above |
| 76 | + above =self.parent(walk) |
| 77 | + |
| 78 | + return above |
| 79 | + |
| 80 | + def after(self, p): |
| 81 | + """Return the position just after p in the natural order |
| 82 | + |
| 83 | + Symmetric to before(p)""" |
| 84 | + |
| 85 | + self._validate(p) |
| 86 | + |
| 87 | + if self.right(p): |
| 88 | + return self._subtree_first_position(self.right(p)) |
| 89 | + else: |
| 90 | + walk= p |
| 91 | + above = self.parent(walk) |
| 92 | + |
| 93 | + while above is not None and walk == self.right(above): |
| 94 | + walk = above |
| 95 | + above = self.parent(walk) |
| 96 | + |
| 97 | + return above |
| 98 | + |
| 99 | + def find_position(self, k): |
| 100 | + """Return position with key k, or self neighbor or None if empty""" |
| 101 | + |
| 102 | + if self.is_empty(): |
| 103 | + return None |
| 104 | + else: |
| 105 | + p = self._subtree_search(self.root(), k) |
| 106 | + self._rebalance_access(p) # hook for balanced tree subclasses |
| 107 | + return p |
| 108 | + |
| 109 | + def find_min(self): |
| 110 | + """Return (k,v) pair with the minimum key is map""" |
| 111 | + |
| 112 | + if self.is_empty(): |
| 113 | + return None |
| 114 | + else: |
| 115 | + p = self.first() |
| 116 | + return (p.key, p.value) |
| 117 | + |
| 118 | + def find_ge(self, k): |
| 119 | + """Return (k,v) pair with least key greater than or equal to k |
| 120 | + Return None if there is not such key""" |
| 121 | + |
| 122 | + if self.is_empty(): |
| 123 | + return None |
| 124 | + else: |
| 125 | + p = self.find_position(k) |
| 126 | + if p.key() < k: |
| 127 | + p = self.after(p) |
| 128 | + return (p.key(), p.value()) if p is not None else None |
| 129 | + |
| 130 | + def find_range(self, start, stop): |
| 131 | + """iterate through all items such that start <= key < stop |
| 132 | + if start is None, begin iteration with min key of map |
| 133 | + if stop is None, iterate until last key (max key in map)""" |
| 134 | + |
| 135 | + if not self.is_empty(): |
| 136 | + if start is None: |
| 137 | + p = self.first |
| 138 | + else: |
| 139 | + p = self.find_position(start) |
| 140 | + if p.key() < start: |
| 141 | + p = self.after(p) |
| 142 | + |
| 143 | + while p is not None and (stop is None or p.key() < stop): |
| 144 | + yield (p.key(), p.value()) |
| 145 | + p = self.after(p) |
| 146 | + |
| 147 | + def __getitem__(self, k): |
| 148 | + """Return value associated with key k |
| 149 | + if tree is empty raise error """ |
| 150 | + |
| 151 | + if self.is_empty(): |
| 152 | + raise KeyError("Key Error: "+ repr(k)) |
| 153 | + else: |
| 154 | + p = self._subtree_search(self.root(), k) |
| 155 | + self._rebalance_access(p) # hook for balanced tree subclasses |
| 156 | + if k != p.key(): |
| 157 | + raise KeyError("Key Error" + repr(k)) |
| 158 | + return p.value() |
| 159 | + |
| 160 | + def __setitem__(self, k, v): |
| 161 | + """assign value v to key k if it exists, else make position """ |
| 162 | + |
| 163 | + # if tree is empty add pair as root of the tree |
| 164 | + if self.is_empty(): |
| 165 | + leaf= self._add_root(self._Item(k,v)) |
| 166 | + else: |
| 167 | + p = self._subtree_search(self.root(), k) |
| 168 | + |
| 169 | + if k == p.key(): |
| 170 | + p.element()._value = v |
| 171 | + self._rebalance_access(p) |
| 172 | + return |
| 173 | + else: |
| 174 | + item = self._Item(k,v) |
| 175 | + if p.key() < k: |
| 176 | + leaf= self._add_right(p, item) |
| 177 | + else: |
| 178 | + leaf = self._add_left(p, item) |
| 179 | + |
| 180 | + self._rebalance_insert(leaf) |
| 181 | + |
| 182 | + def __iter__(self): |
| 183 | + """Genrate iteration of all keys in the map in order """ |
| 184 | + p = self.first() |
| 185 | + while p is not None: |
| 186 | + yield p.key() |
| 187 | + p= self.after(p) |
| 188 | + |
| 189 | + def delete(self, p): |
| 190 | + """Remove item at position p""" |
| 191 | + |
| 192 | + self._validate(p) |
| 193 | + |
| 194 | + if self.left(p) and self.right(p): |
| 195 | + replacement = self._subtree_last_position(p) |
| 196 | + self._replace(p, replacement.element()) # replaces Item at p with replacement item |
| 197 | + p = replacement |
| 198 | + parent = self.parent(p) |
| 199 | + self._delete(p) |
| 200 | + self._rebalance_delete(parent) #if root deleted than parent is None, so we rebalance the tree |
| 201 | + |
| 202 | + def __delitem__(self, k): |
| 203 | + """Remove item associated with key k""" |
| 204 | + if not self.is_empty(): |
| 205 | + p = self._subtree_search(self.root(), k) |
| 206 | + if k == p.key(): |
| 207 | + self.delete(p) |
| 208 | + return |
| 209 | + self._rebalance_access(p) |
| 210 | + |
| 211 | + raise KeyError("Key Error: "+ repr(k)) |
0 commit comments