Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
25a38e0
Update .travis.yml
julienawilson Jan 18, 2017
97e30fc
Update README.md
julienawilson Jan 18, 2017
c8b6b90
added new methods to README and module doctstrings
rveeblefetzer Jan 18, 2017
f7e5913
Merge branch 'bst' of https://github.com/julienawilson/data-structure…
rveeblefetzer Jan 18, 2017
cb54d06
adding delete method
rveeblefetzer Jan 18, 2017
18d7877
added 1-child node instances for delete
rveeblefetzer Jan 18, 2017
4ba611a
delete for node with two childs
julienawilson Jan 18, 2017
cf9a522
added the easy tests for delete
rveeblefetzer Jan 18, 2017
a65f8c6
Merge branch 'bst' of https://github.com/julienawilson/data-structure…
rveeblefetzer Jan 18, 2017
1505ae8
fix in the delete method
julienawilson Jan 18, 2017
136cd50
delete fix
julienawilson Jan 19, 2017
2214ff2
fix in delete, more tests
julienawilson Jan 19, 2017
dba1e85
balance fix
julienawilson Jan 19, 2017
621f7ff
added tests for delete to check two-way connections of deleted; added…
rveeblefetzer Jan 19, 2017
046afe0
cleanup for merge
rveeblefetzer Jan 19, 2017
95ec636
deleted corpse code
rveeblefetzer Jan 19, 2017
cc7ad6c
changed balance method so it can take non-root nodes as argument
rveeblefetzer Jan 19, 2017
5e0c421
added autobalanc helper functions to rotate left/right
rveeblefetzer Jan 19, 2017
c63dc6d
tests for rotation
julienawilson Jan 19, 2017
98ec9d1
bug in left right rotation
julienawilson Jan 24, 2017
b26a465
true/false switch for autobalancing tree
julienawilson Jan 24, 2017
99a354c
fixed the rotations bug. small operator error.
julienawilson Jan 24, 2017
61c5e7d
adding to docstrings
rveeblefetzer Jan 24, 2017
276306e
Merge branch 'bst' of https://github.com/julienawilson/data-structure…
rveeblefetzer Jan 24, 2017
343328a
Merge branch 'bst-rotation' of https://github.com/julienawilson/data-…
rveeblefetzer Jan 24, 2017
2f8af43
added docstrings, made autobalance, rebalance and rotations into hidd…
rveeblefetzer Jan 24, 2017
04d8649
adding initial files
rveeblefetzer Jan 25, 2017
ae697bd
hash table
julienawilson Jan 25, 2017
41bcb9e
Merge branch 'hash-table' of https://github.com/julienawilson/data-st…
rveeblefetzer Jan 25, 2017
14197b9
added pseudocode-ish addititve hash
rveeblefetzer Jan 25, 2017
366131f
add hash func written
julienawilson Jan 25, 2017
577725b
added blank test file for hash table module
rveeblefetzer Jan 25, 2017
a21cad5
Merge branch 'hash-table' of https://github.com/julienawilson/data-st…
rveeblefetzer Jan 25, 2017
5ca754b
set and hash functions
julienawilson Jan 25, 2017
0e23f34
Merge branch 'hash-table' of https://github.com/julienawilson/data-st…
rveeblefetzer Jan 25, 2017
1d25cbd
get for the hash function
julienawilson Jan 25, 2017
0ec88b5
added test for seting a number; fix typon in docstrings
rveeblefetzer Jan 25, 2017
201e12a
Merge branch 'hash-table' of https://github.com/julienawilson/data-st…
rveeblefetzer Jan 25, 2017
bf2a18b
added tests using unix dictionary list
rveeblefetzer Jan 25, 2017
7743b62
xor hash
julienawilson Jan 25, 2017
2c15414
added tests for xor with unix dict file
rveeblefetzer Jan 25, 2017
5eddf66
Added class docstring to module; added module info to README
rveeblefetzer Jan 25, 2017
e2f7888
Update README.md
julienawilson Jan 26, 2017
6636b16
hash table tests tidy
julienawilson Jan 26, 2017
a26518f
Merge branch 'hash-table' of https://github.com/julienawilson/data-st…
julienawilson Jan 26, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ language: python
python:
- "2.7"
- "3.5"

install:
- pip install -e .[test]

