Skip to content

Commit 5e8a4bb

Browse files
committed
refactor
1 parent 753a299 commit 5e8a4bb

File tree

4 files changed

+823
-417
lines changed

4 files changed

+823
-417
lines changed

ElitePython/_modidx.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
'ElitePython/binary_trees.py'),
2525
'ElitePython.binary_trees.bfs': ( 'Data Structures/Binary Trees/binary_trees.html#bfs',
2626
'ElitePython/binary_trees.py'),
27+
'ElitePython.binary_trees.bst_insert': ( 'Data Structures/Binary '
28+
'Trees/binary_trees.html#bst_insert',
29+
'ElitePython/binary_trees.py'),
30+
'ElitePython.binary_trees.bst_search': ( 'Data Structures/Binary '
31+
'Trees/binary_trees.html#bst_search',
32+
'ElitePython/binary_trees.py'),
2733
'ElitePython.binary_trees.create_n_nodes': ( 'Data Structures/Binary '
2834
'Trees/binary_trees.html#create_n_nodes',
2935
'ElitePython/binary_trees.py'),

ElitePython/binary_trees.py

Lines changed: 88 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,96 @@
1-
"""Create and operate on Binary Trees"""
2-
31
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/Data Structures/Binary Trees/binary_trees.ipynb.
42

53
# %% auto 0
64
__all__ = ['visualize_tree', 'Node', 'set_parents', 'create_n_nodes', 'dfs_inorder', 'dfs_preorder', 'dfs_postorder', 'bfs',
7-
'insert_node', 'search_tree', 'delete_node']
5+
'insert_node', 'search_tree', 'delete_node', 'bst_insert', 'bst_search']
86

97
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 3
108
from collections import deque
119
import matplotlib.pyplot as plt
1210
import networkx as nx
1311
from typing import Callable, Generator
1412

15-
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 5
16-
def visualize_tree(root):
13+
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 12
14+
def visualize_tree(root, show_parents=False):
15+
"""Visualize a binary tree using NetworkX and Matplotlib.
16+
Args:
17+
root: The root node of the tree.
18+
show_parents: If True, include parent values in node labels.
19+
"""
1720
if not root:
1821
print("Empty tree")
1922
return
20-
23+
import math
2124
G = nx.DiGraph()
2225
pos = {}
2326
labels = {}
2427
node_id = 0
25-
28+
node_map = {}
2629
def add_nodes(node, x=0.0, y=0.0, layer=1):
2730
nonlocal node_id
2831
if node:
2932
current_id = node_id
33+
node_map[node] = current_id
3034
node_id += 1
3135
G.add_node(current_id)
3236
pos[current_id] = (x, y)
33-
labels[current_id] = str(node.value)
34-
37+
if show_parents and hasattr(node, 'parent') and node.parent:
38+
labels[current_id] = f"{node.value}\n(p:{node.parent.value})"
39+
else:
40+
labels[current_id] = str(node.value)
3541
if node.left:
3642
left_id = node_id
3743
G.add_edge(current_id, left_id)
38-
add_nodes(node.left, x - 1/layer, y - 1, layer + 1)
44+
add_nodes(node.left, x - 1/(layer+0.5), y - 1, layer + 1)
3945
if node.right:
4046
right_id = node_id
4147
G.add_edge(current_id, right_id)
42-
add_nodes(node.right, x + 1/layer, y - 1, layer + 1)
43-
48+
add_nodes(node.right, x + 1/(layer+0.5), y - 1, layer + 1)
4449
return current_id
45-
4650
add_nodes(root)
47-
48-
plt.figure(figsize=(3, 2))
49-
nx.draw(G, pos, labels=labels, with_labels=True, node_color='lightblue',
50-
node_size=1000, font_size=16, font_weight='bold', arrows=True)
51+
# Adaptive figure size
52+
depth = root.height if hasattr(root, 'height') else 3
53+
width = max(6, 2 ** (depth-1))
54+
plt.figure(figsize=(width, depth*2))
55+
# Highlight root
56+
node_colors = ["#ffcc00" if n == 0 else "#90caf9" for n in G.nodes]
57+
nx.draw(G, pos, labels=labels, with_labels=True, node_color=node_colors,
58+
node_size=1200, font_size=14, font_weight='bold', arrows=True, edge_color="#888888")
5159
plt.title("Binary Tree Visualization")
60+
plt.axis('off')
5261
plt.show()
5362

