树的定义与基本术语

树型结构是一类重要的非线性数据结构,其中以树和二叉树最为常用,是以分支关系定义的层次结构。在编译程序中,可用树来表示源程序的语法结构;在数据库系统中,树型结构也是信息的重要组织形式之一;在机器学习中,决策树,随机森林,GBDT等是常见的树模型。
  树(Tree)是n(n>=0)个结点的有限集。在任意一棵树中:(1)有且仅有一个特定的称为根(Root)的节点;(2)当n>1时,其余节点可分为m(m>0)个互不相交的有限集T1,T2,T3,…其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。

图1 树型结构

在图1,该树一共有13个节点,其中A是根,其余节点分成3个互不相交的子集:T1 = {B,E,F,K,L},T2 = {C,G},T3 = {D,H,I,J,M};T1,T2,T3都是根A的子树,且本身也是一棵树。例如T1,其根为B,其余节点分为两个互不相交的子集;T11 = {E,K,L},T12 = {F}.T11和T12都是B的子树。而在T11中E是根,{K}{L}是E的两棵互不相交的子树,其本身又是只有一个根节点的树。
  
  树的基本术语
  树的结点包含一个数据元素及若干指向其子树的分支。节点拥有的子树数量称为节点的度(Degree)。在图1中,A的度为3,B的度为2,C的度为1,F的度为0。度为0的结点称为叶子(Leaf)结点。在图1中,K,L,F,G,M,I,J都是该树的叶子。度不为0的结点称为分支结点树的度是指树内个结点的度的最大值。
  结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(Parent)。在图1,中,D是A的孩子,A是D的双亲。同一个双亲的孩子之间互称兄弟(Sibling)。在图1中,H,I,J互为兄弟。结点的祖先是从根到该结点所经分支上的所有结点。在图1中,M的祖先为A,D,H。对应地,以某结点为根的子树中的任一结点都称为该结点的子孙。在图1中,B的子孙为E,F,K,L。
  树的层次(Level)是从根开始,根为第一层,根的孩子为第二层等。双亲在同一层的结点互为同兄弟,在图1中,K,L,M互为堂兄弟。树中结点的最大层次称为树的深度(Depth)或高度,在图1中,树的深度为4。
  如果将树中结点的各子树看成从左到右是有次序的(即不能交换),则称该树为有序树,否则为无序树
  **森林(Forest)m(m>=0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。在机器学习模型中,决策树为树型结构,而随机森林为森林,是由若干决策树组成的森林。

二叉树的定义与基本性质

**二叉树(Binary Tree)**是一种特殊的树型结构,它的特点是每个结点至多有两棵子树(即二叉树中不存在度大于2的结点),且二叉树的子树有左右之分,其次序不能任意颠倒(有序树)。
  二叉树具有下列重要性质:

性质1)在二叉树的第i层上至多有2^{i-1}个结点(i>=1)
性质2)深度为k的二叉树至多有2^{k}-1个结点(k>=1)
性质3)对任何一棵二叉树,如果其叶子节点数为度为2的结点数为n0,度为2的结点数为n2,则n0 = n2 + 1。
  一棵深度为K且有2^k-1个结点的二叉树称为满二叉树。深度为K,结点数为n的二叉树,当且仅当其每一个结点都与深度为k]的满二叉树中编号为1至n的结点一一对应时,称之为完全二叉树。在下图2中,(a)为满二叉树,(b)为完全二叉树。

图2 特殊形态的二叉树

下面介绍完全二叉树的两个特性:

