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
1. TreeNode
, Binary Tree
& insert_node
: Setup
Introduced previously.
# 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) = None
root = TreeNode(50)
root = [25,75,10,33,56,89,4,11,30,40,52,61,82,95]
node_list =True) insert_node_list(root, node_list,show_outputs
t[25]|c[50]: Current[50] has NO left-child: INSERTED LEFT CurrentLeft[25]
t[75]|c[50]: Current[50] has NO right-child: INSERTED right Currentright[75]
t[10]|c[50]: Current[50] has ONE left-child : GO LEFT[25]
t[10]|c[25]: Current[25] has NO left-child: INSERTED LEFT CurrentLeft[10]
t[33]|c[50]: Current[50] has ONE left-child : GO LEFT[25]
t[33]|c[25]: Current[25] has NO right-child: INSERTED right Currentright[33]
t[56]|c[50]: Current[50] has ONE right-child : GO right[75]
t[56]|c[75]: Current[75] has NO left-child: INSERTED LEFT CurrentLeft[56]
t[89]|c[50]: Current[50] has ONE right-child : GO right[75]
t[89]|c[75]: Current[75] has NO right-child: INSERTED right Currentright[89]
t[4]|c[50]: Current[50] has ONE left-child : GO LEFT[25]
t[4]|c[25]: Current[25] has ONE left-child : GO LEFT[10]
t[4]|c[10]: Current[10] has NO left-child: INSERTED LEFT CurrentLeft[4]
t[11]|c[50]: Current[50] has ONE left-child : GO LEFT[25]
t[11]|c[25]: Current[25] has ONE left-child : GO LEFT[10]
t[11]|c[10]: Current[10] has NO right-child: INSERTED right Currentright[11]
t[30]|c[50]: Current[50] has ONE left-child : GO LEFT[25]
t[30]|c[25]: Current[25] has ONE right-child : GO right[33]
t[30]|c[33]: Current[33] has NO left-child: INSERTED LEFT CurrentLeft[30]
t[40]|c[50]: Current[50] has ONE left-child : GO LEFT[25]
t[40]|c[25]: Current[25] has ONE right-child : GO right[33]
t[40]|c[33]: Current[33] has NO right-child: INSERTED right Currentright[40]
t[52]|c[50]: Current[50] has ONE right-child : GO right[75]
t[52]|c[75]: Current[75] has ONE left-child : GO LEFT[56]
t[52]|c[56]: Current[56] has NO left-child: INSERTED LEFT CurrentLeft[52]
t[61]|c[50]: Current[50] has ONE right-child : GO right[75]
t[61]|c[75]: Current[75] has ONE left-child : GO LEFT[56]
t[61]|c[56]: Current[56] has NO right-child: INSERTED right Currentright[61]
t[82]|c[50]: Current[50] has ONE right-child : GO right[75]
t[82]|c[75]: Current[75] has ONE right-child : GO right[89]
t[82]|c[89]: Current[89] has NO left-child: INSERTED LEFT CurrentLeft[82]
t[95]|c[50]: Current[50] has ONE right-child : GO right[75]
t[95]|c[75]: Current[75] has ONE right-child : GO right[89]
t[95]|c[89]: Current[89] has NO right-child: INSERTED right Currentright[95]
2. Confirm Tree
# 50
# 25 75
# 10 33 56 89
# 4 11 30 40 52 61 82 95
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. delete_node
: 0 or 1 Child Nodes Only
def delete_node(root_node: TreeNode, target: int, parent_node=None):
= root_node
current_node if target == current_node.data:
# 2. delete_node - delete cases 1, 2 & 3.
print(f"[Part 1: Search] t[{target}]==c[{current_node.data}]: \t\t\t\tNode Found...")
print(f"[Part 2: Delete] t[{target}]==c[{current_node.data}]: \t\t\t\tDetermine Number of Children...")
if (not current_node.left and not current_node.right): # 0-kids
# Case A: Target has 0-kid --- [(0 left + 0 right)
# only 1 parent per node(p.left or p.right)
if parent_node.left: # if exists burn it
= None
parent_node.left elif parent_node.right:
= None
parent_node.right
print(f"[Part2A: Del_0Kid] t[{target}]==c[{current_node.data}]._0_kid: \t\tBurn'em!🔥[{target}], Parent[{parent_node.data}] will survive 🍀.")
return
elif (current_node.left and not current_node.right) or (not current_node.left and current_node.right):
# Case B: 1-kid --- [(1 left + 0 right) OR (0 left + 1 right)]
if current_node.left:
= current_node.left
target_sgl_child else:
= current_node.right
target_sgl_child
print(f"[Part2B: Del_1Kid] t[{target}]==c[{current_node.data}]._1_kid[{target_sgl_child.data}]: \tIs it Left or Right? kids_gramps[{parent_node.data}]")
if parent_node.left == current_node:
= target_sgl_child ##### 2BI REPLACE TARGET WITH TARGETS LEFT-CHILD
parent_node.left print(f"[Part2B: Del_1Kid] targets.child[{target_sgl_child.data}] has now assumed the identity of the target t[{target}|c{current_node.data}] and is the kid of it's (previous) gramps[{parent_node.data}]: aka now gramps.leftkid[{parent_node.left.data}]")
elif parent_node.right == current_node: # target can be a left or right child
= target_sgl_child ##### 2BII REPLACE TARGET WITH TARGETS LEFT-CHILD
parent_node.right print(f"[Part2B: Del_1Kid] targets.child[{target_sgl_child.data}] has now assumed the identity of the target t[{target}|c{current_node.data}] and is the kid of it's (previous) gramps[{parent_node.data}]: aka now gramps.rightkid[{parent_node.right.data}]")
return
else:
# Case C: 2-kid --- [(1 left + 1 right)
# [deleting_node]: 2KIDS,
# * replace [target] with [successor_node].
# The [s_node] is the [child_node]: least of all descendents that are greater than target
# how to: [target + all descendants in ascending order], [s_node] is next number after [target]
print(f"[Part2C: Del_2Kid] t[{target}]==c[{current_node.data}]._2_kids[{current_node.left.data},{current_node.right.data}]:\t\tGO RIGHT-CHILD[{current_node.right.data}]")
# 50
# 25 75
# 11 33 [56] 89
# [] [] 30 40 52 61 82 95
# STEP 1 FOR ALL NODES WITH 2 KIDS - GO RIGHT ONCE:
# Step 1: [GO-RIGHT] (for all scenarios)
= parent_node = current_node
target_node = current_node.right # target(aka current) always has 2_kids (right always exists)
current_node
# 50
# 25 75
# 11 33 [56] 89
# [] [] 30 40 52 61 82 95
# go left as possible til no leaf (or left child)
# while current_node:
while current_node:
if current_node.left:
print(f"[Part3A: 2K_SNodeLeft] t[{target}],c[{current_node.data}].left_child exists[{current_node.left.data}]: \tgo left...Parent[{parent_node.data}]")
=current_node
parent_node=current_node.left
current_nodeelif current_node.right: #no left + yes right (S_Node is Right)
print(f"[Part3B: 2K_SNodeRite] t[{target}],c[{current_node.data}].right_child exists [{current_node.right.data}]: \t do something...1...Parent[{parent_node.data}]")
print(f"expected c[{current_node.data}]: to [expected 52] with right[{current_node.right.data}] as [expected 55]")
# S_NODE HAS RIGHT_CHILD
# S_NODE replaces TARGET
# RIGHT_CHILDr replaces S_NODE
# insert_node_clean(root,55)
# [X]
# 25 75
# 11 33 61 89
# [] [] 30 40 {52} [] 82 95 CN: {52} -> BECOMES SNODE
# [55]
# (52)
# 25 75
# 11 33 61 89
# [] [] 30 40 (55) [] 82 95 CN: {52} -> BECOMES SNODE
# [X]
= current_node
s_node = s_node.data
target_node.data = s_node.right.data
current_node.data = None
current_node.right print(f"[Part3B: 2K_SNodeRite] t[{target}] replaced w' s_node[{target_node.data}], s_node[{target_node.data}] replaced w' right_child[{current_node.data}] ")
return s_node.data #
else:
print(f"[Part3C: 2K_SNodeLeaf] t[{target}],c[{current_node.data}].no_children): \tParent[{parent_node.data}]")
# 50
# 25 75
# 11 33 [X] 89
# [] [] 30 40 52 61 82 95
= current_node
s_node # delete target by simply replacing the data????
= s_node.data
target_node.data # remove pointer from parent to current_node
if parent_node.left == current_node:
print(f"[Part3C: 2K_SNodeLeaf] t[{target}],c[{current_node.data}].is_s_node[{s_node.data}]): replaces target[{target}] \tParent[{parent_node.data}].left[{parent_node.left.data}] to be deleted")
=None
parent_node.leftreturn
elif parent_node.right == current_node: # IF S_NODE IS RIGHT_CHILD WITH A CHILD, REPLACE ITSELF WITH THE CHILD
print(f"[Part3C: 2K_SNodeLeaf] t[{target}],c[{current_node.data}].is_s_node[{s_node.data}]): replaces target[{target}] \tParent[{parent_node.data}].right[{parent_node.right.data}] to be deleted")
=None
parent_node.rightreturn
return s_node.data # current is S_Node, target is deleted, s_node replaced target
# Step 2:[LEFT EXISTS]: keep_going_left -> ... -> [Leaf.F_left_child]:
# Step 2a: [LEAF F_right_child]: leaf is [s_node] -> replace [target]
# Step 2bi: [LEAF T_right_child]: leaf is [s_node] -> replace [target]
# Step 2bii: and leaf.right_child -> replace [leaf]
# test 1: c has two kids
# if current_node.left and current_node.right:
# print(f"[Part2C: Delete] t[{target}]==c[{current_node.data}]._2_kids: \t\twhat a shame! ☠️ [TBA IN FUTURE]")
# 1. search_node
if target < current_node.data: # go left
if current_node.left:
print(f"[Part 1: Search] t[{target}]<c[{current_node.data}].left_child exists[{current_node.left.data}]: \tgo left...")
= current_node
parent_node = current_node.left
current_node return delete_node(current_node, target,parent_node)
else:
print(f"[Part 1: Search] t[{target}]<c[{current_node.data}].left_child doesnt exists[{None}]: Node Not Found!")
return
else:
if current_node.right:
print(f"[Part 1: Search] t[{target}]>c[{current_node.data}].right_child exists[{current_node.right.data}]: \tgo right...")
= current_node
parent_node = current_node.right
current_node return delete_node(current_node, target,parent_node)
else:
print(f"[Part 1: Search] t[{target}]>c[{current_node.data}].right_child doesnt exists[{None}]: Node Not Found!")
return
4. Testing
= None
root = TreeNode(50)
root = [25,75,10,33,56,89,4,11,30,40,52,61,82,95]
node_list =False)
insert_node_list(root, node_list,show_outputs
4)
delete_node(root,# 50
# 25 75
# 10 33 56 89
# [X] 11 30 40 52 61 82 95
print()
10)
delete_node(root,# 50
# 25 75
# 11 33 56 89
# [] [X] 30 40 52 61 82 95
print()
56)
delete_node(root,# 50
# 25 75
# 11 33 [X] 89
# [] [] 30 40 52 61 82 95
print()
56)
delete_node(root,# 50
# 25 75
# 11 33 [61] 89
# [] [] 30 40 52 [X] 82 95
print(f"root.data[{root.data}]: expected[50]")
print(f"root.right.data[{root.right.data}]: expected[75]")
print(f"root.right.left.data[{root.right.left.data}]: expected[61]")
print(f"root.right.right.data[{root.right.right.data}]: expected[89]")
print(f"root.right.left.left.data[{root.right.left.left.data}]: expected[52]")
print(f"root.right.left.right.data[{root.right.left.right}]: expected[None]")
print(f"root.right.right.left.data[{root.right.right.left.data}]: expected[82]")
print(f"root.right.right.right.data[{root.right.right.right.data}]: expected[95]")
print()
print("INSERT 55 (RIGHT OF 52)")
55)
insert_node(root,# insert_node_clean(root,55)
# 50
# 25 75
# 11 33 61 89
# [] [] 30 40 52 [] 82 95
# [55]
print(root.right.left.left.right.data)
50)
delete_node(root,# insert_node_clean(root,55)
# 50
# 25 75
# 11 33 61 89
# [] [] 30 40 52 [] 82 95
# [55]
[Part 1: Search] t[4]<c[50].left_child exists[25]: go left...
[Part 1: Search] t[4]<c[25].left_child exists[10]: go left...
[Part 1: Search] t[4]<c[10].left_child exists[4]: go left...
[Part 1: Search] t[4]==c[4]: Node Found...
[Part 2: Delete] t[4]==c[4]: Determine Number of Children...
[Part2A: Del_0Kid] t[4]==c[4]._0_kid: Burn'em!🔥[4], Parent[10] will survive 🍀.
[Part 1: Search] t[10]<c[50].left_child exists[25]: go left...
[Part 1: Search] t[10]<c[25].left_child exists[10]: go left...
[Part 1: Search] t[10]==c[10]: Node Found...
[Part 2: Delete] t[10]==c[10]: Determine Number of Children...
[Part2B: Del_1Kid] t[10]==c[10]._1_kid[11]: Is it Left or Right? kids_gramps[25]
[Part2B: Del_1Kid] targets.child[11] has now assumed the identity of the target t[10|c10] and is the kid of it's (previous) gramps[25]: aka now gramps.leftkid[11]
[Part 1: Search] t[56]>c[50].right_child exists[75]: go right...
[Part 1: Search] t[56]<c[75].left_child exists[56]: go left...
[Part 1: Search] t[56]==c[56]: Node Found...
[Part 2: Delete] t[56]==c[56]: Determine Number of Children...
[Part2C: Del_2Kid] t[56]==c[56]._2_kids[52,61]: GO RIGHT-CHILD[61]
[Part3C: 2K_SNodeLeaf] t[56],c[61].no_children): Parent[56]
[Part3C: 2K_SNodeLeaf] t[56],c[61].is_s_node[61]): replaces target[56] Parent[61].right[61] to be deleted
[Part 1: Search] t[56]>c[50].right_child exists[75]: go right...
[Part 1: Search] t[56]<c[75].left_child exists[61]: go left...
[Part 1: Search] t[56]<c[61].left_child exists[52]: go left...
[Part 1: Search] t[56]>c[52].right_child doesnt exists[None]: Node Not Found!
root.data[50]: expected[50]
root.right.data[75]: expected[75]
root.right.left.data[61]: expected[61]
root.right.right.data[89]: expected[89]
root.right.left.left.data[52]: expected[52]
root.right.left.right.data[None]: expected[None]
root.right.right.left.data[82]: expected[82]
root.right.right.right.data[95]: expected[95]
INSERT 55 (RIGHT OF 52)
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[61]
t[55]|c[61]: Current[61] has ONE left-child : GO LEFT[52]
t[55]|c[52]: Current[52] has NO right-child: INSERTED right Currentright[55]
55
[Part 1: Search] t[50]==c[50]: Node Found...
[Part 2: Delete] t[50]==c[50]: Determine Number of Children...
[Part2C: Del_2Kid] t[50]==c[50]._2_kids[25,75]: GO RIGHT-CHILD[75]
[Part3A: 2K_SNodeLeft] t[50],c[75].left_child exists[61]: go left...Parent[50]
[Part3A: 2K_SNodeLeft] t[50],c[61].left_child exists[52]: go left...Parent[75]
[Part3B: 2K_SNodeRite] t[50],c[52].right_child exists [55]: do something...1...Parent[61]
expected c[52]: to [expected 52] with right[55] as [expected 55]
[Part3B: 2K_SNodeRite] t[50] replaced w' s_node[52], s_node[52] replaced w' right_child[55]
55