Дерево

структура данных
Дерево

1/Что такое древовидная структура

树型结构是一种重要的`非线性`数据结构,其中以树和二叉树最为常用,是以分支关系定义的层次结构。
树型结构在客观世界中广泛存在:
   <1>如人类社会的族谱和各种社会组织机构,以及公司里的组织架构。
    在计算机领域中也有广泛应用,如在编译程序中,可用树来表示源程序的语法结构;
    在数据库系统中,树型结构也是信息的重要组织形式之一;
    在机器学习中,决策树,随机森林,GBDT等是常见的树模型。

在树型结构中,有3种节点,分别是:
   根节点root node,
   中间节点decision node,
   叶子节点 terminor node。
   
在一棵树中,只有一个根节点,没有左右孩子节点的就是叶子节点。
一棵树有多少层,及这棵树的深度(或者高度)是多少。
根据树的高度,我们可以知道这棵树最多有多少节点。及2的k次方-1个节点

image.png

2/ Что такое бинарное дерево

二叉树(Binary Tree)是树型结构的一种
之所以称为二叉,而不是三叉,是因为每个节点的出度最大是2,当然也可以是1。

它的特点是每个节点最多有两棵子树(即二叉树中不存在度大于2的节点),,最多分2个叉
且二叉树的子树有左右之分,其次序不能任意颠倒(有序树)。

 Согласно определению бинарного дерева, оно обладает следующими важными свойствами, как показано ниже:

3/ Что такое полное бинарное дерево

满二叉树是完全填满的二叉树,没一层都是填满的,最后一层也是填满的。
满二叉树,及除了叶子节点,其他的每个节点都有2个孩子节点。
对于满二叉树,你再也添加不进去一个新的节点了。

我们如何判断一棵树是满二叉树呢?
    如果一棵树的深度是k,且有2的k次方-1个节点,则为满二叉树。

4/ Что такое полное бинарное дерево

完全二叉树,是除最后一层以外都是填满的,最后一层外也必须从左到右依次填入。
满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。

5/ Обход бинарного дерева

所谓二叉树的遍历,指的是如何按某种搜索路径巡防树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。
对于二叉树,常见的遍历方法有:先序遍历,中序遍历,后序遍历,层序遍历。
这些遍历方法一般使用递归算法实现。

先序遍历的操作定义为:
    若二叉树为空,为空操作;
    否则(1)访问根节点;
       (2)遍历左子树;
       (3)遍历右子树。

中序遍历的操作定义为:
    若二叉树为空,为空操作;
    否则(1)遍历左子树;
        (2)访问根结点;
        (3)遍历右子树。
    
后序遍历的操作定义为:
    若二叉树为空,为空操作;
    否则(1)遍历左子树;
       (2)遍历右子树;
       (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]]

image.png

6/ Реализация кода Python для двоичного дерева

from graphviz import Digraph
import uuid
from random import sample

# 二叉树类
class BTree(object):

    # 初始化
    # 构建二叉树
    def __init__(self, data=None, left=None, right=None):
        #对于一个节点来说,由3部分构成,及数据,左孩子,右孩子
        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].data

        return level_order


    # 二叉树的高度
    def height(self):
        # 空的树高度为0, 
        if self.data is None:
            return 0
            
        # 只有root节点的树高度为1
        elif self.left is None and self.right is None:
            return 1
        
        # 只要有一个不是None,则进行递归
        elif 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):
        # 如果连数据域都没有,则直接返回None
        if self.data is None:
            return None
        
        # 既没有左孩子,也没有右孩子,这样的节点才是叶子节点,打印出来
        elif 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()
        
        # 如果左右孩子都有,则2边都递归
        else:
            self.left.leaves()
            self.right.leaves()

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