5463
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 14
5564
class Node:
56-
"""A node in a binary tree."""
57-
def __init__(self, key, left: "Node|None"=None, right: "Node|None"=None, parent: "Node|None" = None):
58-
self.value = key
65+
"""A node in a binary tree with parent pointer."""
66+
__slots__ = ['value', 'parent', 'left', 'right']
67+
68+
def __init__(self, value, left: "Node|None"=None, right: "Node|None"=None, parent: "Node|None"=None):
69+
self.value = value
5970
self.parent: Node|None = parent
6071
self.left: Node|None = left
6172
self.right: Node|None = right
6273

6374
def __str__(self) -> str:
64-
"""A string representation of the node including its value, left, and right children."""
75+
"""A string representation of the node including its value, left, right, and parent."""
6576
left_value = f"Node({self.left.value})" if self.left else None
6677
right_value = f"Node({self.right.value})" if self.right else None
6778
parent_value = f"Node({self.parent.value})" if self.parent else None
6879
return f"Node({self.value}, left={left_value}, right={right_value}, parent={parent_value})"
6980

7081
def __repr__(self) -> str:
71-
"""A representation of the node including its value, left, and right children."""
7282
return self.__str__()
7383

7484
@property
7585
def height(self) -> int:
86+
"""Returns the height of the node (longest path to a leaf)."""
7687
return 1 + max(
7788
(self.left.height if self.left else 0), (self.right.height if self.right else 0)
7889
)
7990

8091
@property
8192
def depth(self) -> int:
82-
return 1 + (self.parent.depth if self.parent else 0)
83-
93+
return 0 if self.parent is None else 1 + self.parent.depth
8494

8595
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 15
8696
def set_parents(node: Node, parent: Node|None=None) -> Node:
@@ -95,6 +105,7 @@ def set_parents(node: Node, parent: Node|None=None) -> Node:
95105

96106
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 21
97107
def create_n_nodes(num=5) -> Node:
108+
"""Creates a perfect binary tree with num nodes."""
98109
assert num > 0
99110
nodes = [Node(i) for i in range(num)]
100111
for i in range(num):
@@ -108,16 +119,9 @@ def create_n_nodes(num=5) -> Node:
108119
nodes[right_index].parent = nodes[i]
109120
return nodes[0] # root node
110121

111-
visualize_tree(create_n_nodes(1))
112-
visualize_tree(create_n_nodes(5))
113-
visualize_tree(create_n_nodes(9))
114-
115122
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 24
116123
def dfs_inorder(node: Node) -> Generator[Node, None, None]:
117-
"""Depth-first search.
118-
119-
Visits left subtree, then node, then right subtree
120-
"""
124+
"""Inorder DFS traversal: left, node, right."""
121125
if node.left:
122126
yield from dfs_inorder(node.left)
123127
yield node
@@ -128,10 +132,7 @@ def dfs_inorder(node: Node) -> Generator[Node, None, None]:
128132

129133
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 25
130134
def dfs_preorder(node: Node) -> Generator[Node, None, None]:
131-
"""Depth-first search.
132-
133-
Visits the node, then left subtree, then right subtree.
134-
"""
135+
"""Preorder DFS traversal: node, left, right."""
135136
yield node
136137
if node.left:
137138
yield from dfs_preorder(node.left)
@@ -141,10 +142,7 @@ def dfs_preorder(node: Node) -> Generator[Node, None, None]:
141142

142143
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 26
143144
def dfs_postorder(node: Node) -> Generator[Node, None, None]:
144-
"""Depth-first search.
145-
146-
Visits the left subtree, then right subtree, then the node
147-
"""
145+
"""Postorder DFS traversal: left, right, node."""
148146
if node.left:
149147
yield from dfs_postorder(node.left)
150148
if node.right:
@@ -154,7 +152,7 @@ def dfs_postorder(node: Node) -> Generator[Node, None, None]:
154152

155153
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 28
156154
def bfs(node: Node) -> Generator[Node, None, None]:
157-
"""Breadth-first search."""
155+
"""Breadth-first search (level order traversal)."""
158156
queue = deque([node])
159157
while queue:
160158
node = queue.popleft()
@@ -166,45 +164,40 @@ def bfs(node: Node) -> Generator[Node, None, None]:
166164
list(bfs(root))
167165