性质4)具有n个结点的完全二叉树的深度为[log2n]+1其中[x]表示不大于x的最大整数。
性质5)如果对一棵有n个结点的完全二叉树的结点按层序编号(从第一层到最后一层,每层从左到右),则对任一结点i(1<=i<=n)有:
(1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲结点为[1/2]。
(2)如果2i>n,则结点i无左孩子;否则其左孩子是结点2i。
(3)如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1。

二叉树的遍历

二叉树的遍历**,指的是如何按某种搜索路径巡防树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。对于二叉树,常见的遍历方法有:先序遍历,中序遍历,后序遍历,层序遍历。这些遍历方法一般使用递归算法实现。
  先序遍历的操作定义为:若二叉树为空,为空操作;否则(1)访问根节点;(2)先序遍历左子树;(3)先序遍历右子树。
  中序遍历的操作定义为:若二叉树为空,为空操作;否则(1)中序遍历左子树;(2)访问根结点;(3)中序遍历右子树。
  后序遍历的操作定义为:若二叉树为空,为空操作;否则(1)后序遍历左子树;(2)后序遍历右子树;(3)访问根结点。
  层序遍历的操作定义为:若二叉树为空,为空操作;否则从上到下、从左到右按层次进行访问。
  如对于下图3,

图3 示例二叉树

其先序遍历、中序遍历、后序遍历、层序遍历的结果为:

先序遍历为:
18 7 3 4 11 5 1 3 6 2 4
中序遍历为:
3 7 4 18 1 5 3 11 2 6 4
后序遍历为:
3 4 7 1 3 5 2 4 6 11 18
层序遍历为:
[[18], [7, 11], [3, 4, 5, 6], [1, 3, 2, 4]]

关于二叉树的存储结构,可以选择链式存储结构。用于表示二叉树的链表中的结点至少包含3个域:数据域和左、右指针。下面利用链式存储结构实现二叉树(Python实现)。

二叉树的Python实现

Python代码实现二叉树:

from graphviz import Digraph
import uuid
from random import sample# 二叉树类
class BTree(object):# 初始化def __init__(self, data=None, left=None, right=None):self.data = data    # 数据域self.left = left    # 左子树self.right = right  # 右子树self.dot = Digraph(comment='Binary Tree')# 前序遍历def preorder(self):if self.data is not None:print(self.data, end=' ')if self.left is not None:self.left.preorder()if self.right is not None:self.right.preorder()# 中序遍历def inorder(self):if self.left is not None:self.left.inorder()if self.data is not None:print(self.data, end=' ')if self.right is not None:self.right.inorder()# 后序遍历def postorder(self):if self.left is not None:self.left.postorder()if self.right is not None:self.right.postorder()if self.data is not None:print(self.data, end=' ')# 层序遍历def levelorder(self):# 返回某个节点的左孩子def LChild_Of_Node(node):return node.left if node.left is not None else None# 返回某个节点的右孩子def RChild_Of_Node(node):return node.right if node.right is not None else None# 层序遍历列表level_order = []# 是否添加根节点中的数据if self.data is not None:level_order.append([self])# 二叉树的高度height = self.height()if height >= 1:# 对第二层及其以后的层数进行操作, 在level_order中添加节点而不是数据for _ in range(2, height + 1):level = []  # 该层的节点for node in level_order[-1]:# 如果左孩子非空,则添加左孩子if LChild_Of_Node(node):level.append(LChild_Of_Node(node))# 如果右孩子非空,则添加右孩子if RChild_Of_Node(node):level.append(RChild_Of_Node(node))# 如果该层非空,则添加该层if level:level_order.append(level)# 取出每层中的数据for i in range(0, height):  # 层数for index in range(len(level_order[i])):level_order[i][index] = level_order[i][index].datareturn level_order# 二叉树的高度def height(self):# 空的树高度为0, 只有root节点的树高度为1if self.data is None:return 0elif self.left is None and self.right is None:return 1elif self.left is None and self.right is not None:return 1 + self.right.height()elif self.left is not None and self.right is None:return 1 + self.left.height()else:return 1 + max(self.left.height(), self.right.height())# 二叉树的叶子节点def leaves(self):if self.data is None:return Noneelif self.left is None and self.right is None:print(self.data, end=' ')elif self.left is None and self.right is not None:self.right.leaves()elif self.right is None and self.left is not None:self.left.leaves()else:self.left.leaves()self.right.leaves()# 利用Graphviz实现二叉树的可视化def print_tree(self, save_path='./Binary_Tree.gv', label=False):# colors for labels of nodescolors = ['skyblue', 'tomato', 'orange', 'purple', 'green', 'yellow', 'pink', 'red']# 绘制以某个节点为根节点的二叉树def print_node(node, node_tag):# 节点颜色color = sample(colors,1)[0]if node.left is not None:left_tag = str(uuid.uuid1())            # 左节点的数据self.dot.node(left_tag, str(node.left.data), style='filled', color=color)    # 左节点label_string = 'L' if label else ''    # 是否在连接线上写上标签,表明为左子树self.dot.edge(node_tag, left_tag, label=label_string)   # 左节点与其父节点的连线print_node(node.left, left_tag)if node.right is not None:right_tag = str(uuid.uuid1())self.dot.node(right_tag, str(node.right.data), style='filled', color=color)label_string = 'R' if label else ''  # 是否在连接线上写上标签,表明为右子树self.dot.edge(node_tag, right_tag, label=label_string)print_node(node.right, right_tag)# 如果树非空if self.data is not None:root_tag = str(uuid.uuid1())                # 根节点标签self.dot.node(root_tag, str(self.data), style='filled', color=sample(colors,1)[0])     # 创建根节点print_node(self, root_tag)self.dot.render(save_path)                              # 保存文件为指定文件

在上述代码中创建了二叉树类BTree,实现了如下方法:

  1. 初始化方法:该树存放的数据为data,左子树,右子树为left和right,默认均为None;
  2. preorder()方法:递归实现二叉树的先序遍历;
  3. inorder()方法:递归实现二叉树的中序遍历;
  4. postorder()方法:递归实现二叉树的后序遍历;
  5. levelorder()方法:递归实现二叉树的层序遍历;
  6. height()方法:计算二叉树的高度;
  7. leaves()方法:计算二叉树的叶子结点;
  8. print_tree()方法:利用Graphviz实现二叉树的可视化,需要设置的参数为save_path和label,save_path为文件保存路径,默认的保存路径为当前路径下的Binary_Tree.gv,可以用户自己设置;label为是否在Graphviz文件中添加二叉树的左右子树的标签,用于分清哪棵是左子树,哪棵是右子树,可以用用户自己设置。

实现图3的示例二叉树,Python代码如下:

from Binary_Tree import BTree# 构造二叉树, BOTTOM-UP METHOD
right_tree = BTree(6)
right_tree.left = BTree(2)
right_tree.right = BTree(4)left_tree = BTree(5)
left_tree.left = BTree(1)
left_tree.right = BTree(3)tree = BTree(11)
tree.left = left_tree
tree.right = right_treeleft_tree = BTree(7)
left_tree.left = BTree(3)
left_tree.right = BTree(4)right_tree = tree # 增加新的变量
tree = BTree(18)
tree.left = left_tree
tree.right = right_treeprint('先序遍历为:')
tree.preorder()
print()print('中序遍历为:')
tree.inorder()
print()print('后序遍历为:')
tree.postorder()
print()print('层序遍历为:')
level_order = tree.levelorder()
print(level_order)
print()height = tree.height()
print('树的高度为%s.' % height)print('叶子节点为:')
tree.leaves()
print()# 利用Graphviz进行二叉树的可视化
tree.print_tree(save_path='E://BTree.gv', label=True)

运行上述代码可得该二叉树的一些信息,输出结果如下:

先序遍历为:
18 7 3 4 11 5 1 3 6 2 4
中序遍历为:
3 7 4 18 1 5 3 11 2 6 4
后序遍历为:
3 4 7 1 3 5 2 4 6 11 18
层序遍历为:
[[18], [7, 11], [3, 4, 5, 6], [1, 3, 2, 4]]树的高度为4.
叶子节点为:
3 4 1 3 2 4

该Python代码的优势在于利用Graphviz实现了二叉树的可视化,可以形象直观地得到二叉树的图形。在上面的代码中,我们可以看到,构建二叉树不是很方便,需要手动地一个个结点去添加。那么,如果当我们需要根据某个列表,按列表顺序去构建二叉树时,即二叉树的层序遍历为该列表,按照某个列表去构建二叉树

Python代码如下:

from Binary_Tree import BTree# 利用列表构造二叉树
# 列表中至少有一个元素
def create_BTree_By_List(array):i = 1# 将原数组拆成层次遍历的数组,每一项都储存这一层所有的节点的数据level_order = []sum = 1while sum < len(array):level_order.append(array[i-1:2*i-1])i *= 2sum += ilevel_order.append(array[i-1:])# print(level_order)# BTree_list: 这一层所有的节点组成的列表# forword_level: 上一层节点的数据组成的列表def Create_BTree_One_Step_Up(BTree_list, forword_level):new_BTree_list = []i = 0for elem in forword_level:root = BTree(elem)if 2*i < len(BTree_list):root.left = BTree_list[2*i]if 2*i+1 < len(BTree_list):root.right = BTree_list[2*i+1]new_BTree_list.append(root)i += 1return new_BTree_list# 如果只有一个节点if len(level_order) == 1:return BTree(level_order[0][0])else: # 二叉树的层数大于1# 创建最后一层的节点列表BTree_list = [BTree(elem) for elem in level_order[-1]]# 从下往上,逐层创建二叉树for i in range(len(level_order)-2, -1, -1):BTree_list = Create_BTree_One_Step_Up(BTree_list, level_order[i])return BTree_list[0]#array = list(range(1,19))
array = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
tree = create_BTree_By_List(array)print('先序遍历为:')
tree.preorder()
print()height = tree.height()
print('\n树的高度为%s.\n'%height)print('层序遍历为:')
level_order = tree.levelorder()
print(level_order)
print()print('叶子节点为:')
tree.leaves()
print()# 利用Graphviz进行二叉树的可视化
tree.print_tree(save_path='E://create_btree_by_list.gv', label=True)

上述程序中,笔者利用create_BTree_By_List()函数实现了按照某个列表去构建二叉树,输入的参数array为列表,要求列表中至少有一个元素。运行上述程序得到的26个大写字母列表所构建的二叉树的图像如下:

图4 26个大写字母列表所构建的二叉树

输出的结果如下:

先序遍历为:
A B D H P Q I R S E J T U K V W C F L X Y M Z G N O 树的高度为5.层序遍历为:
[['A'], ['B', 'C'], ['D', 'E', 'F', 'G'], ['H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'], ['P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']]叶子节点为:
P Q R S T U V W X Y Z N O

总结

二叉树是很多重要算法及模型的基础,比如二叉搜索树(BST),哈夫曼树(Huffman Tree),CART决策树等。本文先介绍了树的基本术语,二叉树的定义与性质及遍历、储存,然后用Python实现了二叉树的上述方法,代码的最大亮点在于实现了二叉树的可视化,这个功能很激动人心。
  在Python中,已有别人实现好的二叉树的模块,它是binarytree模块,其官方文档的网址为:https://pypi.org/project/binarytree/。其使用的例子如下:

binarytree模块演示

‘O’], [‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’]]

