Skip to content

Commit bd4980a

Browse files
authored
Merge pull request #2741 from keon/fix/structural-cleanup
Structural cleanup: rewrite bst.py, fix test_insertion_sort bug
2 parents 8fed83a + 0332ac3 commit bd4980a

2 files changed

Lines changed: 74 additions & 113 deletions

File tree

algorithms/data_structures/bst.py

Lines changed: 72 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,108 @@
1+
"""Binary Search Tree implementation.
2+
3+
A BST is a node-based binary tree where each node's left subtree contains
4+
only nodes with data less than the node's data, and the right subtree
5+
contains only nodes with data greater than the node's data.
6+
7+
Operations and complexities (n = number of nodes):
8+
- insert: O(log n) average, O(n) worst case
9+
- search: O(log n) average, O(n) worst case
10+
- size: O(n)
11+
- preorder: O(n)
12+
- inorder: O(n)
13+
- postorder: O(n)
114
"""
2-
Implement Binary Search Tree. It has method:
3-
1. Insert
4-
2. Search
5-
3. Size
6-
4. Traversal (Preorder, Inorder, Postorder)
7-
"""
8-
9-
import unittest
15+
from __future__ import annotations
1016

1117

1218
class Node:
13-
def __init__(self, data):
14-
self.data = data
15-
self.left = None
16-
self.right = None
19+
def __init__(self, data: int) -> None:
20+
self.data: int = data
21+
self.left: Node | None = None
22+
self.right: Node | None = None
1723

1824

1925
class BST:
20-
def __init__(self):
21-
self.root = None
26+
def __init__(self) -> None:
27+
self.root: Node | None = None
2228

23-
def get_root(self):
29+
def get_root(self) -> Node | None:
2430
return self.root
2531

26-
"""
27-
Get the number of elements
28-
Using recursion. Complexity O(logN)
29-
"""
30-
31-
def size(self):
32-
return self.recur_size(self.root)
32+
def size(self) -> int:
33+
"""Return the number of nodes in the tree. Complexity: O(n)."""
34+
return self._recur_size(self.root)
3335

34-
def recur_size(self, root):
36+
def _recur_size(self, root: Node | None) -> int:
3537
if root is None:
3638
return 0
37-
else:
38-
return 1 + self.recur_size(root.left) + self.recur_size(root.right)
39-
40-
"""
41-
Search data in bst
42-
Using recursion. Complexity O(logN)
43-
"""
39+
return 1 + self._recur_size(root.left) + self._recur_size(root.right)
4440

45-
def search(self, data):
46-
return self.recur_search(self.root, data)
41+
def search(self, data: int) -> bool:
42+
"""Return True if data exists in the tree. Complexity: O(log n) average."""
43+
return self._recur_search(self.root, data)
4744

48-
def recur_search(self, root, data):
45+
def _recur_search(self, root: Node | None, data: int) -> bool:
4946
if root is None:
5047
return False
5148
if root.data == data:
5249
return True
53-
elif data > root.data: # Go to right root
54-
return self.recur_search(root.right, data)
55-
else: # Go to left root
56-
return self.recur_search(root.left, data)
50+
elif data > root.data:
51+
return self._recur_search(root.right, data)
52+
else:
53+
return self._recur_search(root.left, data)
5754

58-
"""
59-
Insert data in bst
60-
Using recursion. Complexity O(logN)
61-
"""
55+
def insert(self, data: int) -> bool:
56+
"""Insert data into the tree.
6257
63-
def insert(self, data):
58+
Return False if data already exists.
59+
Complexity: O(log n) average.
60+
"""
6461
if self.root:
65-
return self.recur_insert(self.root, data)
62+
return self._recur_insert(self.root, data)
6663
else:
6764
self.root = Node(data)
6865
return True
6966

70-
def recur_insert(self, root, data):
71-
if root.data == data: # The data is already there
67+
def _recur_insert(self, root: Node, data: int) -> bool:
68+
if root.data == data:
7269
return False
73-
elif data < root.data: # Go to left root
74-
if root.left: # If left root is a node
75-
return self.recur_insert(root.left, data)
76-
else: # left root is a None
70+
elif data < root.data:
71+
if root.left:
72+
return self._recur_insert(root.left, data)
73+
else:
7774
root.left = Node(data)
7875
return True
79-
else: # Go to right root
80-
if root.right: # If right root is a node
81-
return self.recur_insert(root.right, data)
76+
else:
77+
if root.right:
78+
return self._recur_insert(root.right, data)
8279
else:
8380
root.right = Node(data)
8481
return True
8582

86-
"""
87-
Preorder, Postorder, Inorder traversal bst
88-
"""
89-
90-
def preorder(self, root):
83+
def preorder(self, root: Node | None) -> list[int]:
84+
"""Return list of node values in preorder (root, left, right)."""
85+
result: list[int] = []
9186
if root:
92-
print(str(root.data), end=" ")
93-
self.preorder(root.left)
94-
self.preorder(root.right)
95-
96-
def inorder(self, root):
87+
result.append(root.data)
88+
result.extend(self.preorder(root.left))
89+
result.extend(self.preorder(root.right))
90+
return result
91+
92+
def inorder(self, root: Node | None) -> list[int]:
93+
"""Return list of node values in inorder (left, root, right)."""
94+
result: list[int] = []
9795
if root:
98-
self.inorder(root.left)
99-
print(str(root.data), end=" ")
100-
self.inorder(root.right)
101-
102-
def postorder(self, root):
96+
result.extend(self.inorder(root.left))
97+
result.append(root.data)
98+
result.extend(self.inorder(root.right))
99+
return result
100+
101+
def postorder(self, root: Node | None) -> list[int]:
102+
"""Return list of node values in postorder (left, right, root)."""
103+
result: list[int] = []
103104
if root:
104-
self.postorder(root.left)
105-
self.postorder(root.right)
106-
print(str(root.data), end=" ")
107-
108-
109-
"""
110-
The tree is created for testing:
111-
112-
10
113-
/ \
114-
6 15
115-
/ \\ / \
116-
4 9 12 24
117-
/ / \
118-
7 20 30
119-
/
120-
18
121-
"""
122-
123-
124-
class TestSuite(unittest.TestCase):
125-
def setUp(self):
126-
self.tree = BST()
127-
self.tree.insert(10)
128-
self.tree.insert(15)
129-
self.tree.insert(6)
130-
self.tree.insert(4)
131-
self.tree.insert(9)
132-
self.tree.insert(12)
133-
self.tree.insert(24)
134-
self.tree.insert(7)
135-
self.tree.insert(20)
136-
self.tree.insert(30)
137-
self.tree.insert(18)
138-
139-
def test_search(self):
140-
self.assertTrue(self.tree.search(24))
141-
self.assertFalse(self.tree.search(50))
142-
143-
def test_size(self):
144-
self.assertEqual(11, self.tree.size())
145-
146-
147-
if __name__ == "__main__":
148-
unittest.main()
105+
result.extend(self.postorder(root.left))
106+
result.extend(self.postorder(root.right))
107+
result.append(root.data)
108+
return result

tests/test_sorting.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
cycle_sort,
1212
exchange_sort,
1313
gnome_sort,
14+
insertion_sort,
1415
max_heap_sort,
1516
merge_sort,
1617
min_heap_sort,
@@ -60,7 +61,7 @@ def test_heap_sort(self):
6061
self.assertTrue(is_sorted(min_heap_sort([1, 3, 2, 5, 65, 23, 57, 1232])))
6162

6263
def test_insertion_sort(self):
63-
self.assertTrue(is_sorted(bitonic_sort([1, 3, 2, 5, 65, 23, 57, 1232])))
64+
self.assertTrue(is_sorted(insertion_sort([1, 3, 2, 5, 65, 23, 57, 1232])))
6465

6566
def test_merge_sort(self):
6667
self.assertTrue(is_sorted(merge_sort([1, 3, 2, 5, 65, 23, 57, 1232])))

0 commit comments

Comments
 (0)