168166
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 30
169-
def insert_node(root: Node, key):
170-
""""""
167+
def insert_node(root: Node, value):
168+
"""Level-order BFS insert. Inserts a new node with the given value."""
171169
queue = deque([root])
172170
while queue:
173171
node = queue.popleft()
174172
if node.left:
175173
queue.append(node.left)
176174
else:
177-
node.left = Node(key=key, left=None, right=None, parent=node)
175+
node.left = Node(value=value, left=None, right=None, parent=node)
178176
break
179-
180177
if node.right:
181178
queue.append(node.right)
182179
else:
183-
node.right = Node(key=key, left=None, right=None, parent=node)
180+
node.right = Node(value=value, left=None, right=None, parent=node)
184181
break
185-
186182

187183
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 33
188-
def search_tree(root: Node, key, traversal_func: Callable[[Node], Generator[Node, None, None]]) -> Node|None:
184+
def search_tree(root: Node, value, traversal_func: Callable[[Node], Generator[Node, None, None]]) -> Node|None:
185+
"""Search for a node with the given value using the specified traversal function."""
189186
for node in traversal_func(root):
190-
if node.value == key:
187+
if node.value == value:
191188
return node
192189
else:
193190
return None
194191

195192
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 36
196-
def delete_node(root: Node, key=None) -> Node|None:
197-
"""Uses BFS to delete the node with a key and swap it with the right-most node."""
198-
193+
def delete_node(root: Node, value=None) -> Node|None:
194+
"""Deletes the node with the given value by swapping with the right-most node."""
199195
# if root has no children
200-
if root.left or root.right:
201-
pass
202-
else:
196+
if not (root.left or root.right):
203197
del root
204198
return None
205-
206-
# find the deepest root node and its parent
207-
queue: deque[tuple[Node, Node|None]] = deque([(root, None)]) # root has no parent
199+
# find the deepest node and its parent
200+
queue: deque[tuple[Node, Node|None]] = deque([(root, None)])
208201
last_parent = None
209202
last_node = None
210203
while queue:
@@ -213,7 +206,6 @@ def delete_node(root: Node, key=None) -> Node|None:
213206
queue.append((last_node.left, last_node))
214207
if last_node.right:
215208
queue.append((last_node.right, last_node))
216-
217209
# delete the leaf node
218210
if last_parent:
219211
if last_parent.right:
@@ -222,26 +214,22 @@ def delete_node(root: Node, key=None) -> Node|None:
222214
last_parent.left = None
223215
else:
224216
raise NotImplementedError("Parent of right-most node not found.")
225-
226-
# find the node with value == key
227-
if last_node and root.value == key:
217+
# find the node with value == value
218+
if last_node and root.value == value:
228219
last_node.left = root.left
229220
last_node.right = root.right
230221
del root
231222
return last_node
232-
elif last_node:
233-
pass
234-
else:
223+
elif not last_node:
235224
raise NotImplementedError
236-
237225
target_queue: deque[Node] = deque([root])
238226
while target_queue:
239227
node = target_queue.popleft()
240228
if node.left:
241229
if node.left is last_node:
242230
node.left = None
243231
return root
244-
elif node.left.value == key:
232+
elif node.left.value == value:
245233
node.left = last_node
246234
last_node.parent = node
247235
return root
@@ -250,9 +238,41 @@ def delete_node(root: Node, key=None) -> Node|None:
250238
if node.right is last_node:
251239
node.right = None
252240
return root
253-
elif node.right.value == key:
241+
elif node.right.value == value:
254242
node.right = last_node
255243
last_node.parent = node
256244
return root
257245
target_queue.append(node.right)
258246
return root
247+
248+
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 41
249+
def bst_insert(root: Node, new_node: Node) -> None:
250+
"""Insert a new node into a BST. Raises an exception if value exists."""
251+
if new_node.value < root.value:
252+
if root.left:
253+
bst_insert(root.left, new_node)
254+
else:
255+
root.left = new_node
256+
new_node.parent = root
257+
return
258+
elif root.value < new_node.value:
259+
if root.right:
260+
bst_insert(root.right, new_node)
261+
else:
262+
root.right = new_node
263+
new_node.parent = root
264+
return
265+
else:
266+
raise Exception(f"Value {root.value} already exists in this tree.")
267+
268+
# %% ../nbs/Data Structures/Binary Trees/binary_trees.ipynb 45
269+
def bst_search(root: Node|None, key) -> Node:
270+
"""Search for a key in a BST. Raises an exception if not found."""
271+
if root.value == key:
272+
return root
273+
elif key < root.value and root.left:
274+
return bst_search(root.left, key)
275+
elif root.right:
276+
return bst_search(root.right, key)
277+
else:
278+
raise Exception(f"Key {key} not found")

0 commit comments

Comments
 (0)