Skip to content

Commit 512b747

Browse files
committed
source code for chapter 10
1 parent e584c0a commit 512b747

File tree

10 files changed

+629
-0
lines changed

10 files changed

+629
-0
lines changed

ch10/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__all__ = ['chain_hash_map', 'cost_performance', 'multi_map', 'probe_hash_map', 'sorted_table_map', 'unsorted_table_map']

ch10/chain_hash_map.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
from .hash_map_base import HashMapBase
23+
from .unsorted_table_map import UnsortedTableMap
24+
25+
class ChainHashMap(HashMapBase):
26+
"""Hash map implemented with separate chaining for collision resolution."""
27+
28+
def _bucket_getitem(self, j, k):
29+
bucket = self._table[j]
30+
if bucket is None:
31+
raise KeyError('Key Error: ' + repr(k)) # no match found
32+
return bucket[k] # may raise KeyError
33+
34+
def _bucket_setitem(self, j, k, v):
35+
if self._table[j] is None:
36+
self._table[j] = UnsortedTableMap() # bucket is new to the table
37+
oldsize = len(self._table[j])
38+
self._table[j][k] = v
39+
if len(self._table[j]) > oldsize: # key was new to the table
40+
self._n += 1 # increase overall map size
41+
42+
def _bucket_delitem(self, j, k):
43+
bucket = self._table[j]
44+
if bucket is None:
45+
raise KeyError('Key Error: ' + repr(k)) # no match found
46+
del bucket[k] # may raise KeyError
47+
48+
def __iter__(self):
49+
for bucket in self._table:
50+
if bucket is not None: # a nonempty slot
51+
for key in bucket:
52+
yield key

ch10/cost_performance.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
from .sorted_table_map import SortedTableMap
23+
24+
class CostPerformanceDatabase:
25+
"""Maintain a database of maximal (cost,performance) pairs."""
26+
27+
def __init__(self):
28+
"""Create an empty database."""
29+
self._M = SortedTableMap() # or a more efficient sorted map
30+
31+
def best(self, c):
32+
"""Return (cost,performance) pair with largest cost not exceeding c.
33+
34+
Return None if there is no such pair.
35+
"""
36+
return self._M.find_le(c)
37+
38+
def add(self, c, p):
39+
"""Add new entry with cost c and performance p."""
40+
# determine if (c,p) is dominated by an existing pair
41+
other = self._M.find_le(c) # other is at least as cheap as c
42+
if other is not None and other[1] >= p: # if its performance is as good,
43+
return # (c,p) is dominated, so ignore
44+
self._M[c] = p # else, add (c,p) to database
45+
# and now remove any pairs that are dominated by (c,p)
46+
other = self._M.find_gt(c) # other more expensive than c
47+
while other is not None and other[1] <= p:
48+
del self._M[other[0]]
49+
other = self._M.find_gt(c)

ch10/hash_map_base.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
from .map_base import MapBase
23+
from collections import MutableMapping
24+
from random import randrange # used to pick MAD parameters
25+
26+
class HashMapBase(MapBase):
27+
"""Abstract base class for map using hash-table with MAD compression.
28+
29+
Keys must be hashable and non-None.
30+
"""
31+
32+
def __init__(self, cap=11, p=109345121):
33+
"""Create an empty hash-table map.
34+
35+
cap initial table size (default 11)
36+
p positive prime used for MAD (default 109345121)
37+
"""
38+
self._table = cap * [ None ]
39+
self._n = 0 # number of entries in the map
40+
self._prime = p # prime for MAD compression
41+
self._scale = 1 + randrange(p-1) # scale from 1 to p-1 for MAD
42+
self._shift = randrange(p) # shift from 0 to p-1 for MAD
43+
44+
def _hash_function(self, k):
45+
return (hash(k)*self._scale + self._shift) % self._prime % len(self._table)
46+
47+
def __len__(self):
48+
return self._n
49+
50+
def __getitem__(self, k):
51+
j = self._hash_function(k)
52+
return self._bucket_getitem(j, k) # may raise KeyError
53+
54+
def __setitem__(self, k, v):
55+
j = self._hash_function(k)
56+
self._bucket_setitem(j, k, v) # subroutine maintains self._n
57+
if self._n > len(self._table) // 2: # keep load factor <= 0.5
58+
self._resize(2 * len(self._table) - 1) # number 2^x - 1 is often prime
59+
60+
def __delitem__(self, k):
61+
j = self._hash_function(k)
62+
self._bucket_delitem(j, k) # may raise KeyError
63+
self._n -= 1
64+
65+
def _resize(self, c):
66+
"""Resize bucket array to capacity c and rehash all items."""
67+
old = list(self.items()) # use iteration to record existing items
68+
self._table = c * [None] # then reset table to desired capacity
69+
self._n = 0 # n recomputed during subsequent adds
70+
for (k,v) in old:
71+
self[k] = v # reinsert old key-value pair

