算法与数据结构(python):树与二叉树

本文介绍了树和二叉树的基本概念,包括根节点、路径、父节点、子节点等,并详细讲解了二叉树的类型:满二叉树、完美二叉树和完全二叉树。此外,还探讨了如何用Python表示二叉树,以及使用递归实现先序、中序和后序遍历的方法。
摘要由CSDN通过智能技术生成

提示:专栏解锁后,可以查看该专栏所有文章。



树和二叉树

     这里先简单讲讲树的概念。树结构是一种包括 节点(nodes)和边(edges)的拥有层级关系的一种结构,它的形式和家谱树非常类似:

  • 根节点(root):树的最上层的节点,任何非空的树都有一个节点
  • 路径(path):从起始节点到终止节点经历过的边
  • 父亲(parent):除了根节点,每个节点的上一层边连接的节点就是它的父亲(节点)
  • 孩子(children):每个节点由边指向的下一层节点
  • 兄弟(siblings):同-一个父亲并且处在同一层的节点
  • 子树(subtree):每个节点包含它所有的后代组成的子树
  • 叶子节点(leaf node):没有孩子的节点成为叶子节点

二叉树

     了解完树的结构以后,我们来看树结构里一种简单但是 却比较常用的树-二叉树。二叉树是一种简单的树,它的每个节点最多只能包含两个孩子,以下都是一些合法的二叉树:

通过上边这幅图再来看几个二叉树相关的概念:
●节点深度(depth):节点对应的level数字
●树的高度(height):二叉树的高度就是level数+1,因为level从0开始计算的
●树的宽度(width):二叉树的宽度指的是包含最多节点的层级的节点数
●树的size:二叉树的节点总个数。

     一棵size为n的二叉树高度最多可以是n,最小的高度是lgn] + 1(完美二叉树),这里log以2为底简写为Ign,和算法导论保持一致。这个结果你只需要用高中的累加公式就可以得到。

     满二叉树(full binary tree)
     如果每个内部节点(非叶节点)都包含两个孩子,就成为满二叉树。下边是一些例子,它可以有多种形状:

     完美二叉树(perfect binary tree)
     当所有的叶子节点都在同一层就是完美二叉树,毫无间隙填充了h层。

     完全二叉树(complete binary tree)
     当一个高度为h的完美二叉树减少到h-1,并且最底层的槽被毫无间隙地从左到右填充,我们就叫它完全二叉树。下图就是完全二叉树的例子:

二叉树的表示

     说了那么多,那么怎么表示一棵二叉树呢?其实你发现会和链表有一些相似之处,一个节点,然后节点需要保存孩子的指针,我以构造下边这个二叉树为例子:我们先定义一个类表示节点:

class BinTreeNode(object):
    def __init__(self,data,left=None,right=None):
        self.data,self.left,self.right=data,left,right

     ``当然和链表类似,root 节点是我们的入口,于是乎定义一个二叉树:

class BinTree(object):
    def __init__(self, root=None):
        self.root=root

     怎么构造上图中的二叉树呢,似乎其他课本没找到啥例子,我自己定义了一种方法,首先我们输入节点信息,仔细看下边代码,叶子节点的left和right都是None,并且只有一个根节点A:


node_list=[
    {'data':'A','left':'B','right':'C','is root':True},
    {'data':'B','left':'D','right':'E','is root':False},
    {'data':'D','left':None,'right':None,'is root':False},
    {'data': 'E', 'left': 'H', 'right': None, 'is root': False},
    {'data': 'H', 'left': None, 'right': None, 'is root': False},
    {'data': 'C', 'left': 'F', 'right': 'G', 'is root': False},
    {'data': 'F', 'left': None, 'right': None, 'is root': False},
   {'data': 'G', 'left': 'I', 'right': 'J', 'is root': False},
   {'data': 'I', 'left': None, 'right': None, 'is root': False},
   {'data': 'J', 'left': None, 'right': None, 'is root': False}
]

class BinTreeNode(object):
    def __init__(self,data,left=None,right=None):
        self.data,self.left,self.right=data,left,right

class BinTree(object):
    def __init__(self, root=None):
        self.root=root

    @classmethod
    def build_from(cls,node_list):
        """
        :param node_list:{'data':'A','left':'B','right':'C','is root':'True'},。。
        :return:
        通过节点信息构造二叉树
        第一次遍历我们构造node节点
        第二次遍历 给根节点和叶子节点 赋值
        最后我们用root初始化这个类并返回一个对象
        """
        node_dict={}
        for node_data in node_list:
            data=node_data['data']
            node_dict[data]=BinTreeNode(data)
        for node_data in node_list:
            data = node_data['data']
            node=node_dict[data]

            if node_data['is root']:
                root=node

            node.left=node_dict.get(node_data['left'])
            node.right = node_dict.get(node_data['right'])
        return cls(root)

btree=BinTree.build_from(node_list)

print(btree)

     classmethod修饰符对应的函数不需要实例化,不需要self参数,但第一个参数需要是表示自身类的cls参数,可以来调用类的属性,类的方法,实例化对象等。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class A(object):
    bar = 1
    def func1(self):  
        print ('foo') 
    @classmethod
    def func2(cls):
        print ('func2')
        print (cls.bar)
        cls().func1()   # 调用 foo 方法
 
