二叉树选自百度百科,如有错误,欢迎指正
二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”和“右子树”。二叉树常被用于实现二叉查找树和二叉堆。
二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。每个子树都满足,大于它的左子树及其所有子节点,小于它的右子树及其所有子节点
代码
首先需要先定义节点类
class Node:
def __init__(self, value, left=None, right=None):
self.value = value # 节点的值
self.left = left # 左子节点
self.right = right # 右子节点
接下来定义树类
from collections import Iterable
class BinaryTree:
def __init__(self, seq=()):
assert isinstance(seq, Iterable) # 确保输入的参数为可迭代对象
self.root = None
这样,BinaryTree 的初始化就完成了
可是 BinaryTree 还没有任何数据,参数 seq 也没有被使用,这时候我们需要实现 insert 方法来插入数据
插入
因为 BinaryTree 的每个节点都是节点对象,所以需要先生成 Node 实例。默认第一个节点为根节点
def __init__(self, seq=()):
assert isinstance(seq, Iterable)
self.root = None
self.insert(*seq)
def insert(self, *args):
if not args:
return
if not self.root:
self.root = Node(args[0])
args = args[1:]
for i in args:
seed = self.root
while True:
if i > seed.value:
if not seed.right:
node = Node(i)
seed.right = node
break
else:
seed = seed.right
else:
if not seed.left:
node = Node(i)
seed.left = node
break
else:
seed=seed.left
最小值、最大值
def minNode(self):
node = self.root
while node.left:
node = node.left
return node
def maxNode(self):
node = self.root
while node.right:
node = node.right
return node
查找
通过给定值,返回对应的节点对象,如果没有则返回 None
def find(self, item):
node = self.root
while node:
if item > node.value:
node = node.right
elif item < node.value:
node = node.left
else:
return node
删除
二叉树删除操作有点复杂。被删除节点没有子节点
被删除节点有一个子节点
被删除节点有两个子节点
为方便描述,简称被删除节点为 A
前两种比较简单,第一种只需要将 A 的父节点对应属性改为 None。第二种,需要将 A 的父节点与子节点关联。第三种就相对复杂一点,有两种方案,寻找 A 的右子树的最小值或左子树的最大值节点 B,根据 B 节点创建临时节点,删除节点 B,然后用临时节点替换 A 节点这里使用第一种方式,使用右子树最小值节点
在此之前,我们需要改写一下 find、minNode 和 maxNode 方法,使其返回更多数据,便于删除操作
def find(self, item, seed=None):
node = seed or self.root # 修改查询起点
parent = None # 父节点
while node:
if item > node.value:
parent, node = node, node.right
elif item < node.value:
parent, node = node, node.left
else:
return (node, parent)
def minNode(self, seed=None):
node = seed or self.root
while node.left:
node = node.left
return node
def maxNode(self, seed=None):
node = seed or self.root
while node.right:
node = node.right
return node
接下来是 remove 方法
def remove(self, item):
result = self.find(item)
if result:
new_node = None # 替换 A 的节点
del_node, del_node_parent = result
if del_node.value == self.root.value:
raise ValueError('can not remove root') # 根节点固定
if del_node.left and del_node.right:
right_min = self.minNode(seed=del_node.right)
new_node = Node(right_min.value)
# 当 A 节点的右子树节点没有子节点时,临时节点的右子节点 为 None
if del_node.right.value == new_node.value:
new_node.left = del_node.left
else:
new_node.left, new_node.right = del_node.left, del_node.right
self.remove(right_min.value)
# A 节点的父节点与临时节点关联
elif del_node.left or del_node.right:
new_node = del_node.left or del_node.right
if del_node_parent.left and del_node_parent.left.value == del_node.value:
del_node_parent.left = new_node
elif del_node_parent.right and del_node_parent.right.value == del_node.value:
del_node_parent.right = new_node
del del_node
return
raise ValueError('item not in tree')
可视化
在画图前,需要遍历每一个节点,并为每个节点定义坐标,并连接相邻的节点,这里需要用到 networkx 跟 matplotlib
import networkx as nx
import matplotlib.pyplot as plt
def create_graph(G, node, pos={}, x=0, y=0, layer=1):
pos[node.value] = (x, y)
if node.left:
G.add_edge(node.value, node.left.value)
l_x, l_y = x - 1 / 2 ** layer, y - 1
l_layer = layer + 1
create_graph(G, node.left, x=l_x, y=l_y, pos=pos, layer=l_layer)
if node.right:
G.add_edge(node.value, node.right.value)
r_x, r_y = x + 1 / 2 ** layer, y - 1
r_layer = layer + 1
create_graph(G, node.right, x=r_x, y=r_y, pos=pos, layer=r_layer)
return (G, pos)
def draw(node): # 以某个节点为根画图
graph = nx.DiGraph()
graph, pos = create_graph(graph, node)
fig, ax = plt.subplots(figsize=(8, 10)) # 比例可以根据树的深度适当调节
nx.draw_networkx(graph, pos, ax=ax, node_size=300)
plt.show()
效果图如下:
这样就可以将抽象的树结构画出来,图像美化可以查看对应的文档调整
文章内容如有错漏,欢迎指正