Python 数据结构 - 二叉树
Python 数据结构 - 二叉树
binary tree
1. 基本概念
1.1 例子
Level 0: A
/ \
Level 1: B C
/ \ / \
Level 2: D E F G
/ / \
Level 3: H I J
1.2 相关概念:
-
根节点(root): 树的最上层的节点,任何非空的树都有一个节点
-
路径(path): 从起始节点到终止节点经历过的边
-
父亲(parent):除了根节点,每个节点的上一层边连接的节点就是它的父亲(节点)
-
孩子(children): 每个节点由边指向的下一层节点
-
兄弟(siblings): 同一个父亲并且处在同一层的节点
-
子树(subtree): 每个节点(以该节点为子树的根节点)包含它所有的后代组成的子树
-
叶子节点(leaf node): 没有孩子的节点成为叶子节点
1.3 属性
-
节点深度(depth): 节点对应的 level 数字
-
树的高度(height): 二叉树的高度就是 level 数 + 1,因为 level 从 0 开始计算的
-
树的宽度(width): 二叉树的宽度指的是包含最多节点的层级的节点数
-
树的(size):二叉树的节点总个数
一棵 size 为 \(n\) 的二叉树高度最多可以是 \(n\),最小的高度是 \(\lfloor \log_2 n \rfloor + 1\)。
1.4 二叉树类型
-
完美二叉树(perfect binary tree):所有的叶子节点都在同一层,毫无间隙填充了 \(h\) 层。
-
完全二叉树(complete binary tree):当一个高度为 \(h\) 二叉树,其前 \(h-1\) 高度构成了完美二叉树,并且其最底层的槽被毫无间隙地从左到右填充。
-
满二叉树(full binary tree):如果每个内部节点(非叶节点)都包含两个 children,就成为满二叉树。
1.5 二叉树遍历
-
先(根)序遍历: 先处理根,之后是左子树,然后是右子树
-
中(根)序遍历: 先处理左子树,之后是根,最后是右子树
-
后(根)序遍历: 先处理左子树,之后是右子树,最后是根
-
层序遍历:按层遍历,每一层从左到右遍历
2 程序实现
2.1 二叉树的节点
定义一个二叉树的节点类
class BinTreeNode(object):
def __init__(self, id, left=None, right=None):
self.id = id
self.left = left
self.right = right
2.2 构建二叉树
根据节点 list
构建二叉树
class BinTree(object):
def __init__(self, root_node=None):
self.root_node = BinTreeNode(**root_node)
self.node_dict = {}
node_id = root_node['id']
# root node
self.node_dict[node_id] = self.root_node
return None
# ---------------------------------------------------------
def build_from_node_list(self, node_list):
# 构建 node_dict,存放所有 node 对象
for node_li in node_list:
node_id = node_li['id']
self.node_dict[node_id] = BinTreeNode(**node_li)
# 把每个 node 的 node.left 和 node.right 修改为 node 对象
# root node
node_id = self.root_node.id
node = self.root_node
node.left = self.node_dict.get(node.left)
node.right = self.node_dict.get(node.right)
for node_li in node_list:
node_id = node_li['id']
node = self.node_dict[node_id]
# 把 node.left 和 node.right 修改为 node 对象
node.left = self.node_dict.get(node.left)
node.right = self.node_dict.get(node.right)
# node_dict.get(key, default=None) 如果查找的 key 不存在时,返回 default
return None
# ---------------------------------------------------------
实例
root_node = {'id': 'A', 'left': 'B', 'right': 'C'}
node_list = [
{'id': 'B', 'left': 'D', 'right': 'E'},
{'id': 'D', 'left': None, 'right': None},
{'id': 'E', 'left': 'H', 'right': None},
{'id': 'C', 'left': 'F', 'right': 'G'},
{'id': 'H', 'left': None, 'right': None},
{'id': 'F', 'left': None, 'right': None},
{'id': 'G', 'left': 'I', 'right': 'J'},
{'id': 'I'}, # 如果不存在子节点,也可以直接省略
{'id': 'J', 'left': None, 'right': None}]
# 创建根节点
btree = BinTree(root_node=root_node)
# 添加节点
btree.build_from_node_list(node_list)
2.3 二叉树遍历
# BinTree() 类 的内置函数
# ---------------------------------------------------------
def preorder_trav(self, subtree): # 先(根)序遍历
if subtree is not None:
self.node_seq.append(subtree.id) # 递归函数里先处理根
self.preorder_trav(subtree.left) # 递归处理左子树
self.preorder_trav(subtree.right) # 递归处理右子树
return None
# ---------------------------------------------------------
def inorder_trav(self, subtree): # 中(根)序遍历
if subtree is not None:
self.inorder_trav(subtree.left) # 递归函数左子树
self.node_seq.append(subtree.id) # 递归处理根
self.inorder_trav(subtree.right) # 递归处理右子树
return None
# ---------------------------------------------------------
def postorder_trav(self, subtree): # 后(根)序遍历
if subtree is not None:
self.postorder_trav(subtree.left) # 递归函数左子树
self.postorder_trav(subtree.right) # 递归处理右子树
self.node_seq.append(subtree.id) # 递归处理根
return None
# ---------------------------------------------------------
def layer_trav(self, subtree): # 层序遍历
cur_nodes = [subtree] # current layer nodes
next_nodes = []
while cur_nodes or next_nodes:
for node in cur_nodes:
self.node_seq.append(node.id)
if node.left:
next_nodes.append(node.left)
if node.right:
next_nodes.append(node.right)
cur_nodes = next_nodes # 继续遍历下一层
next_nodes = []
# ---------------------------------------------------------
def traversal(self, method='preorder'):
self.node_seq = []
if method == 'preorder':
self.preorder_trav(self.root_node)
elif method == 'inorder':
self.inorder_trav(self.root_node)
elif method == 'postorder':
self.postorder_trav(self.root_node)
elif method == 'layer':
self.layer_trav(self.root_node)
return self.node_seq
# ---------------------------------------------------------
实例
print(btree.traversal('preorder'))
print(btree.traversal('inorder'))
print(btree.traversal('postorder'))
print(btree.traversal('layer'))
# 输出
# ['A', 'B', 'D', 'E', 'H', 'C', 'F', 'G', 'I', 'J']
# ['D', 'B', 'H', 'E', 'A', 'F', 'C', 'I', 'G', 'J']
# ['D', 'H', 'E', 'B', 'F', 'I', 'J', 'G', 'C', 'A']
# ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
2.4 可视化
def display(self):
from graphviz import Digraph
root_node = self.root_node
# Create Digraph object
dot = Digraph(node_attr={'shape': 'circle', 'nodesep': '0'}, edge_attr={'arrowhead': 'vee'})
cur_nodes = [root_node] # current layer nodes
next_nodes = []
while cur_nodes or next_nodes:
for node in cur_nodes:
# add node
dot.node(node.id, label=node.id)
if (not node.left) and (not node.right):
pass
else:
if node.left:
# add left edge
dot.edge(node.id, node.left.id)
next_nodes.append(node.left)
else: # When node.left is none
# add virtual left edge
dot.node(node.id+'l', style='invis')
dot.edge(node.id, node.id+'l', style='invis')
# add virtual middle edge
dot.node(node.id+'m', label="", width='0', height='0', style='invis')
dot.edge(node.id, node.id+'m', style='invis')
if node.right:
# add right edge
dot.edge(node.id, node.right.id)
next_nodes.append(node.right)
else: # When node.right is none
# add virtual right edge
dot.node(node.id+'r', label=node.id, style='invis')
dot.edge(node.id, node.id+'r', style='invis')
cur_nodes = next_nodes # 继续遍历下一层
next_nodes = []
return dot
# ---------------------------------------------------------
实例
btree.display()
3 应用:二分查找树
二叉查找树(Binary Search Tree,BST)是一种特殊的二叉树,具有有以下性质:
对于任意一个节点 \(n\),
-
其左子树(left subtree)下的每个后代节点的值都小于节点 \(n\) 的值;
-
其右子树(right subtree)下的每个后代节点的值都大于节点 \(n\) 的值。
参考资料
[1] 14 树和二叉树, in "Python 算法与数据结构视频教程", 地址