数据结构与算法Day6 树与树算法

树(tree)的概念

树是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由 n ( n > = 1 ) n(n>=1) nn>=1个有限节点组成一个具有层次关系的集合。
特点:

  1. 每个节点有零个或多个子节点;
  2. 没有父节点的节点称为根节点;
  3. 每一个非根节点有且只有一个父节点
  4. 除了根节点外,每个子节点可以分为多个不相交的子树(每个节点只有一个上级)

在这里插入图片描述

树的术语

术语说明
节点的度一个节点含有的子树的个数称为该节点的度
树的度一棵树中,最大的节点的度称为树的度
叶节点或终端节点度为零的节点
父亲节点或父节点若一个节点含有子节点,则这个节点称为其子节点的父节点
孩子节点或子节点一个节点含有的子树,子树节点称为该节点的子节点
兄弟节点具有相同父节点的节点互称为兄弟节点
节点的层次从根开始定义起,根为第1层,根的子节点为第2层,以此类推
树的高度或深度树中节点的最大层次
堂兄弟节点父节点在同一层的节点互为堂兄弟;
节点的祖先从根到该节点所经分支上的所有节点
子孙以某节点为根的子树中任一节点都称为该节点的子孙
森林由m(m>=0)棵互不相交的树的集合称为森林

树的种类

说明
无序树树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;
有序树树中任意节点的子节点之间有顺序关系,这种树称为有序树;
有序树种类说明
二叉树每个节点最多含有两个子树的树称为二叉树
霍夫曼树(用于信息编码)带权路径最短的二叉树称为哈夫曼树或最优二叉树
B树一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树
二叉树的种类说明
完全二叉树对于一棵二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列
满二叉树除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的完全二叉树。
平衡二叉树(AVL树)当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
排序二叉树(二叉查找树(Binary Search Tree)对于任意一个节点,由其延伸出去所有左边节点的值比其小,右边节点的值比其大

树的存储与表示

顺序存储

将数据结构存储在固定的数组中,然在遍历速度上有一定的优势,但因所占空间比较大(不是满二叉树就会有空缺),是非主流二叉树。
在这里插入图片描述

链式存储

二叉树通常以链式存储。由于对节点的个数无法掌握,常见树的存储表示都转换成二叉树进行处理,子节点个数最多为2。
在这里插入图片描述

常见的一些树的应用场景

  1. xml,html等,那么编写这些东西的解析器的时候,不可避免用到树
  2. 路由协议就是使用了树的算法
  3. mysql数据库索引
  4. 文件系统的目录结构
  5. 所以很多经典的AI算法其实都是树搜索,此外机器学习中的decision tree也是树结构

二叉树

二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)

二叉树的性质(特性)

  1. 在二叉树的第i层上至多有 2 i − 1 2^{i-1} 2i1$个结点(i>0)
  2. 深度为k的二叉树至多有 2 k − 1 2^k - 1 2k1个结点(k>0)
  3. 对于任意一棵二叉树,如果其叶结点数为 N 0 N_0 N0,而度数为2的结点总数为 N 2 N_2 N2,则 N 0 = N 2 + 1 N_0=N_2+1 N0=N2+1
  4. 具有n个结点的完全二叉树的深度必为 l o g ( n + 1 ) log{(n+1)} log(n+1)
  5. 对完全二叉树,若从上至下、从左至右编号,则编号为 i i i 的结点,其左孩子编号必为 2 i 2i 2i,其右孩子编号必为 2 i + 1 2i+1 2i1;其双亲的编号必为 i 2 \frac{i}{2} 2i i = 1 i=1 i1 时为根,除外)

二叉树的实现

树节点实现

class Node():
    """树节点"""
    def __init__(self, elem=None):
        self.elem=item
        self.lchild=None
        self.rchild=None

树的创建

给一个root根节点,一开始为空,随后添加节点。用广度进行遍历来添加节点

class Tree():
    """创建树"""
    def __init__(self,node=None):
        self.root=node
        
    def add(self,item):
        node=Node(item)
        if not self.root:   #if None 则不进入条件语句   
            self.root=node
            return
        
        #广度优先的查找方式和队列操作相一致
        queue=[]
        queue.append(self.root) 
        
       
        while queue:#注意[]和[None]的区别;根节点为空,if [None]是为真,
            cur_node=queue.pop(0)
            if cur_node.lchild:
                queue.append(cur_node.lchild)
            else:
                cur_node.lchild=node
                return
            if cur_node.rchild:
                queue.append(cur_node.rchild)
            else:
                cur_node.rchild=node
                return            

            

二叉树的遍历

所谓遍历(traversal)是指对树中所有结点的信息的访问,即依次对树中每个结点访问一次且仅访问一次

遍历模型实现方法
深度优先一般用递归,大部分也能用堆栈来实现
广度优先一般用队列来实现

广度优先遍历(层次遍历)

从树的root开始,从上到下从从左到右遍历整个树的节点

    def bread_travel(self):
        if not self.root:
            print('ROOT NONE')
            return None
        queue=[]
        queue.append(self.root)
        
        while queue:
            cur_node=queue.pop(0)
            if cur_node==None:
            	print(None)
            	continue
            print(cur_node.elem)
            if cur_node.lchild:
                queue.append(cur_node.lchild)
            if cur_node.rchild:
                queue.append(cur_node.rchild)
            

深度优先遍历

对于一颗二叉树,深度优先搜索(Depth First Search)是沿着树的深度遍历树的节点,尽可能深的搜索树的分支,常被用于访问树的节点。主要有三种方法,它们之间的不同在于访问每个节点的次序不同:

  1. 先序遍历(preorder)
  2. 中序遍历(inorder)
  3. 后序遍历(postorder)

在这里插入图片描述

先序遍历

在先序遍历中,先访问根节点,然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树。
根节点->左子树->右子树

    def preorder(self,node):
        """先序遍历,根左右"""
        if node is None: #遍历结束条件
            return
        print(node.elem)
        self.preorder(node.lchild) #self勿漏
        self.preorder(node.rchild)
中序遍历

在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树。
左子树->根节点->右子树

    def inorder(self, node):
        """中序遍历,左根右"""
        if node is None:
            return 
        self.inorder(node.lchild) 
        print(node.elem,end='\t')
        self.inorder(node.rchild)
后序遍历

在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点。
左子树->右子树->根节点

    def postorder(self, node):
        """中序遍历,左根右"""
        if node is None:
            return 
        self.postorder(node.lchild)      
        self.postorder(node.rchild)  
        print(node.elem,end='\t')

二叉树由深度遍历确定一棵树

必须要给出中序遍历

遍历顺序
先序遍历根左右
后序遍历左右根
中序遍历左根右
  1. 由先序遍历的首元素确定根元素,或者由后序遍历的尾元素确定根元素
  2. 通过中序中的根元素把树分为左、右子树

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值