Skip to content
Open

Hash #14

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
b9f3ad4
add insert and test_bst.py with insert
henry808 Mar 4, 2015
5c44fa0
Added _size attribute, size(), and test for empty tree.
Mar 4, 2015
f910750
Fixed size increment bug that overlooked first/empty case and added t…
Mar 4, 2015
588ca91
Added additional attribute for tracking the depth of a tree, as well …
Mar 4, 2015
e56c299
added contains and test for contains
henry808 Mar 4, 2015
5e8601b
added the ability to generate a graphic representation of a graph usi…
henry808 Mar 5, 2015
25bc1d1
added docstrings to functions
henry808 Mar 5, 2015
1e1b34e
fixed balance and created a better test for it
henry808 Mar 5, 2015
8a43f41
fixed balance function
henry808 Mar 5, 2015
f43ea6e
README.md for bst
henry808 Mar 5, 2015
b524234
Merge.
Mar 5, 2015
2513a16
Merged.
Mar 5, 2015
996e65e
I understand what I'm getting from a call on in_order() is a generato…
Mar 5, 2015
b580d92
Fixed generator and added test.
Mar 5, 2015
971d5e5
Changed in_order() to reflect nature of the recursive call only yield…
Mar 6, 2015
9e87d91
add breadth first, post_order, and pre_order generators to bst.py and…
henry808 Mar 6, 2015
6271642
Merged.
Mar 6, 2015
c36afbc
Fixed unicode decode error.
Mar 6, 2015
312b764
Added .travis file for this branch.
Mar 6, 2015
2d88e5a
Added requirements.txt.
Mar 6, 2015
37c5e60
Updated .yml file.
Mar 6, 2015
7691f13
updated README.md
henry808 Mar 6, 2015
84d1be1
Merge branch 'bst-traverse' of github.com:jefimenko/data-structures i…
henry808 Mar 6, 2015
fd16ce8
Updated to run all tests.
Mar 6, 2015
0ff9c3e
Merge branch 'bst-traverse' of https://github.com/jefimenko/data-stru…
Mar 6, 2015
80f6369
Changed generators.
Mar 8, 2015
5e7f867
added helper functions to bst.py to make functions more clear
henry808 Mar 9, 2015
37d5807
changes to try to fix in order, pre order, and post order traversals
henry808 Mar 9, 2015
aeaba6e
tests now look at all values in expected case
henry808 Mar 9, 2015
a8c6f85
added a deque to breadth first traverse and fixed in, pre, and post o…
henry808 Mar 9, 2015
7ce920e
Added reference from children to parents.
Mar 9, 2015
dbb4e47
reverted to older commit because lost changes
henry808 Mar 9, 2015
f17a5aa
Readded parent information.
Mar 9, 2015
d46308c
Added helper function to get a parent for a given node.
Mar 9, 2015
28d888a
Added first case for deletion function.
Mar 9, 2015
88f48ed
Added simple case of deleting node with no children.
Mar 9, 2015
4f1b3ee
Added tests for deleting nodes with one child, TODO: add assertions f…
Mar 9, 2015
09253c0
wrote node_depth funciton and removed node from dictionary. still now…
henry808 Mar 10, 2015
fb638b6
took out nodes from trees in test_bst.py
henry808 Mar 10, 2015
ace1c00
merged
henry808 Mar 10, 2015
981ea38
added test for node_depth
henry808 Mar 10, 2015
a416735
fixed node_depth so it is correct when a node with value 0 exists
henry808 Mar 10, 2015
635fab2
added _rightmost and tests for _rightmost
henry808 Mar 10, 2015
7f14b3c
Added a internal method for swapping nodes.
Mar 10, 2015
23d8fa1
fixed swap sot that it stores parents in temp value
henry808 Mar 10, 2015
f4c0bb9
Added tests for swapping parent/child nodes.
Mar 10, 2015
7cd9ed2
Added parent references to dot graph for tree.
Mar 10, 2015
1d9f460
Added delete for simple case of no children, and recursive case for p…
Mar 10, 2015
be37e9c
Fixed case of deleting a node that is on the left of it's parent, but…
Mar 10, 2015
f4673ad
wrote get, set, and heap in hash.py
henry808 Mar 13, 2015
6fb2a9f
Merge branch 'hash' of https://github.com/jefimenko/data-structures i…
Mar 13, 2015
ccc63d4
Added basic tests for hash.
Mar 13, 2015
32caadb
added test for UNIX word dictionary
henry808 Mar 13, 2015
786f79d
changed list to h_list to clarify name
henry808 Apr 2, 2015
dd8d073
added a test for duplicate keys put into hash table
henry808 Apr 2, 2015
7b0f126
wrote two tests for using a non-sting as a key
henry808 Apr 2, 2015
f877187
added a nicer error raised from using something of the wrong type
henry808 Apr 2, 2015
81dd743
rewrote set so that it replaces a list item when setting with a dupli…
henry808 Apr 2, 2015
c52fefe
added test to make sure that one one key stored for duplicate keys
henry808 Apr 2, 2015
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
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: python
python:
- "2.7"
install: "pip install -r requirements.txt"
script: py.test
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,14 @@
Code Fellows Dev Accelerator: This repository will be for implementations of classic data structures in python.


