Skip to content

Commit

Permalink
Merge pull request #60 from nbro/iss59
Browse files Browse the repository at this point in the history
Made size a property in all data structures
  • Loading branch information
nbro committed Mar 14, 2017
2 parents 3722560 + 68d68c3 commit f49e761
Show file tree
Hide file tree
Showing 17 changed files with 256 additions and 251 deletions.
57 changes: 29 additions & 28 deletions ands/ds/BST.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def has_two_children(self) -> bool:
def count(self) -> int:
"""Counts the numbers of nodes under `self` (including `self`)."""

def _count(u, c: int):
def _count(u, c: int) -> int:
if u is None:
return c
else:
Expand All @@ -185,7 +185,14 @@ def _count(u, c: int):
c = 0
return _count(self, c)

def __fields(self):
def __str__(self):
return "{" + str(self.key) + ": " + str(self.value) + "}"

def __repr__(self):
return self.__str__()
# return tabulate(self.__fields(), tablefmt="fancy_grid")

def __fields(self) -> list:
return [["key", self.key],
["value", self.value],
["parent", self.parent],
Expand All @@ -195,13 +202,6 @@ def __fields(self):
["grandparent", self.grandparent],
["uncle", self.uncle]]

def __str__(self):
return "{" + str(self.key) + ": " + str(self.value) + "}"

def __repr__(self):
return self.__str__()
# return tabulate(self.__fields(), tablefmt="fancy_grid")


class BST:
"""`BST` is a class that represents a classical binary search tree."""
Expand All @@ -223,6 +223,25 @@ def __init__(self, root=None):
self.root = root
assert is_bst(self)

@property
def size(self) -> int:
"""Returns the total number of nodes.
Time complexity: O(1)."""
assert is_bst(self)
if self.root is not None:
assert self.root.count() == self._n
else:
assert self._n == 0
return self._n

def is_empty(self) -> bool:
"""Returns true if this tree has 0 nodes.
Time complexity: O(1)."""
assert is_bst(self)
return self.size == 0

def _initialise_if_empty(self, u: BSTNode) -> None:
"""Sets `u` as the new root and unique node of this tree."""
assert self.root is None
Expand All @@ -242,24 +261,6 @@ def clear(self) -> None:
self._n = 0
assert is_bst(self)

def size(self) -> int:
"""Returns the total number of nodes.
Time complexity: O(1)."""
assert is_bst(self)
if self.root is not None:
assert self.root.count() == self._n
else:
assert self._n == 0
return self._n

def is_empty(self) -> bool:
"""Returns true if this tree has 0 nodes.
Time complexity: O(1)."""
assert is_bst(self)
return self.size() == 0

