提示:专栏解锁后,可以查看该专栏所有文章。
树和二叉树
树
这里先简单讲讲树的概念。树结构是一种包括 节点(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)#印反转后的层次遍历
作者:电气-余登武