parens.py:
Includes a function balanceness() that takes string and determines if it is 'open', 'balanced', or 'broken', depending on the sequence of parenthesis contained. 'Open', 'balanced', and 'broken' are respectively determined as more leading '('s than ')'s, equal number of leading '(' and ')', and any sequence including ')' that do not have a preceding '('. 'Open' strings are represented by returning a 1, 'balanced' by 0, and 'broken' by -1, with an emptry string being considered 'balanced'.
## parens.py:
Includes a function balanceness() that takes string and determines if it is 'open', 'balanced', or 'broken', depending on the sequence of parenthesis contained. 'Open', 'balanced', and 'broken' are respectively determined as more leading '('s than ')'s, equal number of leading '(' and ')', and any sequence including ')' that do not have a preceding '('. 'Open' strings are represented by returning a 1, 'balanced' by 0, and 'broken' by -1, with an emptry string being considered 'balanced'.

## bst.py:
Binary search tree is a tree where each node has a left child and a right
child and every left child is smaller and every right child is larger. We implement it with a dictionary where each key is the value of the node and each value has three things in it, depth, left child and right child. This module includes a Bst class and the following functions: insert, balance, contains, size, depth, get\_dot. It also includes generators that traverse the tree in order: in\_order, pre\_order, post\_order, and breadth\_first.



## Resources:
[Wikipedia Breadth First Search](en.wikipedia.org/wiki/Breadth-first_search)
301 changes: 301 additions & 0 deletions bst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
#!/usr/bin/env python
import random
import subprocess
from collections import deque


class Bst(object):
"""Binary Search Tree using a dictionary"""
def __init__(self, value=None):
self.tree = {}
self.top = None
self._size = 0
self._depth = 0

if value is not None:
self.tree[value] = {}
self.top = value
self._size += 1
self._depth += 1

def left(self, current):
return self.tree[current].get('left')

def right(self, current):
return self.tree[current].get('right')

def parent(self, current):
return self.tree[current].get('parent')

def insert(self, value):
"""Insert a node with value in order.

In value is already present, it is ignored."""
current = self.top
if current is None:
self.tree[value] = {}
self.top = value
self._size += 1
self._depth += 1
return
depth = 1 # starts trying to insert at depth 2
while current is not None:
depth += 1
if current == value:
return
if current < value:
traverse = self.right(current)
child = 'right'
else:
traverse = self.left(current)
child = 'left'
if traverse is None:
#actual insert
self.tree[value] = {}

self.tree[current][child] = value
self.tree[value]['parent'] = current
self._size += 1
if depth > self._depth:
self._depth = depth
return
current = traverse

