|
| 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) |
1 | 14 | """ |
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 |
10 | 16 |
|
11 | 17 |
|
12 | 18 | 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 |
17 | 23 |
|
18 | 24 |
|
19 | 25 | class BST: |
20 | | - def __init__(self): |
21 | | - self.root = None |
| 26 | + def __init__(self) -> None: |
| 27 | + self.root: Node | None = None |
22 | 28 |
|
23 | | - def get_root(self): |
| 29 | + def get_root(self) -> Node | None: |
24 | 30 | return self.root |
25 | 31 |
|
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) |
33 | 35 |
|
34 | | - def recur_size(self, root): |
| 36 | + def _recur_size(self, root: Node | None) -> int: |
35 | 37 | if root is None: |
36 | 38 | 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) |
44 | 40 |
|
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) |
47 | 44 |
|
48 | | - def recur_search(self, root, data): |
| 45 | + def _recur_search(self, root: Node | None, data: int) -> bool: |
49 | 46 | if root is None: |
50 | 47 | return False |
51 | 48 | if root.data == data: |
52 | 49 | 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) |
57 | 54 |
|
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. |
62 | 57 |
|
63 | | - def insert(self, data): |
| 58 | + Return False if data already exists. |
| 59 | + Complexity: O(log n) average. |
| 60 | + """ |
64 | 61 | if self.root: |
65 | | - return self.recur_insert(self.root, data) |
| 62 | + return self._recur_insert(self.root, data) |
66 | 63 | else: |
67 | 64 | self.root = Node(data) |
68 | 65 | return True |
69 | 66 |
|
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: |
72 | 69 | 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: |
77 | 74 | root.left = Node(data) |
78 | 75 | 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) |
82 | 79 | else: |
83 | 80 | root.right = Node(data) |
84 | 81 | return True |
85 | 82 |
|
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] = [] |
91 | 86 | 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] = [] |
97 | 95 | 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] = [] |
103 | 104 | 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 |
0 commit comments