Python数据结构与算法6-树


  • 是一种抽象数据类型(ADT),用来模拟具有树状结构性质的数据集合。
    特点
    1)每个节点有零个或多个子节点;
    2)没有父节点的节点称为根节点;
    3)每一个非根节点有且只有一个父节点;
    4)除了根节点外,每个子节点可以分为多个不相交的子树;

在这里插入图片描述

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

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

  3. 树的储存与表示
    1)顺序存储:将数据结构存储在固定的数组中,不好根据二叉树结点数目的增多而动态扩展。如果内存空间分配过大,造成内存空间的浪费;如果分配过小,则会产生数组溢出的问题。方便查找和遍历但不方便插入和删除,故不常用。
    在这里插入图片描述
    2)链式存储:由于对节点的个数无法掌握,常见树的存储表示都转换成二叉树进行处理,子节点个数最多为2。 可以动态分配内存空间,插入和删除节点更加方便,是更为常用的储存方式。
    在这里插入图片描述

  • 树的常见应用场景
    1)xml,html解析器
    2)路由协议
    3)mysql数据库索引
    4)文件系统的目录结构
    5)很多经典的AI算法其实都是树搜索,此外机器学习中的decision tree也是树结构

  • 树的实现
    创建树节点类:

    # 创建树节点
    class Node(object):
        def __init__(self, item = None, lchild = None, rchild = None):
            self.item = item
            self.lchild = lchild
            self.rchild = rchild
    

    创建具有添加节点功能的树:

    # 创建树
    class Tree(object):
        def __init__(self, root = None):
            self.root = root      # 指定根节点创建树
        
        def add(self, item):      # 加入节点
            node = Node(item)
            if not self.root:      # 如果根节点为空,直接将根节点取为插入节点
                self.root = node
                return
            queue = [self.root]      #构建队列,完成树已有节点遍历
            while queue:
                cur = queue.pop(0)      # 从队列头取出一个节点
                if cur.lchild == None:      # 如果左节点为空,直接插入,退出循环
                    cur.lchild = node
                    return
                else:
                    queue.append(cur.lchild)      # 否则,将左节点尾插到队列中
                if cur.rchild == None:      # 同样判断右节点
                    cur.rchild = node
                    return
                else:
                    queue.append(cur.rchild)
            
    
  • 广度优先遍历(Breadth First Search,BFS)
    从树的根节点开始,从上倒下,从左到右的遍历树的节点。

运用队列实现:

	def breadth_travel(self):      # 广度优先遍历
	    if not self.root:      # 如果树为空,返回空
	        return
	    queue = [self.root]      # 用队列方式遍历树
	    while queue:
	        cur = queue.pop(0)
	        print(cur.item, end = ' ')
	        if cur.lchild is not None:
	            queue.append(cur.lchild)
	        if cur.rchild is not None:
	            queue.append(cur.rchild)
  • 深度优先遍历(Depth First Search,DFS)
    沿着树的深度遍历树的节点,尽可能深的搜索树的分支。根据访问次序不同分为先序遍历(preorder)中序遍历(inorder)后序遍历(postorder)
    在这里插入图片描述

    1)先序遍历
    根节点 -> 左节点 ->右节点
    实现方式:

    	def preorder(self, node):      # 深度优先遍历(先序遍历)
    	    if node == None:      # 递归终止条件
    	        return
    	    print(node.item, end = ' ')     # 根节点
    	    self.preorder(node.lchild)      # 左节点
    	    self.preorder(node.rchild)      # 右节点
    
        def preorder_stack(self, root):      # 利用栈实现深度优先遍历(先序遍历)
            if root == None:
                return
            stack = [root]      # 创建栈
            while stack:
                cur = stack.pop()      # 打印当前节点
                print(cur.item, end = ' ')
                if cur.rchild is not None:      # 先将右节点压入,则后输出
                    stack.append(cur.rchild)
                if cur.lchild is not None:      # 后将左节点压入,将先输出
                    stack.append(cur.lchild)
    

    2)中序遍历
    左节点 -> 根节点 ->右节点
    实现方式:

	def inorder(self, node):      # 深度优先遍历(中序遍历)
	    if node == None:      # 递归终止条件
	        return
	    self.inorder(node.lchild)      # 左节点
	    print(node.item, end = ' ')    # 根节点
	    self.inorder(node.rchild)      # 右节点

    def inorder_stack(self, root):      # 利用栈实现深度优先遍历(中序遍历)
        if root == None:      
            return
        stack = [root]      # 创建栈
        node = root.lchild
        while stack or node: # stack为空时,仍有最后一个可能不为空的node需要输出
            while node:      # 一直压入左节点
                stack.append(node)
                node = node.lchild
            # 退出循环时,栈内最后一个节点没有左节点,node=None
            cur = stack.pop()
            print(cur.item, end = ' ')      # 打印该节点
            node = cur.rchild     # 开始搜索该节点右子树

3)后序遍历
左节点 -> 右节点 ->根节点
实现方式:

	 def postorder(self, node):      # 深度优先遍历(后序遍历)
	    if node == None:      # 递归终止条件
	        return
	    self.postorder(node.lchild)      # 左节点
	    self.postorder(node.rchild)      # 右节点
	    print(node.item, end = ' ')      # 根节点

    def postorder_stack(self, root):      # 利用栈实现深度优先遍历(后序遍历)
        if root == None:     
            return
        stack1 = [root]      # 用来遍历树
        stack2 = []      # 用来记录遍历的序号
        while stack1:
            node = stack1.pop()
            if node.lchild is not None:
                stack1.append(node.lchild)
            if node.rchild is not None:
                stack1.append(node.rchild)
            stack2.append(node)      # stack2 实际记录顺序为根节点->右节点->左节点 
        while stack2:      # 以栈的方式依次弹出stack2中的节点,输出顺序为记录顺序的倒序,即为后序遍历
            cur = stack2.pop()
            print(cur.item, end = ' ')
  • 广度优先与深度优先比较

    深度优先搜索:不全部保留结点,占用空间少;主要通过栈或递归实现,有回溯操作(即有入栈、出栈操作),运行速度慢。
    广度优先搜索:保留全部结点,占用空间大; 主要通过队列实现,无回溯操作(即无入栈、出栈操作),运行速度快。

  • 根据已有排序画出树
    先序,中序,后序中任意包括中序的两个可以确定一个树。必须要中序因为中序排列中根节点区分了左右节点。
    要点在于:利用前序或后序确定根节点,在中序中找到该节点,完成左右子树区分,再代回前序或后序找到左右子树的根节点,以此类推。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值