def is_root(self, u: BSTNode) -> bool:
"""Checks if `u` is the same object as `self.root`.
Expand Down Expand Up @@ -997,7 +998,7 @@ def insert(self, x: object, value=None) -> None:
This function does a pseudo-random insertion of keys."""
assert is_bst(self)
r = randint(0, self.size() * 3 // 8) # random operation for now!!
r = randint(0, self.size * 3 // 8) # random operation for now!!
if r == 0:
self.root_insert(x, value)
else:
Expand Down
13 changes: 7 additions & 6 deletions ands/ds/BinaryHeap.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(self, ls=None):
self.heap = [] if not isinstance(ls, list) else ls
self._build_heap()

@property
def size(self) -> int:
"""Returns the number of elements in this heap.
Expand All @@ -71,7 +72,7 @@ def is_empty(self) -> bool:
"""Returns true if this heap is empty, false otherwise.
Time complexity: O(1)."""
return self.size() == 0
return self.size == 0

def clear(self) -> None:
"""Removes all elements from this heap.
Expand All @@ -89,8 +90,8 @@ def add(self, x: object) -> None:
if x is None:
raise ValueError("x cannot be None")
self.heap.append(x)
if self.size() > 1:
self._push_up(self.size() - 1)
if self.size > 1:
self._push_up(self.size - 1)

def contains(self, x: object) -> bool:
"""Returns true if `x` is in this heap, false otherwise.
Expand All @@ -114,10 +115,10 @@ def delete(self, x: object) -> None:
raise LookupError("x not found")

# self has at least one element.
if i == self.size() - 1:
if i == self.size - 1:
self.heap.pop()
else:
self._swap(i, self.size() - 1)
self._swap(i, self.size - 1)
self.heap.pop()
self._push_down(i)
self._push_up(i)
Expand Down Expand Up @@ -201,7 +202,7 @@ def _is_good_index(self, i: int) -> bool:
"""Returns true if `i` is in the bounds of elf.heap, false otherwise.
Time complexity: O(1)."""
return False if (i < 0 or i >= self.size()) else True
return False if (i < 0 or i >= self.size) else True

def __str__(self):
return str(self.heap)
Expand Down
100 changes: 51 additions & 49 deletions ands/ds/LinearProbingHashTable.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
- Should a client of this class be able to specify its custom hash function ??
- size could be implemented as a counter
# References
- [http://interactivepython.org/runestone/static/pythonds/SortSearch/Hashing.html](http://interactivepython.org/runestone/static/pythonds/SortSearch/Hashing.html)
Expand Down Expand Up @@ -91,11 +93,6 @@ def __init__(self, capacity: int = 11):
self._keys = [None] * self._n
self._values = [None] * self._n

def __invariants(self):
"""These conditions should always hold at the beginning and end of public methods!"""
assert len(self._keys) == len(self._values) == self._n
assert not has_duplicates_ignore_nones(self._keys)

@property
def size(self) -> int:
"""Returns the number of pairs key-value in this map."""
Expand All @@ -108,6 +105,22 @@ def capacity(self) -> int:
self.__invariants()
return len(self._keys)

@staticmethod
def _hash_code(key, size: int) -> int:
"""Returns a hash code (an int) between 0 and `size` (excluded).
`size` must be the size of the buffer based on which
this function should return a hash value."""
return hash(key) % size

@staticmethod
def _rehash(old_hash: int, size: int) -> int:
"""Returns a new hash value based on the previous one called `old_hash`.
`size` must be the size of the buffer based on which
we want to have a new hash value from the old hash value."""
return (old_hash + 1) % size

def put(self, key: object, value: object) -> None:
"""Inserts the pair `key`/`value` in this map.
Expand Down Expand Up @@ -193,6 +206,35 @@ def get(self, key: object) -> object:
self.__invariants()
return value

@staticmethod
def _get(key: object, keys: list, values: list, size: int) -> object:
"""Helper method of `self.get` and thus it's considered PRIVATE."""
assert not has_duplicates_ignore_nones(keys)

hash_value = LinearProbingHashTable._hash_code(key, size)

data = None
stop = False
found = False
position = hash_value

while keys[position] is not None and not found and not stop:

if keys[position] == key:
found = True
data = values[position]
else:
# Find a new possible position by rehashing
position = LinearProbingHashTable._rehash(position, size)

# We are at the initial slot,
# and thus nothing was found.
if position == hash_value:
stop = True

assert not has_duplicates_ignore_nones(keys)
return data

def delete(self, key: object) -> object:
"""Deletes the mapping between `key` and its corresponding associated value.
If there's no mapping, nothing is done."""
Expand Down Expand Up @@ -235,50 +277,10 @@ def __str__(self):
def __repr__(self):
return self.__str__()

@staticmethod
def _hash_code(key, size: int) -> int:
"""Returns a hash code (an int) between 0 and `size` (excluded).
`size` must be the size of the buffer based on which
this function should return a hash value."""
return hash(key) % size

@staticmethod
def _rehash(old_hash: int, size: int) -> int:
"""Returns a new hash value based on the previous one called `old_hash`.
`size` must be the size of the buffer based on which
we want to have a new hash value from the old hash value."""
return (old_hash + 1) % size

@staticmethod
def _get(key: object, keys: list, values: list, size: int) -> object:
"""Helper method of `self.get` and thus it's considered PRIVATE."""
assert not has_duplicates_ignore_nones(keys)

hash_value = LinearProbingHashTable._hash_code(key, size)

data = None
stop = False
found = False
position = hash_value

while keys[position] is not None and not found and not stop:

if keys[position] == key:
found = True
data = values[position]
else:
# Find a new possible position by rehashing
position = LinearProbingHashTable._rehash(position, size)

# We are at the initial slot,
# and thus nothing was found.
if position == hash_value:
stop = True

assert not has_duplicates_ignore_nones(keys)
return data
def __invariants(self):
"""These conditions should always hold at the beginning and end of public methods!"""
assert len(self._keys) == len(self._values) == self._n
assert not has_duplicates_ignore_nones(self._keys)


def has_duplicates_ignore_nones(ls: list) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion ands/ds/MaxHeap.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def remove_max(self) -> object:
Time complexity: O(log(n))."""
assert is_max_heap(self)
if not self.is_empty():
self._swap(0, self.size() - 1)
self._swap(0, self.size - 1)
m = self.heap.pop()
if not self.is_empty():
self._push_down(0)
Expand Down
2 changes: 1 addition & 1 deletion ands/ds/MinHeap.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def remove_min(self) -> object:
Time complexity: O(log(n))."""
assert is_min_heap(self)
if not self.is_empty():
self._swap(0, self.size() - 1)
self._swap(0, self.size - 1)
m = self.heap.pop()
if not self.is_empty():
self._push_down(0)
Expand Down
18 changes: 9 additions & 9 deletions ands/ds/MinMaxHeap.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ def remove_max(self) -> object:
if not self.is_empty():
i = self._find_max_index()

if i == self.size() - 1:
if i == self.size - 1:
m = self.heap.pop()
assert is_min_max_heap(self)
return m

self._swap(i, self.size() - 1)
self._swap(i, self.size - 1)
m = self.heap.pop()
self._push_up(i)
self._push_down(i)
Expand All @@ -103,12 +103,12 @@ def remove_min(self) -> object:
Time complexity: O(log(n))."""
if not self.is_empty():
if self.size() == 1:
if self.size == 1:
m = self.heap.pop()
assert is_min_max_heap(self)
return m

self._swap(0, self.size() - 1)
self._swap(0, self.size - 1)
m = self.heap.pop()
self._push_up(0)
self._push_down(0)
Expand Down Expand Up @@ -221,9 +221,9 @@ def _find_max_index(self) -> int:
Time complexity: O(1)."""
if self.is_empty():
return -1
elif self.size() == 1:
elif self.size == 1:
return 0
elif self.size() == 2:
elif self.size == 2:
return 1
else:
return 1 if self.heap[1] > self.heap[2] else 2
Expand Down Expand Up @@ -349,11 +349,11 @@ def is_min_max_heap(h: MinMaxHeap) -> bool:

if h.heap:

if h.size() == 1:
if h.size == 1:
return True
if h.size() == 2:
if h.size == 2:
return max(h.heap) == h.heap[1] and min(h.heap) == h.heap[0]
if h.size() >= 3:
if h.size >= 3:
if h.heap[0] != min(h.heap) or (h.heap[1] != max(h.heap) and h.heap[2] != max(h.heap)):
return False

Expand Down
17 changes: 9 additions & 8 deletions ands/ds/Queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ def __init__(self, ls=None):
ls = []
self._q = deque(ls)

@property
def size(self) -> int:
"""Returns the size of this queue."""
return len(self._q)

def is_empty(self) -> bool:
"""Returns true if this queue is empty, false otherwise."""
return self.size == 0

def enqueue(self, elem) -> None:
"""Adds `elem` to the end of this queue.
If `elem` is None, ValueError is raised."""
Expand All @@ -68,14 +77,6 @@ def dequeue(self):
"""Returns the first element of this queue, or None if the queue is empty."""
return None if self.is_empty() else self._q.popleft()

def is_empty(self) -> bool:
"""Returns true if this queue is empty, false otherwise."""
return self.size() == 0

def size(self) -> int:
"""Returns the size of this queue."""
return len(self._q)

def __str__(self):
return str(list(self._q))

Expand Down
Loading

0 comments on commit f49e761

Please sign in to comment.