def delete(self, val):
if self.contains(val):
left_child = self.left(val)
right_child = self.right(val)
parent = self.parent(val)
if left_child is None and right_child is None:
# Delete a node with not children
if parent is not None:
# For a node with a parent...
if self.left(parent) == val:
del self.tree[parent]['left']
else:
del self.tree[parent]['right']
else:
# For deleting a node that is the root with no children
self.top = None
del self.tree[val]
elif left_child is not None:
biggest_child = self._rightmost(left_child)
self._swap_nodes(val, biggest_child)
self.delete(val)
else:
if parent is not None:
if self.left(parent) == val:
self.tree[parent]['left'] = right_child
else:
self.tree[parent]['right'] = right_child
self.tree[right_child]['parent'] = parent
else:
self.top = right_child
del self.tree[val]


def _swap_nodes(self, current, target):
"""
Swap two nodes.

Additionally, update all information on related nodes, as in, children
and parent nodes.
"""
cur_parent = self.parent(current)
targ_parent = self.parent(target)

children = []

children.append(self.left(current))
children.append(self.right(current))
children.append(self.left(target))
children.append(self.right(target))

for child in children:
# If a node children, update it's childrens' information
# for 'swapping'
if child and self.parent(child) == current:
self.tree[child]['parent'] = target
elif child:
self.tree[child]['parent'] = current

# Swapping information in the parent nodes relative to the nodes being 'swapped'
if current != self.top:
if current == self.tree.get(cur_parent).get('left'):
self.tree[cur_parent]['left'] = target
else:
self.tree[cur_parent]['right'] = target

if target == self.tree.get(targ_parent).get('left'):
self.tree[targ_parent]['left'] = current
else:
self.tree[targ_parent]['right'] = current

# Swap current and target's left, right, and parent information
self.tree[current], self.tree[target] = self.tree[target], self.tree[current]

if current == self.top:
self.top = target


def _rightmost(self, start):
"""Returns None if start is empty tree"""
while start is not None:
if self.right(start) is None:
return start
start = self.right(start)



# left_of_parent = self.left(self.parent(current))
# right_of_parent = self.right(self.parent(current))

# if current == val:
# if current == left_of_parent:
# del self.tree[self.parent(current)]['left']
# else:
# del self.tree[self.parent(current)]['right']

def balance(self):
"""Returns the balance of the tree:

Returns a positive representing how much deeper the tree is on the
right site or negative if the left is longer.
Return 0 if the tree is balanced (same depth on both sides)

"""
left_deep = 0
right_deep = 0
for node, v in self.tree.items():
if self.top > node:
if left_deep < self.node_depth(node):
left_deep = self.node_depth(node)
elif self.top < node:
if right_deep < self.node_depth(node):
right_deep = self.node_depth(node)
return right_deep - left_deep

def contains(self, value):
"""Returns true if value is in the tree."""
return value in self.tree

def size(self):
"""Returns the number of nodes in the tree."""
return self._size

def depth(self):
"""Returns the depth of the tree."""
return self._depth

def node_depth(self, current):
"""Returns the depth of a node in the tree."""
depth = 0
while current is not None:
current = self.parent(current)
depth +=1
return depth

def get_dot(self):
"""return the tree with root 'self' as a dot graph for visualization"""
return "digraph G{\n%s}" % ("" if not self.tree else (
"\n%s\n" % (
# "\t%s;\n%s\n" % (
# list(self.tree),
"\n".join(self._get_dot(self.top))
)
))

def _get_dot(self, current):
"""recursively prepare a dot graph entry for this node."""
left = self.tree[current].get('left')
right = self.tree[current].get('right')
parent = self.tree[current].get('parent')
if parent is not None:
yield "\t%s -> %s;" % (current, parent)

