class TreeNode:
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def insert_node(root_node: TreeNode, target: int):
= root_node
current_node if target == current_node.data:
print(f"t[{target}]|c[{current_node.data}]: \t\tCannot insert duplicates")
return
if target < current_node.data: # left
if current_node.left: # go left-child: if exists
print(f"t[{target}]|c[{current_node.data}]: \t\tCurrent[{current_node.data}] has ONE left-child : GO LEFT[{current_node.left.data}]")
=current_node.left
current_nodereturn insert_node(current_node, target)
else: # insert left-child: if not exist
= TreeNode(target)
current_node.left print(f"t[{target}]|c[{current_node.data}]: \t\tCurrent[{current_node.data}] has NO left-child: INSERTED LEFT CurrentLeft[{current_node.left.data}]")
return current_node.left
else:
if current_node.right: # go right-child: if exists
print(f"t[{target}]|c[{current_node.data}]: \t\tCurrent[{current_node.data}] has ONE right-child : GO right[{current_node.right.data}]")
=current_node.right
current_nodereturn insert_node(current_node, target)
else: # insert right-child: if not exist
= TreeNode(target)
current_node.right print(f"t[{target}]|c[{current_node.data}]: \t\tCurrent[{current_node.data}] has NO right-child: INSERTED right Currentright[{current_node.right.data}]")
pass
def insert_node_clean(root_node: TreeNode, target: int):
= root_node
current_node if target == current_node.data:
return
if target < current_node.data: # left
if current_node.left: # go left-child: if exists
=current_node.left
current_nodereturn insert_node_clean(current_node, target)
else: # insert left-child: if not exist
= TreeNode(target)
current_node.left return current_node.left
else:
if current_node.right: # go right-child: if exists
=current_node.right
current_nodereturn insert_node_clean(current_node, target)
else: # insert right-child: if not exist
= TreeNode(target)
current_node.right pass
# create a basic tree
# 50
# 25 75
# 10 33 56 89
# 4 11 30 40 52 61 82 95
def insert_node_list(root_node:TreeNode, node_list: list[int], show_outputs:bool=True):
for node in node_list:
if show_outputs:
insert_node(root_node, node) else:
insert_node_clean(root_node, node)
def create_tree_from_root_node(root_node = TreeNode(50),node_list=[25,75,10,33,56,89,4,11,30,40,52,61,82,95]):
=False)
insert_node_list(root_node, node_list,show_outputs
1. TreeNode
, Binary Tree
& insert_node
: Setup
Introduced previously.
2. Confirm Tree
# 50
# 25 75
# 10 33 56 89
# 4 11 30 40 52 61 82 95
= TreeNode(50)
root
create_tree_from_root_node(root)print(f"[L1] root.data: \t\t\t[{root.data}] (expected: 50)")
print()
print(f"[L2] root.left.data: \t\t\t[{root.left.data}] (expected: 25)")
print(f"[L2] root.right.data: \t\t\t[{root.right.data}] (expected: 75)")
print()
print(f"[L3] root.left.left.data: \t\t[{root.left.left.data}] (expected: 10)")
print(f"[L3] root.left.right.data: \t\t[{root.left.right.data}] (expected: 33)")
print(f"[L3] root.right.left.data: \t\t[{root.right.left.data}] (expected: 56)")
print(f"[L3] root.right.right.data: \t\t[{root.right.right.data}] (expected: 89)")
print()
print(f"[L4] root.left.left.left.data: \t\t[{root.left.left.left.data}] (expected: 4)")
print(f"[L4] root.left.left.right.data: \t[{root.left.left.right.data}] (expected: 11)")
print(f"[L4] root.left.right.left.data: \t[{root.left.right.left.data}] (expected: 30)")
print(f"[L4] root.left.right.right.data: \t[{root.left.right.right.data}] (expected: 40)")
print()
print(f"[L4] root.right.left.left.data: \t[{root.right.left.left.data}] (expected: 52)")
print(f"[L4] root.right.left.right.data: \t[{root.right.left.right.data}] (expected: 61)")
print(f"[L4] root.right.right.left.data: \t[{root.right.right.left.data}] (expected: 82)")
print(f"[L4] root.right.right.right.data:\t[{root.right.right.right.data}] (expected: 95)")
[L1] root.data: [50] (expected: 50)
[L2] root.left.data: [25] (expected: 25)
[L2] root.right.data: [75] (expected: 75)
[L3] root.left.left.data: [10] (expected: 10)
[L3] root.left.right.data: [33] (expected: 33)
[L3] root.right.left.data: [56] (expected: 56)
[L3] root.right.right.data: [89] (expected: 89)
[L4] root.left.left.left.data: [4] (expected: 4)
[L4] root.left.left.right.data: [11] (expected: 11)
[L4] root.left.right.left.data: [30] (expected: 30)
[L4] root.left.right.right.data: [40] (expected: 40)
[L4] root.right.left.left.data: [52] (expected: 52)
[L4] root.right.left.right.data: [61] (expected: 61)
[L4] root.right.right.left.data: [82] (expected: 82)
[L4] root.right.right.right.data: [95] (expected: 95)
3. find_successor_node
def find_successor_node(target_node: TreeNode):
= target_node.right # go right
successor_node print(f"t{target_node.data}.tr[{successor_node.data}]: go right...{successor_node.data}")
if not successor_node.left:
# [75] to succeed [50]
if successor_node.right:
print(f"s{successor_node.data} has no left kids: s will t[{target_node.data}], t.right -> s.right[{successor_node.right.data}]")
= successor_node.data # no need to update targets parents because only replacing data not nodes
target_node.data = successor_node.right
target_node.right else:
= successor_node.data
target_node.data print(f"s{successor_node.data} has no left kids: s will succeed t[{target_node.data}], t.right -> s.right[{None}]")
pass
while successor_node.left:
print(f"s{successor_node.data}.l[{successor_node.left.data}]: s has a left_child, go left...")
= successor_node
parent_node = successor_node.left
successor_node
print(f"s{successor_node.data}: s no further left_childs, check is s has a right_child...")
# successor_node is not left_most child
# 1. has no_right_child: no further action
# i. parent_left = None instead of parent_left = s_node (removing s_node to replace target_node)
# ii target.data = s_node.data
if not successor_node.right:
print(f"s{successor_node.data}: s has 0 right_child...p[{parent_node.data}] from p.left[{parent_node.left.data}] -> p.left[{None}]")
= None
parent_node.left
if successor_node.right:
print(f"s{successor_node.data}: s has 1 right_child...p[{parent_node.data}] from p.left[{parent_node.left.data}] -> s[{successor_node.data}].right[{successor_node.right.data}]")
# print(f"s{successor_node.data}: succesor has 1 right_child...parent.left -> successors.right[{successor_node.right.data}]")
= successor_node.right
parent_node.left
# 2. has snode has_right_child
# i. parent_left = snode_right_child
# ii target.data = s_node.data
print(f"s{successor_node.data} to succeed t[{target_node.data}]")
= successor_node.data
target_node.data return successor_node
# NO RIGHT CHILD
# [t50]
# 25 [s75]
# NO RIGHT CHILD
# [s75]
# 25 []
# RIGHT CHILD
# [t50]
# 25 [s75]
# [sc80]
# RIGHT CHILD
# [s75]
# 25 [sc80]
# [scc81]
4. delete_node
def delete_node(root_node: TreeNode, target: int):
= root_node
current_node = None
parent_node = None
target_node while current_node:
if target == current_node.data:
= current_node
target_node print(f"t{target}==c{current_node.data}: target found! [Determine Children Count]...")
break
elif target < current_node.data: # go left
print(f"t{target}<-c{current_node.data}: go left...")
= current_node
parent_node = current_node.left
current_node else: # go right
print(f"t{target}->c{current_node.data}: go right...")
= current_node
parent_node = current_node.right
current_node if not target_node:
print(f"t{target}!=p{parent_node.data}: target not found because node has no more children!")
return
if target_node.left and target_node.right:
print(f"t{target}.lr[{target_node.left.data},{target_node.right.data}]: target has two kids! [Find Successor Node]...")
= find_successor_node(target_node)
s_node return
else: # 0 or 1 kid
= (target_node.left or target_node.right)
targets_child print(f"t[{target}]: target has 0 or 1 children! [Find Successor Node]...")
if not parent_node: # root node has no parents: replace parent (and its kids) with child (and its kids)
print(f"t{target}.tc[{targets_child.data}]:tc[{targets_child.data}] succeeds t[{target}], no parents ptrs to update")
= targets_child.data
target_node.data = targets_child.left
target_node.left = targets_child.right
target_node.right elif parent_node.left == target_node:
if targets_child:
print(f"t{target}.tc[{targets_child.data}]:tc[{targets_child.data}] succeeds t[{target}], p[{parent_node.data}]l[{parent_node.left.data}] updated to point to tc[{targets_child.data}]")
= targets_child
parent_node.left print(f"t{target}.tc[{targets_child.data}]:final result p[{parent_node.data}]l[{parent_node.left.data}]")
else:
print(f"t{target} has no kids: {target} deleted, parent_node.left pointing to [None]")
= targets_child
parent_node.left return
elif parent_node.right == target_node:
if targets_child:
print(f"t{target}.tc[{targets_child.data}]:tc[{targets_child.data}] succeeds t[{target}], p[{parent_node.data}]r[{parent_node.right.data}] updated to point to tc[{targets_child.data}]")
= targets_child
parent_node.right print(f"t{target}.tc[{targets_child.data}]:final result p[{parent_node.data}]r[{parent_node.right.data}]")
else:
print(f"t{target} has no kids: {target} deleted, parent_node.right pointing to [None]")
= targets_child
parent_node.right
return
return
# 50
# 25 75
# 10 33 56 89
# 4 11 30 40 52 61 82 95
= None
root = TreeNode(50)
root
create_tree_from_root_node(root)50)
delete_node(root, # delete_node(root, 10)
t50==c50: target found! [Determine Children Count]...
t50.lr[25,75]: target has two kids! [Find Successor Node]...
t50.tr[75]: go right...75
s75.l[56]: s has a left_child, go left...
s56.l[52]: s has a left_child, go left...
s52: s no further left_childs, check is s has a right_child...
s52: s has 0 right_child...p[56] from p.left[52] -> p.left[None]
s52 to succeed t[50]
= None
root = TreeNode(50)
root =[25])
create_tree_from_root_node(root, node_list50)
delete_node(root,
root.data
t50==c50: target found! [Determine Children Count]...
t[50]: target has 0 or 1 children! [Find Successor Node]...
t50.tc[25]:tc[25] succeeds t[50], no parents ptrs to update
25
= None
root = TreeNode(50)
root =[25,10])
create_tree_from_root_node(root, node_list25) delete_node(root,
t25<-c50: go left...
t25==c25: target found! [Determine Children Count]...
t[25]: target has 0 or 1 children! [Find Successor Node]...
t25.tc[10]:tc[10] succeeds t[25], p[50]l[25] updated to point to tc[10]
t25.tc[10]:final result p[50]l[10]
= None
root = TreeNode(50)
root =[51,52])
create_tree_from_root_node(root, node_list51) delete_node(root,
t51->c50: go right...
t51==c51: target found! [Determine Children Count]...
t[51]: target has 0 or 1 children! [Find Successor Node]...
t51.tc[52]:tc[52] succeeds t[51], p[50]r[51] updated to point to tc[52]
t51.tc[52]:final result p[50]r[52]
= None
root = TreeNode(50)
root
create_tree_from_root_node(root)4) delete_node(root,
t4<-c50: go left...
t4<-c25: go left...
t4<-c10: go left...
t4==c4: target found! [Determine Children Count]...
t[4]: target has 0 or 1 children! [Find Successor Node]...
t4 has no kids: 4 deleted, parent_node.left pointing to [None]
= None
root = TreeNode(50)
root =[25,75])
create_tree_from_root_node(root, node_list75)
delete_node(root,
# NO RIGHT CHILD
# [t50]
# 25 [s75]
# NO RIGHT CHILD
# [s75]
# 25 []
t75->c50: go right...
t75==c75: target found! [Determine Children Count]...
t[75]: target has 0 or 1 children! [Find Successor Node]...
t75 has no kids: 75 deleted, parent_node.right pointing to [None]
# RIGHT CHILD
# [t50]
# 25 [s75]
# [sc80]
# RIGHT CHILD
# [s75]
# 25 [sc80]
# [scc81]
# [50]X
# 25 [75]
# 10 33 [56] 89
# 4 11 30 40 [52] 61 82 95
= None
root = TreeNode(50)
root
create_tree_from_root_node(root)50)
delete_node(root,
# [52]
# 25 [75]
# 10 33 [56] 89
# 4 11 30 40 [] 61 82 95
print(root.data) # expected 52
print(root.right.left.data) # expected 56
print(root.right.left.left.data) # expected error
t50==c50: target found! [Determine Children Count]...
t50.lr[25,75]: target has two kids! [Find Successor Node]...
t50.tr[75]: go right...75
s75.l[56]: s has a left_child, go left...
s56.l[52]: s has a left_child, go left...
s52: s no further left_childs, check is s has a right_child...
s52: s has 0 right_child...p[56] from p.left[52] -> p.left[None]
s52 to succeed t[50]
52
56
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[20], line 18 16 print(root.data) # expected 52 17 print(root.right.left.data) # expected 56 ---> 18 print(root.right.left.left.data) # expected error AttributeError: 'NoneType' object has no attribute 'data'
# [50]X
# 25 [75]
# 10 33 [56] 89
# 4 11 30 40 [52] 61 82 95
# [55]
= None
root = TreeNode(50)
root
create_tree_from_root_node(root)55)
insert_node(root, 50)
delete_node(root,
# expected 55
root.right.left.left.data
# [52]
# 25 [75]
# 10 33 [56] 89
# 4 11 30 40 [55] 61 82 95
# []
t[55]|c[50]: Current[50] has ONE right-child : GO right[75]
t[55]|c[75]: Current[75] has ONE left-child : GO LEFT[56]
t[55]|c[56]: Current[56] has ONE left-child : GO LEFT[52]
t[55]|c[52]: Current[52] has NO right-child: INSERTED right Currentright[55]
t50==c50: target found! [Determine Children Count]...
t50.lr[25,75]: target has two kids! [Find Successor Node]...
t50.tr[75]: go right...75
s75.l[56]: s has a left_child, go left...
s56.l[52]: s has a left_child, go left...
s52: s no further left_childs, check is s has a right_child...
s52: s has 1 right_child...p[56] from p.left[52] -> s[52].right[55]
s52 to succeed t[50]
55