叶子节点为:
P Q R S T U V W X Y Z N O

关于这个模块的更多功能,可参考其官方文档。当然,最好亲自实现一下二叉树哦,这样能够加深对二叉树的理解~

deeply understanding Binary tree--二叉树相关推荐

  1. [LeetCode] Invert Binary Tree - 二叉树翻转系列问题

    目录: 1.Invert Binary Tree - 二叉树翻转 [递归] 题目概述: Invert a binary tree. 4/ \2 7/ \ / \ 1 3 6 9 to 4/ \7 2/ ...

  2. LeetCode 545. Boundary of Binary Tree 二叉树边界

    LeetCode 545. Boundary of Binary Tree 二叉树边界 Given a binary tree, return the values of its boundary i ...

  3. LeetCode:104_Maximum Depth of Binary Tree | 二叉树的最大深度 | Easy

    要求:求二叉树的深度(二叉树的深度为最远叶子节点到根节点的距离,即根节点到最远叶子节点的距离) Given a binary tree, find its maximum depth.The maxi ...

  4. 【LeetCode】Minimum Depth of Binary Tree 二叉树的最小深度 java

    [LeetCode]Minimum Depth of Binary Tree Given a binary tree, find its minimum depth. The minimum dept ...

  5. 01 二叉树的BFS(广度、层次或水平遍历实现)【Binary Tree 二叉树】

    二叉树的遍历分为BFS和DFS两种大类 下面完整实现BFS遍历二叉树 * 例如二叉树* 1* / \* 2 3* /\* 4 5 BFS遍历结果:1-2-3-4-5 具体的代码实现: 方法一.采用递归 ...

  6. [CareerCup] 4.4 Create List at Each Depth of Binary Tree 二叉树的各层创建链表

    4.4 Given a binary tree, design an algorithm which creates a linked list of all the nodes at each de ...

  7. 111. Minimum Depth of Binary Tree 二叉树的最小深度

    给定一个二叉树,找出其最小深度. 最小深度是从根节点到最近叶子节点的最短路径上的节点数量. 说明: 叶子节点是指没有子节点的节点. 示例: 给定二叉树 [3,9,20,null,null,15,7], ...

  8. 104. Maximum Depth of Binary Tree 二叉树的最大深度

    给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. 说明: 叶子节点是指没有子节点的节点. 示例: 给定二叉树 [3,9,20,null,null,15,7], ...

  9. [CareerCup] 4.7 Lowest Common Ancestor of a Binary Search Tree 二叉树的最小共同父节点

    4.7 Design an algorithm and write code to find the first common ancestor of two nodes in a binary tr ...