if left is not None:
yield "\t%s -> %s;" % (current, left)
for i in self._get_dot(left):
yield i
elif right is not None:
r = random.randint(0, 1e9)
yield "\tnull%s [shape=point];" % r
yield "\t%s -> null%s;" % (current, r)
if right is not None:
yield "\t%s -> %s;" % (current, right)
for i in self._get_dot(right):
yield i
elif left is not None:
r = random.randint(0, 1e9)
yield "\tnull%s [shape=point];" % r
yield "\t%s -> null%s;" % (current, r)


def in_order(self, current='start'):
"""
Generator that traverses the binary tree in order.
"""
if current == 'start':
current = self.top
if current is not None:
for node in self.in_order(self.left(current)):
yield node
yield current
for node in self.in_order(self.right(current)):
yield node


def pre_order(self, current='dutch'):
"""Generator that traverses the binary tree pre order."""
if current == 'dutch':
current = self.top
if current is not None:
yield current
for node in self.pre_order(self.left(current)):
yield node
for node in self.pre_order(self.right(current)):
yield node

def post_order(self, current='dutch'):
"""Generator that traverses the binary tree post order."""
if current == 'dutch':
current = self.top
if current is not None:
for node in self.post_order(self.left(current)):
yield node
for node in self.post_order(self.right(current)):
yield node
yield current

def breadth_first(self):
"""Generator that traverses the binary tree in breadth first order."""
q1 = deque()
q1.appendleft(self.top)
current = self.top
while q1:
current = q1.pop()
if self.left(current) is not None:
q1.appendleft(self.left(current))
if self.right(current) is not None:
q1.appendleft(self.right(current))
yield current


def main():
"""Best case and worst case are the same."""
tree = Bst()
inserts = [7, 4, 11, 2, 9, 6, 12, 5, 13, 0, 10, 8, 3, 1]
for i in inserts:
tree.insert(i)

tree._swap_nodes(6, 4)
print tree.tree
# for num in enumerate(tree.pre_order()):
# print num
dot_graph = tree.get_dot()
t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE)
t.communicate(dot_graph)


if __name__ == '__main__':
main()
34 changes: 34 additions & 0 deletions hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Hash(object):
def __init__(self, buckets):
self.h_list = []
for i in range(0, buckets):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the start value is 0 you don't need to specify it. That makes this whole expression a bit cleaner if you say something like:

self.h_list = [[] for _ in range(buckets)]

self.h_list.append([])

def set(self, key, val):
"""Store the given val using the given key.

Replaces the value if setting with a key already in.
"""
tup = (key, val)
bucket = self.h_list[self.hash(key)]
for item in bucket:
if item[0] == key:
bucket.remove(item)
break
bucket.append(tup)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good.


def hash(self, key):
if not isinstance(key, (str, unicode)):
type_ = type(key)
raise TypeError('Cannot hash something of type {}'.format(type_))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good. I like this.

total = 0
for letter in key:
total += ord(letter)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good, but this is cleaner:

total = sum(map(ord, key))

return total % len(self.h_list)

def get(self, key):
"""return the value stored with the given key"""
bucket = self.h_list[self.hash(key)]
for element in bucket:
if element[0] == key:
return element[1]
5 changes: 3 additions & 2 deletions linked_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def insert(self, val):
Set .data attribute to val and rearrange the list so the head is
the new node with a reference to the old head.
"""
# if isinstance(val, unicode):
# val = val.encode('utf-8')
self.head = List_Node(val, self.head)
self._size += 1

Expand Down Expand Up @@ -104,7 +106,7 @@ def display_prep(self):
while temp:
dummy = temp.data
if isinstance(temp.data, str or unicode):
dummy = "'{}'".format(dummy.encode('utf-8'))
dummy = "'{}'".format(dummy)

if temp is self.head:
output = "{}{}".format(output, dummy)
Expand All @@ -113,5 +115,4 @@ def display_prep(self):
output = "{}, {}".format(output, dummy)
temp = temp.next


return output + ")"
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
py==1.4.26
pytest==2.6.4
Loading