数组、链表和树存储方式分析
对于树结构,不论是查找修改还是增加删除,效率都比较高,结合了链表和数组的优点,如以下的二叉树:
1、数组的第一个元素作为第一个节点
2、数组的第二个元素3比7小,放在7的左边
3、数组的第三个元素10比7大,放在7的右边
4、数组的第四个元素1比7小,也比3小,放在3的左边
5、数组的第五个元素5比7小,但比3大,放在3的右边
6、数组的第六个元素9比7大,但比10小,放在10的左边
7、数组的第七个元素12比7大,比10大,放在10的右边
二叉树的前中后序遍历
思路分析
代码实现
# 创建 HeroNode 节点
class HeroNode:
def __init__(self, no: int, name: str):
self.no = no
self.name = name
self.left = None
self.right = None
def __str__(self):
return f"no={self.no}, name={self.name}"
# 前序遍历
def pre_order(self):
# 先输出父节点
print(self, end=' => ')
# 左子树不为空则递归左子树
if self.left is not None:
self.left.pre_order()
# 右子树不为空则递归右子树
if self.right is not None:
self.right.pre_order()
# 中序遍历
def infix_order(self):
# 左子树不为空则递归左子树
if self.left is not None:
self.left.infix_order()
# 输出父节点
print(self, end=' => ')
# 右子树不为空则递归右子树
if self.right is not None:
self.right.infix_order()
# 后序遍历
def post_order(self):
# 左子树不为空则递归左子树
if self.left is not None:
self.left.post_order()
# 右子树不为空则递归右子树
if self.right is not None:
self.right.post_order()
# 输出父节点
print(self, end=' => ')
# 建立 HeroNode 二叉树
class BinaryTree:
root: HeroNode = None
# 前序遍历
def pre_order(self):
if self.root is not None:
self.root.pre_order()
else:
print("二叉树为空...")
# 前序遍历
def infix_order(self):
if self.root is not None:
self.root.infix_order()
else:
print("二叉树为空...")
# 前序遍历
def post_order(self):
if self.root is not None:
self.root.post_order()
else:
print("二叉树为空...")
def test_binary_tree():
# 手动创建二叉树
binary_tree = BinaryTree()
root = HeroNode(1, '宋江')
node2 = HeroNode(2, '吴用')
node3 = HeroNode(3, '卢俊义')
node4 = HeroNode(4, '林冲')
root.left = node2
root.right = node3
node3.right = node4
binary_tree.root = root
# 前序
print("前序遍历:", end=" ")
binary_tree.pre_order()
print()
# 中序
print("中序遍历:", end=" ")
binary_tree.infix_order()
print()
# 后序
print("后序遍历:", end=" ")
binary_tree.post_order()
print()
test_binary_tree()
二叉树的前中后序查找
思路分析
代码实现
""" 前中后序遍历查找 """
# 创建 HeroNode 节点
class HeroNode:
def __init__(self, no: int, name: str):
self.no = no
self.name = name
self.left = None
self.right = None
def __str__(self):
return f"no={self.no}, name={self.name}"
# 前序遍历查找
def pre_order_search(self, no: int):
"""
前序遍历查找某个节点
:param no: 要查找节点的 id
:return: 找到的 HeroNode 节点或 None
"""
print("进入前序遍历...")
if self.no == no: # 如果当前节点是,直接返回
return self
find_node = None
# 如果左子节点不为空,则向左子节点继续递归查找
# 找到则返回,找不到则看右子节点
if self.left is not None:
find_node = self.left.pre_order_search(no)
if find_node: # 说明在左子树上找到,直接返回
return find_node
# 否则判断右子节点,若不为空,向右子树递归查找
if self.right is not None:
find_node = self.right.pre_order_search(no)
# 如果右子树找到,则find_node有值,否则find_node为空
return find_node
# 中序遍历查找
def infix_order_search(self, no: int):
"""
中序遍历查找某个节点
:param no: 要查找节点的 id
:return: 找到的 HeroNode 节点或 None
"""
find_node = None
# 如果左子节点不为空,则向左子节点继续递归查找
# 找到则返回,找不到则看右子节点
if self.left is not None:
find_node = self.left.infix_order_search(no)
if find_node: # 说明在左子树上找到,直接返回
return find_node
print("进入中序遍历...")
if self.no == no: # 如果当前节点是,直接返回
return self
# 否则判断右子节点,若不为空,向右子树递归查找
if self.right is not None:
find_node = self.right.infix_order_search(no)
# 如果右子树找到,则find_node有值,否则find_node为空
return find_node
# 后序遍历查找
def post_order_search(self, no: int):
"""
后序遍历查找某个节点
:param no: 要查找节点的 id
:return: 找到的 HeroNode 节点或 None
"""
find_node = None
# 如果左子节点不为空,则向左子节点继续递归查找
# 找到则返回,找不到则看右子节点
if self.left is not None:
find_node = self.left.post_order_search(no)
if find_node: # 说明在左子树上找到,直接返回
return find_node
# 否则判断右子节点,若不为空,向右子树递归查找
if self.right is not None:
find_node = self.right.post_order_search(no)
# 如果右子树找到,则find_node有值,否则find_node为空
if find_node:
return find_node
print("进入后序遍历...")
if self.no == no: # 如果当前节点是,直接返回
return self
return None
# 建立 HeroNode 二叉树
class BinaryTree:
root: HeroNode = None
# 前序查找
def pre_order_search(self, no):
if self.root is not None:
return self.root.pre_order_search(no)
else:
return None
# 中序查找
def infix_order_search(self, no):
if self.root is not None:
return self.root.infix_order_search(no)
else:
return None
# 后序查找
def post_order_search(self, no):
if self.root is not None:
return self.root.post_order_search(no)
else:
return None
def test_binary_tree():
# 手动创建二叉树
binary_tree = BinaryTree()
root = HeroNode(1, '宋江')
node2 = HeroNode(2, '吴用')
node3 = HeroNode(3, '卢俊义')
node4 = HeroNode(4, '林冲')
node5 = HeroNode(5, "关胜")
root.left = node2
root.right = node3
node3.right = node4
node3.left = node5
binary_tree.root = root
print("前序遍历查找:") # 比较了4次(看输出得到的结果)
res = binary_tree.pre_order_search(5)
if res:
print("找到了,节点信息为:", res)
else:
print("找不到编号为5的节点")
print()
print("中序遍历查找:") # 比较了3次(看输出得到的结果)
res = binary_tree.infix_order_search(5)
if res:
print("找到了,节点信息为:", res)
else:
print("找不到编号为5的节点")
print()
print("后序遍历查找:") # 比较了2次(看输出得到的结果)
res = binary_tree.post_order_search(5)
if res:
print("找到了,节点信息为:", res)
else:
print("找不到编号为5的节点")
test_binary_tree()
二叉树删除节点
思路分析
代码实现
""" 递归删除二叉树节点 """
# HeroNode 节点
class HeroNode:
def __init__(self, no: int, name: str):
self.no = no
self.name = name
self.left = None
self.right = None
def __str__(self):
return f"no={self.no}, name={self.name}"
# 前序遍历
def pre_order(self):
# 先输出父节点
print(self, end=' => ')
# 左子树不为空则递归左子树
if self.left is not None:
self.left.pre_order()
# 右子树不为空则递归右子树
if self.right is not None:
self.right.pre_order()
def delete_node(self, no: int):
"""
递归删除节点规则:
如果删除的节点是叶子节点,则直接删除
如果删除的节点不是叶子节点,则删除该节点及其左右子树
:param no: 要删除的节点编号
:return:
"""
# 如果左子节点不为空,且左子节点是要删除的节点,则删除左子节点及其子树,并返回
if self.left and self.left.no == no:
self.left = None # 相当于删除左子节点及其子树
return
# 如果右子节点不为空,且右子节点是要删除的节点,则删除右子节点及其子树,并返回
if self.right and self.right.no == no:
self.right = None # 相当于删除右子节点及其子树
return
# 如果左右子节点都不是要删除的节点,则首先向左子节点递归删除
if self.left:
self.left.delete_node(no)
# 如果递归完左子节点还没找到要删除的节点,则继续向右子节点递归删除
if self.right:
self.right.delete_node(no)
# 建立 HeroNode 二叉树
class BinaryTree:
root: HeroNode = None
# 前序遍历
def pre_order(self):
if self.root is not None:
self.root.pre_order()
else:
print("二叉树为空...")
# 删除树节点
def delete_node(self, no: int):
if self.root:
if self.root.no == no: # 如果根节点是要删除的节点
self.root = None # 则把根节点置空
else:
self.root.delete_node(no)
else:
print("树空,不能删除节点...")
def test_binary_tree():
# 手动创建二叉树
binary_tree = BinaryTree()
root = HeroNode(1, '宋江')
node2 = HeroNode(2, '吴用')
node3 = HeroNode(3, '卢俊义')
node4 = HeroNode(4, '林冲')
node5 = HeroNode(5, "关胜")
root.left = node2
root.right = node3
node3.right = node4
node3.left = node5
binary_tree.root = root
print("删除前:", end='')
binary_tree.pre_order()
print()
binary_tree.delete_node(5)
print("删除后:", end='')
binary_tree.pre_order()
print()
test_binary_tree()
顺序存储二叉树
思路分析
代码实现
需求如下:
"""顺序存储二叉树"""
# 顺序存储二叉树就是用数组结构存储二叉树节点数据,
# 要求用数组存储后,可以对该数组进行前序遍历、中序遍历、后序遍历
# 其实就是将二叉树从左到右,从上到下的节点依次存入数组中,如下:
"""
假设有如下一棵二叉树,它对应的顺序存储就是:arr = [1, 2, 3, 4, 5, 6, 7]
1
2 3
4 5 6 7
"""
class ArrayBinaryTree:
def __init__(self, arr):
self.arr = arr
def pre_order(self, index: int):
"""
以前序遍历方式访问数组元素
:param index: 数组下标
:return:
"""
n = len(self.arr) # n为数组长度
if self.arr and n > 0:
# 输出当前元素
print(f"arr[{index}]={self.arr[index]}", end=" ")
# 向左子树递归
if 2 * index + 1 < n:
self.pre_order(2 * index + 1)
# 向右子树递归
if 2 * index + 2 < n:
self.pre_order(2 * index + 2)
else:
print("数组为空!")
def infix_order(self, index: int):
"""
以中序遍历方式访问数组元素
:param index: 数组下标
:return:
"""
n = len(self.arr)
if self.arr and n > 0:
if 2 * index + 1 < n: # 向左子树递归
self.infix_order(2 * index + 1)
print(f"arr[{index}]={self.arr[index]}", end=" ")
if 2 * index + 2 < n: # 向右子树递归
self.infix_order(2 * index + 2)
else:
print("数组为空")
def post_order(self, index: int):
"""
以后序遍历方式访问数组元素
:param index: 数组下标
:return:
"""
n = len(self.arr)
if self.arr and n > 0:
if 2 * index + 1 < n: # 向左子树递归
self.post_order(2 * index + 1)
if 2 * index + 2 < n: # 向右子树递归
self.post_order(2 * index + 2)
print(f"arr[{index}]={self.arr[index]}", end=" ")
arr_binary_tree = ArrayBinaryTree([1, 2, 3, 4, 5, 6, 7])
print("前序遍历:")
arr_binary_tree.pre_order(0)
print()
print("中序遍历:")
arr_binary_tree.infix_order(0)
print()
print("后序遍历:")
arr_binary_tree.post_order(0)
print()
线索化二叉树
简单介绍
思路分析
遍历线索化二叉树
线索化二叉树和遍历线索化二叉树的代码实现
""" 线索化二叉树 """
# 创建 Node 节点
class Node:
no: int = None
left = None
right = None
# left_type/right_type 表示指针类型
# 值为0时表示指向的是左子树/右子树,为1时表示指向的是前驱节点/后继节点
left_type: int = 0
right_type: int = 0
def __init__(self, no: int):
self.no = no
self.left = None
self.right = None
def __str__(self):
return f"no={self.no}"
# 建立线索化 Node 二叉树
class ThreadedBinaryTree:
root: Node = None
# 为了实现线索化,需要一个指针(变量)指向当前节点的前驱节点
# 如中序线索化中,需要一个指针(变量)指向当前节点在中序遍历结果中的前驱节点
# 在递归进行线索化时, pre 总指向当前节点按某种遍历方式结果的前驱节点
pre: Node = None
# 建立中序线索化二叉树
def threaded_infix_tree(self, node: Node):
"""
建立中序线索化二叉树
:param node: 要线索化的节点
:return:
"""
if node is None: # 节点为空,不需要线索化
return
# 先线索化左子树
self.threaded_infix_tree(node.left)
# 线索化当前节点
# 结合图形理解代码,第一个要线索化的节点是8
# 先处理当前节点的左子节点
if node.left is None: # 如果当前节点的左指针为空,则让左指针指向当前节点的前驱节点pre
node.left = self.pre
node.left_type = 1 # 设置指针类型为1,表示该指针指向的是前驱节点
# 处理后继节点
if self.pre and self.pre.right is None:
self.pre.right = node
self.pre.right_type = 1 # 设置指针类型为1,表示该指针指向的是后继节点
# 每处理一个节点后,让当前节点成为下一个节点的前驱节点
self.pre = node
# 后线索化右子树
self.threaded_infix_tree(node.right)
# 遍历中序线索化二叉树
def for_in_tree(self):
node = self.root
while node:
# 循环找到 left_type = 1 的节点,该节点就是中序遍历的第一个节点,即图中的节点8
while node.left_type == 0:
node = node.left
# 输出当前节点
print(node, end=" ")
# 如果当前节点的右指针指向的是后继节点,则一直输出
while node.right_type == 1:
node = node.right
print(node, end=" ")
# 替换要遍历的节点,具体过程通过debug和结合图来看会更容易理解
node = node.right
def test_binary_tree():
# 手动创建二叉树
binary_tree = BinaryTree()
root = Node(1)
node2 = Node(3)
node3 = Node(6)
node4 = Node(8)
node5 = Node(10)
node6 = Node(14)
root.left = node2
root.right = node3
node2.left = node4
node2.right = node5
node3.left = node6
threaded_binary_tree = ThreadedBinaryTree()
threaded_binary_tree.root = root
# 测试线索化
print(f"线索化前:10的left={node5.left},right={node5.right}")
threaded_binary_tree.threaded_infix_tree(root)
# 以10号节点,即node5测试
print(f"线索化后:10的前驱节点={node5.left},后继节点={node5.right}")
# 测试遍历线索化二叉树
threaded_binary_tree.for_in_tree()
test_binary_tree()