Skip to content

Commit de2e407

Browse files
committed
Create TreeMap.py
1 parent 3513e54 commit de2e407

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed

BinarySearchTrees/TreeMap.py

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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

Comments
 (0)