Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions config.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -1064,6 +1064,20 @@
"uuid": "6b7f7b1f-c388-4f4c-b924-84535cc5e1a0",
"slug": "hexadecimal",
"deprecated": true
},
{
"uuid": "6f196341-0ffc-9780-a7ca-1f817508247161cbcd9",
"slug": "binary-search-tree",
"core": false,
"unlocked_by": null,
"difficulty": 4,
"topics":[
"data_structures",
"classes",
"trees",
"searching",
"object_oriented_programming"
]
}
],
"foregone": [
Expand Down
52 changes: 52 additions & 0 deletions exercises/binary-search-tree/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
Insert and search for numbers in a binary tree.

When we need to represent sorted data, an array does not make a good
data structure.

Say we have the array `[1, 3, 4, 5]`, and we add 2 to it so it becomes
`[1, 3, 4, 5, 2]` now we must sort the entire array again! We can
improve on this by realizing that we only need to make space for the new
item `[1, nil, 3, 4, 5]`, and then adding the item in the space we
added. But this still requires us to shift many elements down by one.

Binary Search Trees, however, can operate on sorted data much more
efficiently.

A binary search tree consists of a series of connected nodes. Each node
contains a piece of data (e.g. the number 3), a variable named `left`,
and a variable named `right`. The `left` and `right` variables point at
`nil`, or other nodes. Since these other nodes in turn have other nodes
beneath them, we say that the left and right variables are pointing at
subtrees. All data in the left subtree is less than or equal to the
current node's data, and all data in the right subtree is greater than
the current node's data.

For example, if we had a node containing the data 4, and we added the
data 2, our tree would look like this:

4
/
2

If we then added 6, it would look like this:

4
/ \
2 6

If we then added 3, it would look like this

4
/ \
2 6
\
3

And if we then added 1, 5, and 7, it would look like this

4
/ \
/ \
2 6
/ \ / \
1 3 5 7
26 changes: 26 additions & 0 deletions exercises/binary-search-tree/binary_search_tree.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
class Node:
def __init__(self):
pass


class BinarySearchTree:
def __init__(self):
pass

def __base_function(self, num):
pass

def __replace_node(self, num):
pass

def contains(self, num):
pass

def add(self, num):
pass

def remove(self, num):
pass

def size(self):
pass
153 changes: 153 additions & 0 deletions exercises/binary-search-tree/binary_search_tree_test.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
import unittest

from binary_search_tree import BinarySearchTree


class BinarySearchTreeTests(unittest.TestCase):
def test_add_one_value_in_tree(self):
tree = BinarySearchTree()
self.assertEqual(tree.add(3), True)
self.assertEqual(tree.size(), 1)
self.assertEqual(tree.contains(3), True)

def test_add_multiple_values_in_tree(self):
tree = BinarySearchTree()
self.assertEqual(tree.add(-1), True)
self.assertEqual(tree.add(50), True)
self.assertEqual(tree.add(-73), True)
self.assertEqual(tree.add(20), True)
self.assertEqual(tree.size(), 4)
self.assertEqual(tree.contains(-1), True)
self.assertEqual(tree.contains(50), True)
self.assertEqual(tree.contains(-73), True)
self.assertEqual(tree.contains(20), True)

def test_add_repeated_values_in_tree(self):
tree = BinarySearchTree()
self.assertEqual(tree.add(2), True)
self.assertEqual(tree.add(3), True)
self.assertEqual(tree.add(4), True)
self.assertEqual(tree.add(2), False)
self.assertEqual(tree.add(1), True)
self.assertEqual(tree.add(1), False)
self.assertEqual(tree.add(1), False)
self.assertEqual(tree.add(2), False)
self.assertEqual(tree.add(3), False)
self.assertEqual(tree.add(4), False)
self.assertEqual(tree.add(5), True)
self.assertEqual(tree.size(), 5)
self.assertEqual(tree.contains(1), True)
self.assertEqual(tree.contains(2), True)
self.assertEqual(tree.contains(3), True)
self.assertEqual(tree.contains(4), True)
self.assertEqual(tree.contains(5), True)

def test_remove_one_value_in_tree_with_no_elements(self):
tree = BinarySearchTree()
self.assertEqual(tree.remove(70), False)
self.assertEqual(tree.size(), 0)
self.assertEqual(tree.contains(70), False)

def test_remove_multiple_values_in_tree_with_no_elements(self):
tree = BinarySearchTree()
self.assertEqual(tree.remove(70), False)
self.assertEqual(tree.remove(-25), False)
self.assertEqual(tree.size(), 0)
self.assertEqual(tree.contains(70), False)
self.assertEqual(tree.contains(-25), False)

def test_remove_value_in_tree_with_one_element(self):
tree = BinarySearchTree()
tree.add(0)
self.assertEqual(tree.remove(0), True)
self.assertEqual(tree.size(), 0)
self.assertEqual(tree.contains(0), False)

def test_remove_multiple_values_in_tree(self):
tree = BinarySearchTree()
tree.add(3)
tree.add(1)
tree.add(2)
tree.add(5)
tree.add(4)
self.assertEqual(tree.remove(3), True)
self.assertEqual(tree.remove(1), True)
self.assertEqual(tree.remove(5), True)
self.assertEqual(tree.remove(6), False)
self.assertEqual(tree.remove(7), False)
self.assertEqual(tree.size(), 2)
self.assertEqual(tree.contains(1), False)
self.assertEqual(tree.contains(2), True)
self.assertEqual(tree.contains(3), False)
self.assertEqual(tree.contains(4), True)
self.assertEqual(tree.contains(5), False)
self.assertEqual(tree.contains(6), False)
self.assertEqual(tree.contains(7), False)

def test_contains_value_in_tree_with_no_elements(self):
tree = BinarySearchTree()
self.assertEqual(tree.contains(-123), False)
self.assertEqual(tree.contains(-0), False)

def test_contains_value_in_tree_with_multiple_operations(self):
tree = BinarySearchTree()
tree.remove(1)
self.assertEqual(tree.contains(1), False)
tree.add(1)
self.assertEqual(tree.contains(1), True)
tree.remove(2)
self.assertEqual(tree.contains(2), False)
tree.add(3)
self.assertEqual(tree.contains(3), True)
tree.add(2)
self.assertEqual(tree.contains(2), True)
tree.add(4)
self.assertEqual(tree.contains(4), True)
tree.remove(1)
self.assertEqual(tree.contains(1), False)
tree.remove(1)
self.assertEqual(tree.contains(1), False)
tree.add(2)
self.assertEqual(tree.contains(2), True)
tree.add(5)
self.assertEqual(tree.contains(5), True)
tree.remove(2)
self.assertEqual(tree.contains(1), False)
self.assertEqual(tree.contains(2), False)
self.assertEqual(tree.contains(3), True)
self.assertEqual(tree.contains(4), True)
self.assertEqual(tree.contains(5), True)

def test_size_in_tree_with_no_elements(self):
tree = BinarySearchTree()
self.assertEqual(tree.size(), 0)

def test_size_in_tree_with_multiple_operations(self):
tree = BinarySearchTree()
self.assertEqual(tree.size(), 0)
tree.add(1)
self.assertEqual(tree.size(), 1)
tree.remove(1)
self.assertEqual(tree.size(), 0)
tree.remove(2)
self.assertEqual(tree.size(), 0)
tree.add(3)
self.assertEqual(tree.size(), 1)
tree.add(2)
self.assertEqual(tree.size(), 2)
tree.add(4)
self.assertEqual(tree.size(), 3)
tree.remove(1)
self.assertEqual(tree.size(), 3)
tree.remove(1)
self.assertEqual(tree.size(), 3)
tree.add(2)
self.assertEqual(tree.size(), 3)
tree.add(5)
self.assertEqual(tree.size(), 4)
tree.remove(2)
self.assertEqual(tree.size(), 3)


if __name__ == '__main__':
unittest.main()
68 changes: 68 additions & 0 deletions exercises/binary-search-tree/example.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
class Node:
def __init__(self):
self.children = [None, None]
self.num = None


class BinarySearchTree:
def __init__(self):
self.__size = 0
self.__root = Node()

def __base_function(self, num):
node = self.__root
while (node.num is not None):
if node.num > num:
if node.children[0] is None:
node.children[0] = Node()
node = node.children[0]
elif node.num < num:
if node.children[1] is None:
node.children[1] = Node()
node = node.children[1]
else:
break
return node

def __replace_node(self, node):
node.num = None
pos = -1

for i in range(len(node.children)):
if not (node.children[i] is None or node.children[i].num is None):
pos = i

if not pos == -1:
child_node = node.children[pos]
n_pos = (pos + 1) % 2
while not (child_node.children[n_pos] is None
or child_node.children[n_pos].num is None):
child_node = child_node.children[n_pos]
node.num = child_node.num
self.__replace_node(child_node)

def contains(self, num):
node = self.__base_function(num)
if node.num is None:
return False
else:
return True

def add(self, num):
node = self.__base_function(num)
if node.num is None:
node.num = num
self.__size += 1
return True
return False

def remove(self, num):
node = self.__base_function(num)
if node.num is not None:
self.__replace_node(node)
self.__size -= 1
return True
return False

def size(self):
return self.__size