Expand Down
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
[![Build Status](https://travis-ci.org/julienawilson/data-structures.svg?branch=master)](https://travis-ci.org/julienawilson/data-structures)

# Data Structures
=======
[![Build Status](https://travis-ci.org/julienawilson/data-structures.svg?branch=bst)](https://travis-ci.org/julienawilson/data-structures)

# data-structures
>>>>>>> 2f8af436a8271e1a77876030b065993c23274041
Patrick Saunders and Julien Wilson
<br>
Data Structures created in Python401
Expand All @@ -26,7 +31,7 @@ A linked list that points in both directions
A tree of nodes sorted by values less than and greater than root branching to the left and right, respectively.

Methods include:
* insert(self, val): Insert value into tree; if value already exists, ignore it.
* insert(self, val): Insert value into tree; if value already exists, ignore it. Method autobalances after insertion, and tree size increments by one.
* search(self, val): Return node containing that value, else None.
* size(self): Return number of nodes/vertices in tree, 0 if empty.
* depth(self): Return number of levels in tree. Tree with one value has depth of 0.
Expand All @@ -35,3 +40,21 @@ Methods include:
Trees that are higher on the left than the right should return a positive value;
trees that are higher on the right than the left should return a negative value;
an ideally-balanced tree should return 0.
* in_order(self): Return a generator that returns each node value from in-order traversal.
* pre_order(self): Return a generator that returns each node value from pre-order traversal.
* post_order(self): Return a generator that returns each node value from post_order traversal.
* breadth_first(self): Return a generator returns each node value from breadth-first traversal.
* delete(value): Delete a node's connections (edges), effectively deleting node. Method autobalances after deletion, and tree size decrements by one.

##Hash Table
Stores key-value pairs using a given hashing algorithm. Choices for hashing algorithms are additive hash and xor hash.
Additive hash sums the Unicode code point for each letter in the word or string, then calls modulo with the number of buckets in the table.
XOR hash runs exclusive or with the letters of the word or string.

Methods include:
* set(key, value): Add a key-value pair to the hash table. calls a hash function, but is otherwise O(k) where k is the number of items in the bucket.
* get(key): Retrieve a value for the given key. Add a key-value pair to the hash table. calls a hash function, but is otherwise O(k) where k is the number of items in the bucket.
* _hash(hash_alg): Decide which hash algorithm to call. O(1)
* _additive_hash(word): return a hash using an additive method; O(n) where n=length of word
* _xor_hash(word): return a hash using an additive method; O(n) where n=length of word
*
158 changes: 144 additions & 14 deletions src/bst.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
"""Classes for binary search tree.

Methods include:
insert(self, val): Insert value into tree; if value already exists, ignore it.
search(self, val): Return node containing that value, else None.
size(self): Return number of nodes/vertices in tree, 0 if empty.
depth(self): Return number of levels in tree. Tree with one value has depth of 0.
contains(self, val): Return True if value is in tree, False if not.
balance(self): Return a positive or negative integer representing tree's balance.
insert(val): Insert value into tree; if value already exists, ignore it.
Method autobalances after insertion, and tree size increments by one.
search(val): Return node containing that value, else None.
size(): Return number of nodes/vertices in tree, 0 if empty.
depth(): Return number of levels in tree. Tree with one value has depth of 0.
contains(val): Return True if value is in tree, False if not.
balance(): Return a positive or negative integer representing tree's balance.
Trees that are higher on the left than the right should return a positive value;
trees that are higher on the right than the left should return a negative value;
an ideally-balanced tree should return 0.
in_order(): Return a generator that returns each node value from in-order traversal.
pre_order(): Return a generator that returns each node value from pre-order traversal.
post_order(): Return a generator that returns each node value from post_order traversal.
breadth_first(): Return a generator returns each node value from breadth-first traversal.
delete(value): Delete a node's connections (edges), effectively deleting node.
Method autobalances after deletion, and tree size decrements by one.

"""

from queue import Queue
Expand All @@ -18,11 +26,12 @@
class Node():
"""Node object for the binary search tree."""

def __init__(self, value, left=None, right=None):
def __init__(self, value, left=None, right=None, parent=None):
"""Instantiate a node object."""
self.value = value
self.left = left
self.right = right
self.parent = parent


class BinarySearchTree():
Expand All @@ -36,7 +45,7 @@ def __init__(self):
self._size = 0
self.root = None

def insert(self, value):
def insert(self, value, autobalance=True):
"""Insert a value in to the binary search tree."""
if self.root is None:
self.root = Node(value)
Expand All @@ -49,17 +58,21 @@ def insert(self, value):
current_node = current_node.left
else:
current_node.left = Node(value)
current_node.left.parent = current_node
self._size += 1
break
elif value > current_node.value:
if current_node.right:
current_node = current_node.right
else:
current_node.right = Node(value)
current_node.right.parent = current_node
self._size += 1
break
else:
break
if autobalance:
self._autobalance()

def search(self, value):
"""Search the Binary Search Tree for a value, return node or none."""
Expand Down Expand Up @@ -123,19 +136,96 @@ def contains(self, value):
else:
return False

def balance(self):
"""Return numerical representation of how balanced the tree is."""
if self.root.left:
depth_left = self.depth(self.root.left)
def balance(self, node='root'):
"""Return numerical representation of how balanced the tree (or branches) is."""
if node is None:
return 0
if node == 'root':
node = self.root
if node.left:
depth_left = self.depth(node.left) + 1
else:
depth_left = 0
if self.root.right:
depth_right = self.depth(self.root.right)
if node.right:
depth_right = self.depth(node.right) + 1
else:
depth_right = 0
balance = depth_right - depth_left
return balance

def _autobalance(self, node=None):
"""Make sure tree rebalances itself."""
if node is None:
node = self.root
nodes = self.post_order()
while True:
try:
this_node = next(nodes)
if abs(self.balance(this_node)) > 1:
self._rebalance(this_node)
except StopIteration:
break

# pass

def _rebalance(self, node):
"""Balance the given node."""
if self.balance(node) > 1:
if self.balance(node.right) >= 1:
self._rotate_left(node)
else:
self._rotate_right(node.right)
self._rotate_left(node)
elif self.balance(node) < -1:
if self.balance(node.left) <= -1:
self._rotate_right(node)
else:
self._rotate_left(node.left)
self._rotate_right(node)

def _rotate_right(self, node, holder_node=None):
"""Helper function to shift nodes clockwise."""
if node is None:
return
try:
if node.left.right:
holder_node = node.left.right
except AttributeError:
pass
if node.left:
node.left.parent = node.parent
node.left.right = node
if node.parent:
node.parent.left = node.left
node.parent = node.left
node.left = holder_node
if holder_node:
node.left.parent = node
if node == self.root:
self.root = node.parent

def _rotate_left(self, node, holder_node=None):
"""Helper function to shift nodes counterclockwise."""
if node is None:
return
try:
if node.right.left:
holder_node = node.right.left
except AttributeError:
pass

if node.right:
node.right.parent = node.parent
node.right.left = node
if node.parent:
node.parent.right = node.right
node.parent = node.right
node.right = holder_node
if holder_node:
node.right.parent = node
if node == self.root:
self.root = node.parent

def in_order(self):
"""Return generator that returns tree values one at a time using in-order traversal."""
stack = []
Expand Down Expand Up @@ -191,3 +281,43 @@ def breadth_first(self):
if current_node.right:
trav_list.enqueue(current_node.right)
yield current_node

def delete(self, value, autobalance=True):
"""Get rid of a node. Or at least its connection."""
target_node = self.search(value)
if not target_node:
return
if not (target_node.left or target_node.right):
if target_node.value > target_node.parent.value:
target_node.parent.right = None
else:
target_node.parent.left = None
elif not (target_node.left and target_node.right):
if target_node.left:
target_node.left.parent = target_node.parent
target_node.parent.left = target_node.left
else:
target_node.right.parent = target_node.parent
target_node.parent.right = target_node.right
else:
current_node = target_node.right
while current_node.left:
current_node = current_node.left
replace_node = current_node
self.delete(current_node.value)
self._size += 1 # undoes size change within delete
if target_node.parent:
replace_node.parent = target_node.parent
if replace_node.value < target_node.value:
target_node.parent.left = replace_node
else:
target_node.parent.right = replace_node
replace_node.left = target_node.left
replace_node.right = target_node.right
target_node.parent = None
target_node.left = None
target_node.right = None
self._size -= 1
if autobalance:
self._autobalance()

62 changes: 62 additions & 0 deletions src/hash_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Class for hash tables.

Choices for hashing algorithms are additive hash and xor hash.
Additive hash sums the Unicode code point for each letter in the word or string,
then calls modulo with the number of buckets in the table.
XOR hash runs exclusive or with the letters of the word or string.
Methods include:
set(key, value): Add a key-value pair to the hash table.
get(key): Retrieve a value for the given key.

"""



class HashTable(object):
"""Something something."""

def __init__(self, size, hash_alg='additive'):
"""Initialize a hash table."""
self._size = size
self.buckets = [[] for bucket in range(self._size)]
self._hash_alg = self._hash(hash_alg)

def _hash(self, hash_alg):
if hash_alg == 'additive':
return self._additive_hash
if hash_alg == 'xor':
return self._xor_hash
else:
raise ValueError("Please enter a valid hash algorithm. The options are 'additive' and 'xor'.")

def _additive_hash(self, word):
"""Return Additive hash value."""
return sum([ord(char) for char in list(word)]) % self._size

def _xor_hash(self, word):
"""Return a xor hash."""
hash_val = 0
for i in range(len(word)):
hash_val ^= ord(word[i])
return hash_val

def set(self, key, value):
"""Set a new key-value pair in the hash table."""
if type(key) is not str:
raise TypeError("Key for hash table must be a string.")
hash_val = self._hash_alg(key)
for pair in self.buckets[hash_val]:
if pair[0] == key:
pair[1] = value
return
self.buckets[hash_val].append([key, value])

def get(self, key):
"""Get the value from the hash table."""
if type(key) is not str:
raise TypeError("Key for hash table must be a string.")
hash_val = self._hash_alg(key)
for pair in self.buckets[hash_val]:
if pair[0] == key:
return pair[1]
return
Loading