ch10/map_base.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
from collections import MutableMapping
23+
24+
class MapBase(MutableMapping):
25+
"""Our own abstract base class that includes a nonpublic _Item class."""
26+
27+
#------------------------------- nested _Item class -------------------------------
28+
class _Item:
29+
"""Lightweight composite to store key-value pairs as map items."""
30+
__slots__ = '_key', '_value'
31+
32+
def __init__(self, k, v):
33+
self._key = k
34+
self._value = v
35+
36+
def __eq__(self, other):
37+
return self._key == other._key # compare items based on their keys
38+
39+
def __ne__(self, other):
40+
return not (self == other) # opposite of __eq__
41+
42+
def __lt__(self, other):
43+
return self._key < other._key # compare items based on their keys

ch10/multi_map.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 MultiMap:
23+
"""
24+
A multimap class built upon use of an underlying map for storage.
25+
26+
This uses dict for default storage.
27+
28+
Subclasses can override class variable _MapType to change the default.
29+
That catalog class must have a default constructor that produces an empty map.
30+
As an example, one might define the following subclass to use a SortedTableMap
31+
32+
class SortedTableMultimap(MultiMap):
33+
_MapType = SortedTableMap
34+
"""
35+
_MapType = dict # Map type; can be redefined by subclass
36+
37+
def __init__(self):
38+
"""Create a new empty multimap instance."""
39+
self._map = self._MapType() # create map instance for storage
40+
self._n = 0
41+
42+
def __len__(self):
43+
"""Return number of (k,v) pairs in multimap."""
44+
return self._n
45+
46+
def __iter__(self):
47+
"""Iterate through all (k,v) pairs in multimap."""
48+
for k,secondary in self._map.items():
49+
for v in secondary:
50+
yield (k,v)
51+
52+
def add(self, k, v):
53+
"""Add pair (k,v) to multimap."""
54+
container = self._map.setdefault(k, []) # create empty list, if needed
55+
container.append(v)
56+
self._n += 1
57+
58+
def pop(self, k):
59+
"""Remove and return arbitrary (k,v) pair with key k (or raise KeyError)."""
60+
secondary = self._map[k] # may raise KeyError
61+
v = secondary.pop()
62+
if len(secondary) == 0:
63+
del self._map[k] # no pairs left
64+
self._n -= 1
65+
return (k, v)
66+
67+
def find(self, k):
68+
"""Return arbitrary (k,v) pair with given key (or raise KeyError)."""
69+
secondary = self._map[k] # may raise KeyError
70+
return (k, secondary[0])
71+
72+
def find_all(self, k):
73+
"""Generate iteration of all (k,v) pairs with given key."""
74+
secondary = self._map.get(k, []) # empty list, by default
75+
for v in secondary:
76+
yield (k,v)

ch10/probe_hash_map.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
from .hash_map_base import HashMapBase
23+
24+
class ProbeHashMap(HashMapBase):
25+
"""Hash map implemented with linear probing for collision resolution."""
26+
_AVAIL = object() # sentinal marks locations of previous deletions
27+
28+
def _is_available(self, j):
29+
"""Return True if index j is available in table."""
30+
return self._table[j] is None or self._table[j] is ProbeHashMap._AVAIL
31+
32+
def _find_slot(self, j, k):
33+
"""Search for key k in bucket at index j.
34+
35+
Return (success, index) tuple, described as follows:
36+
If match was found, success is True and index denotes its location.
37+
If no match found, success is False and index denotes first available slot.
38+
"""
39+
firstAvail = None
40+
while True:
41+
if self._is_available(j):
42+
if firstAvail is None:
43+
firstAvail = j # mark this as first avail
44+
if self._table[j] is None:
45+
return (False, firstAvail) # search has failed
46+
elif k == self._table[j]._key:
47+
return (True, j) # found a match
48+
j = (j + 1) % len(self._table) # keep looking (cyclically)
49+
50+
def _bucket_getitem(self, j, k):
51+
found, s = self._find_slot(j, k)
52+
if not found:
53+
raise KeyError('Key Error: ' + repr(k)) # no match found
54+
return self._table[s]._value
55+
56+
def _bucket_setitem(self, j, k, v):
57+
found, s = self._find_slot(j, k)
58+
if not found:
59+
self._table[s] = self._Item(k,v) # insert new item
60+
self._n += 1 # size has increased
61+
else:
62+
self._table[s]._value = v # overwrite existing
63+
64+
def _bucket_delitem(self, j, k):
65+
found, s = self._find_slot(j, k)
66+
if not found:
67+
raise KeyError('Key Error: ' + repr(k)) # no match found
68+
self._table[s] = ProbeHashMap._AVAIL # mark as vacated
69+
70+
def __iter__(self):
71+
for j in range(len(self._table)): # scan entire table
72+
if not self._is_available(j):
73+
yield self._table[j]._key

0 commit comments

Comments
 (0)