A.func2()               # 不需要实例化

     很多博客只是说@calssmethod的作用就是“可以不需要实例化,直接类名方法名()来调用。这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,,同时有利于命名空间的整洁”。很抽象,实具体作用如下:
     @classmethod的作用实际是可以在class内实例化class,一般使用在有工厂模式要求时。作用就是比如输入的数据需要清洗一遍再实例化,可以把清洗函数定义在class内部并加上@classmethod装饰器已达到减少代码的目的。总结起来就是: @class method可以用来为一个类创建一些预处理的实例。

二叉树的遍历

     不直到你有没有发现,二叉树其实是一种递归结构, 因为单独拿出来一个subtree子树出来,其实它还是一棵树。那遍历它就很方便啦,我们可以直接用递归的方式来遍历它。但是当处理顺序不同的时候,树又分为三种遍历方式:

  • 先(根)序遍历:先处理根,之后是左子树,然后是右子树
  • 中(根)序遍历:先处理左子树,之后是根,最后是右子树
  • 后(根)序遍历:先处理左子树,之后是右子树,最后是根

node_list=[
    {'data':'0','left':'1','right':'2','is root':True},
    {'data':'1','left':'3','right':'4','is root':False},
    {'data':'2','left':'5','right':'6','is root':False},
    {'data': '3', 'left': '7', 'right': '8', 'is root': False},
    {'data': '4', 'left': '9', 'right': None, 'is root': False},
    {'data': '5', 'left': None, 'right': None, 'is root': False},
    {'data': '6', 'left': None, 'right': None, 'is root': False},
   {'data': '7', 'left': None, 'right': None, 'is root': False},
   {'data': '8', 'left': None, 'right': None, 'is root': False},
   {'data': '9', 'left': None, 'right': None, 'is root': False}
]

class BinTreeNode(object):
    def __init__(self,data,left=None,right=None):
        self.data,self.left,self.right=data,left,right

class BinTree(object):
    def __init__(self, root=None):
        self.root=root

    @classmethod
    def build_from(cls,node_list):
        """
        :param node_list:{'data':'A','left':'B','right':'C','is root':'True'},。。
        :return:
        通过节点信息构造二叉树
        第一次遍历我们构造node节点
        第二次遍历 给根节点和叶子节点 赋值
        最后我们用root初始化这个类并返回一个对象
        """
        node_dict={}
        for node_data in node_list:
            data=node_data['data']
            node_dict[data]=BinTreeNode(data)
        for node_data in node_list:
            data = node_data['data']
            node=node_dict[data]

            if node_data['is root']:
                root=node

            node.left=node_dict.get(node_data['left'])
            node.right = node_dict.get(node_data['right'])
        return cls(root)

    def preorder_trav(self,subtree):
        """
        先序遍历:根左右
        :param subtree:root
        :return:
        """
        if subtree is not None:
            print(subtree.data)#递归函数里先处理根
            self.preorder_trav(subtree.left)#递归处理左子树
            self.preorder_trav(subtree.right)#递归处理右子树

    def cenorder_trav(self,subtree):
        """
        中序遍历 左根右

        :param subtree:root
        :return:
        """
        if subtree is not None:
            self.cenorder_trav(subtree.left)  # 递归处理左子树
            print(subtree.data)  # 递归函数里先处理根
            self.cenorder_trav(subtree.right)  # 递归处理右子树

    def afterorder_trav(self, subtree):
        """
        后序排序:左右根

        :param subtree:root
        :return:
        """
        if subtree is not None:
            self.afterorder_trav(subtree.left)  # 递归处理左子树
            self.afterorder_trav(subtree.right)  # 递归处理右子树
            print(subtree.data)  # 递归函数里先处理根

    def levelorder_trav(self, subtree):
        """
        层次遍历:
        二叉树的层次遍历即从上往下、从左至右依次打印树的节点。
         其思路就是将二叉树的节点加入队列,出队的同时将其非空左右孩子依次入队,出队到队列为空即完成遍历。
        :param subtree:
        :return:
        """
        outList = []
        queue = [subtree]
        while queue != [] and subtree:
            outList.append(queue[0].data)
            if queue[0].left != None:
                queue.append(queue[0].left)
            if queue[0].right != None:
                queue.append(queue[0].right)
            queue.pop(0)
        print(outList)
        #return outList

    def reverse(self,subtree):
        """
        :param subtree: 反转二叉树
        :return:
        """
        if subtree is not None:
            subtree.left,subtree.right=subtree.right,subtree.left
            self.reverse(subtree.left)
            self.reverse(subtree.right

btree=BinTree.build_from(node_list)

btree.preorder_trav(btree.root)#前序
btree. cenorder_trav(btree.root)#中序
btree. afterorder_trav(btree.root)#后序
btree. levelorder_trav(btree.root)#层次遍历
btree. reverse(btree.root)#反转
btree.levelorder_trav(btree.root)#印反转后的层次遍历

在这里插入图片描述
作者:电气-余登武

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总裁余(余登武)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值