from matplotlib import pyplot as plt
class Node(object):
"""
二叉树结点:
(1)结点有左右子域,左子域left_child,右子域right_child;
(2)结点有左右子域标志位:
left_tag:为0表示该left_child存放的是当前节点的左孩子结点,为1表示该left_child存放的是当前节点的前驱节点
right_tag为0表示该left_child存放的是当前节点的右孩子结点,为1表示该left_child存放的是当前节点的后继节点
"""
def __init__(self, value, id_=None):
"""
初始化节点
:param value: 节点中所保存的值
"""
self.left_child = None
self.left_tag = 0
self.right_child = None
self.right_tag = 0
self.value = value
def __str__(self):
return self.value
class BiTreeThread(object):
"""二叉树及其常用方法"""
def __init__(self, d_hor=4, d_vec=8, radius=1.5, figsize =(11, 9)):
"""
对所展示二叉树的一些元素参数的设置
:param d_hor: 节点与节点的水平距离
:param d_vec: 节点与节点的垂直距离
:param radius: 节点的半径
:param radius: 画布大小,用一个元祖表示画布width和high,单位为inch
"""
self.root = None
self.d_hor = d_hor
self.d_vec = d_vec
self.radius = radius
self.figsize = figsize
def get_left_width(self, root):
"""获得根左边宽度,也是根的左子孙节点数"""
return self.get_width(root.left_child)
def get_right_width(self, root):
"""获得根右边宽度,也是根的右子孙节点数"""
return self.get_width(root.right_child)
def get_width(self, root):
"""获得树的宽度,也是该树的节点数。使用的是中序遍历方式"""
if root:
return self.get_width(root.left_child) + 1 + self.get_width(root.right_child)
else:
return 0
def get_height(self, root):
"""获得二叉树的高度, 使用后序遍历"""
if root:
return max(self.get_height(root.left_child), self.get_height(root.right_child)) + 1
else:
return 0
def get_w_h(self, root):
"""获得树的宽度和高度"""
w = self.get_width(root)
h = self.get_height(root)
return w, h
def __draw_a_node(self, x, y, value, ax):
"""画一个节点"""
c_node = plt.Circle((x, y), radius=self.radius, color="#65DDFF")
ax.add_patch(c_node)
plt.text(x, y, value, ha='center', va='center', fontsize=25, )
def __draw_a_edge(self, x1, y1, x2, y2):
"""画一条边"""
x = (x1, x2)
y = (y1, y2)
plt.plot(x, y, 'g-')
def __create_win(self, root):
"""创建窗口"""
WEIGHT, HEIGHT = self.get_w_h(root)
WEIGHT = (WEIGHT+1)*self.d_hor
HEIGHT = (HEIGHT+1)*self.d_vec
fig = plt.figure(figsize=self.figsize)
ax = fig.add_subplot(111)
plt.xlim(0, WEIGHT)
plt.ylim(0, HEIGHT)
x = (self.get_left_width(root) + 1) * self.d_hor
y = HEIGHT - self.d_vec
return fig, ax, x, y
def __print_tree_by_preorder(self, root, x, y, ax):
"""通过先序遍历打印二叉树"""
if not root:
return
self.__draw_a_node(x, y, root.value, ax)
lx = rx = 0
ly = ry = y - self.d_vec
if root.left_child:
lx = x - self.d_hor * (self.get_right_width(root.left_child) + 1)
self.__draw_a_edge(x, y, lx, ly)
if root.right_child:
rx = x + self.d_hor * (self.get_left_width(root.right_child) + 1)
self.__draw_a_edge(x, y, rx, ry)
self.__print_tree_by_preorder(root.left_child, lx, ly, ax)
self.__print_tree_by_preorder(root.right_child, rx, ry, ax)
def show_BiTree_1(self):
"""可视化二叉树"""
_, ax, x, y = self.__create_win(self.root)
self.__print_tree_by_preorder(self.root, x, y, ax)
plt.show()
def show_BiTree_2(self):
"""采用逆中序方法+矩阵转置"""
def inner_func(root, layer):
if root:
inner_func(root.left_child, layer + 1)
print(f"{' ' * layer}{root.value}")
inner_func(root.right_child, layer + 1)
return inner_func(self.root, 1)
def __create_tree(self):
value = input("输入节点数据:").strip()
if value:
node = Node(value)
node.left_child = self.__create_tree()
node.right_child = self.__create_tree()
return node
else:
return None
def create_tree(self):
"""先序方式创建二叉树"""
self.root = self.__create_tree()
def create_tree_extension(self, seq):
"""
利用扩展先序遍历序列创建二叉树
扩展先序遍历序列:AB.DF..G..C.E.H..
创建过程:首先读入当前根节点的数据,如果是“.”则将当前树根置为空,否则申请一个新节点,存入当前根节点的数据,
分别用当前根节点的左子域和右子域进行递归调用,创建左右子树。
:param seq: 扩展先序遍历序列
:return: 创建好的一棵树
"""
if seq:
index = 0
def inner_function():
nonlocal index
try:
if seq[index] == ".":
index += 1
return
else:
node = Node(seq[index])
index += 1
node.left_child = inner_function()
node.right_child = inner_function()
return node
except IndexError as e:
pass
self.root = inner_function()
def in_thread(self):
"""
此处只是中序线索化二叉树,还有线序线索化二叉树和后序线索化二叉树
算法思想:
(1) 中序线索化二叉树采用中序遍历算法框架;
(2) 加线索操作就是访问结点操作;
(3) 加线索操作需要利用刚访问过结点与当前结点的关系,因此设置一个指针pre,始终记录刚访问过的结点,其操作如下:
a. 如果当前遍历结点root的左子域为空,则让左子域指向pre;
b. 如果前驱pre的左子域为空,则让右子域指向当前遍历结点root;
c. 为下次做准备,当前访问结点root作为下一个访问结点的前驱pre。
中序线索化二叉树第一个结点没有前驱,最后一个结点没有后继,因此有两个空子域。
先序线索化二叉树最后一个结点没有后继,因此有一个空子域。
后序线索化二叉树第一个结点没有前驱,因此有一个空子域。
"""
pre_node = None
def inner_func(root):
if root:
nonlocal pre_node
inner_func(root.left_child)
if pre_node:
if not root.left_child:
root.left_child = pre_node
root.left_tag = 1
if not pre_node.right_child:
pre_node.right_child = root
pre_node.right_tag = 1
pre_node = root
inner_func(root.right_child)
return inner_func(self.root)
def in_precursor(self, p):
"""
在中序线索二叉树中查找某个结点的前驱
算法思想:
根据线索二叉树的基本概念和存储结构可知,对于结点p,当p.left_tag=1时,p.left_child指向p的前驱。
当p.left_tag=0时,p.left_child指向p的左孩子。由中序遍历的规律可知,作为根p的前驱结点,它是中序遍历p
的左子树时访问的最后一个结点,即左子树的“最右下端”结点。
:param p: 所给结点
:return: 所给结点的前驱结点
"""
if p.value is not None:
if p.left_tag:
return p.left_child
elif p.left_child:
p = p.left_child
while not p.right_tag:
p = p.right_child
return p
else:
print("该节点是中序线索化二叉树第一个结点,没有前驱结点!!!")
else:
print("该结点为空")
def in_next(self, p):
"""
在中序线索二叉树中查找某个结点的后继
算法思想:
根据线索二叉树的基本概念和存储结构可知,对于结点p,当p.right_tag=1时,p.right_child指向p的后继。
当p.right_tag=0时,p.right_child指向p的右孩子。由中序遍历的规律可知,作为根p的后继结点,它是中序遍历p
的右子树时访问的最后一个结点,即右子树的“最左下端”结点。
:param p: 所给结点
:return: 所给结点的后继结点
"""
if p.value is not None:
if p.right_tag:
return p.right_child
elif p.right_child:
p = p.right_child
while not p.left_tag:
p = p.left_child
return p
else:
print("该节点是中序线索化二叉树最后一个结点,没有后继结点!!!")
else:
print("该结点为空")
def __in_first(self, root):
"""求出中序遍历下的第一个访问结点"""
if root:
while root.left_child:
root = root.left_child
return root
def trav_in_order_thread(self):
"""
遍历中序线索二叉树
遍历某种线索树算法思想:
(1) 求出某种遍历次序下第一个被访问结点
(2) 然后连续求出刚访问结点的后继节点,直至所有的结点均被访问
通过调用__in_first和in_next,可以实现对中序线索树的中序遍历,且不需要使用递归栈。
"""
from collections import deque
in_seq = deque()
p = self.__in_first(self.root)
while p:
in_seq.append(p.value)
p = self.in_next(p)
return in_seq
def insert_node(self, p, r, left_or_right="r"):
pass
from BiTreeThread import BiTreeThread
if __name__ == '__main__':
bi_tree_thread = BiTreeThread()
bi_tree_thread.create_tree_extension(r"AB.DF..G..C.E.H..")
bi_tree_thread.show_BiTree_1()
bi_tree_thread.in_thread()
print(bi_tree_thread.trav_in_order_thread())