最新文章

  1. 哈希分布与一致性哈希算法简介
  2. C++11中nullptr的使用
  3. 阿里打破自然语言理解世界纪录,AI常识推理水平正在逼近人类
  4. WSGI和PASTE
  5. 这是一个不完整的详细Java多线程,但对于初学者足够了,相信我,你会爱上她的
  6. openssl之BIO系列之1---抽象的IO接口
  7. python之旅(六) - 自省
  8. android与mysql的交互,与Android中的外部SQLite数据库进行交互.
  9. AVC sequence header AAC sequence header
  10. 关于C# .net JavaScript跨域
  11. uniapp好用项目和插件总结
  12. 奥城大学计算机专业,[美国金融硕士排名2018]2018年美国硕士双录取大学名单
  13. 云栖独栋别墅_绿野云溪花海独栋别墅
  14. 我的刻骨铭心的2018——致未来的自己和现在的你
  15. BZOJ 3876 AHOI2014 支线剧情 费用流
  16. 计算机文档软件,电脑文档软件
  17. 后渗透篇:劫持技术(lpk.dll劫持游戏注入【Win7 实例】)
  18. java jtable方法_Java中JTable如何使用?
  19. CSS样式的补充:​如何设置文本框内的默认文字?​
  20. Oracle 12安装教程

热门文章

  1. 有可能导致HttpQueryInfo 执行时出现12150 错误的一个原因
  2. centos7 安装sqlserver驱动以及扩展
  3. JS新手入门自学笔记
  4. jquery叠加页片自动切换特效
  5. [Remoting专题系列] 八:元数据
  6. 求职互联网数据分析,如何准备行业知识?
  7. hdu 4775 Infinite Go(暴力)
  8. 超强1000个jquery极品插件!(感觉好强大,转载一下)
  9. 【Python笔记】datetime 模块用法
  10. 软技能-代码之外的生存指南-读书笔记(持续更新中)