人工智能
python,大数据,机器学习,深度学习,计算机视觉
三、python算法篇(六)二叉树
前言
大学时候学数据结构总是看不懂,学二叉树有什么用?画图玩?相信有些工作了不研究数据结构的也不知道。
举个二叉树应用场景,很多都玩过象棋,电脑象棋模拟下一步该走哪就是把每一种策略给用数画出来,然后选最优方案,前面我有博文提到,人工智能的第二次浪潮:IBM深蓝的电脑象棋打败了人类棋手。(但是人工智能的第三次浪潮阿尔法狗围棋不是这个方法,围棋是深度学习)
树的定义
树:是一种抽象数据类型或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。
它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树(根朝上,叶朝下)。
二叉树的定义
1.二叉树:每个节点最多有两个子树的树结构。(通常子树被称作“左子树”left subtree和“右子树”right subtree)
2.完全二叉树:假设二叉树的高度为h,除了最底层(第h层)外其他上面各层(1~h-1层)的结点数都达到最大个数,最底层(第h层)有叶子结点并且叶子结点都是从左到右依次排布。
如图,高度为4,除了第4层,上面三层都满了。
注意:如果去掉G结点,则不是完全二叉树。
3.满二叉树:除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
直接看图体会!
二叉树性质
1.在二叉树的第i层上至多有2i-1个结点(i>0)
2.深度为k的二叉树至多有2k-1个结点(k>0)----- 即满二叉树有2k-1个结点
3.具有n个结点的完全二叉树的深度必为log2(n+1)
证:由性质2
深度x 的二叉树有结点y = 2^x-1个。令结点y=n时,求深度x
n = 2^x-1 解得x = log(n+1) ----- 计算机书籍一般都简记为log,不写底数
4.对于任意一颗二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0 = N2+1
证:如图,叶结点数N0=5(红色圈),度数为2的结点总数N2=4(蓝色圈)。可见N0=N2+1
5,对于完全二叉树,若从上到下、从左到右编号,则编号为i的结点,其左孩子编号必为2i,其右孩子编号必为2i+1,其双亲编号必为i/2(根节点即i=1时除外)
证:如图,以B结点(编号i=2)为例
二叉树的先序、中序、后序遍历
先序:根左右
中序:左根右
后序:左右根
[巧记]:先中后指根的先后遍历,左右永远都是先左后右。
故第一步确定根位置,第二步先左后右往依次放到对应位置即可。
二叉树由遍历确定一棵树
前面是给你一颗树让你写遍历结果,那么反过来给你遍历结果让你画树你会不会?
注意:先序中序后序至少给两个才能画出来树,而且给的两个必须有一个是中序遍历,为什么?因为先序是根左右,后序是左右根,这两个结果的根没有把左右分开没办法确定左右的位置,只有中序是左根右才把左右分开。
例:已知一颗树的遍历结果,先序:0 1 3 7 8 4 9 2 5 6, 中序:7 3 8 1 9 4 0 5 2 6,请写出它的后序遍历结果?
【思路】要根据先序和中序画出来树,再能写后序。
【分析】
先序:0 1 3 7 8 4 9 2 5 6 ------- 条件1
中序:7 3 8 1 9 4 0 5 2 6 ------- 条件2
先序:根 左 右 ------------ 条件3
中序:左 根 右 ------------- 条件4
(1)由条件1, 3知,根节点是0。 # 再将这个结果代入到条件2, 4中,得到
中序:7 3 8 1 9 4 0 5 2 6 ------- 条件2。 # 再将这个结果代入条件1,得到
先序:0 1 3 7 8 4 9 2 5 6 ------- 条件1
所以1 3 7 8 4 9 是根结点0的左子树,左子树的根为1,(同理2 5 6 为右子树,先不管)
(2)所以中序:7 3 8 1 9 4 0 5 2 6 ------- 条件2
先序:0 1 3 7 8 4 9 2 5 6 ------- 条件1
新根来了,3和4
(3)再由中序:7 3 8 1 9 4 0 5 2 6 ------- 条件2可以画出:
(4)同理根节点的右子树也可得到,最后画出树如下:
通过树可以得到后序遍历结果是:7 8 3 9 4 1 5 6 2 0
变式题:原题将已知条件的先序改为后序,求先序。即已知一颗树的遍历结果,后序:0 1 3 7 8 4 9 2 5 6, 中序:7 3 8 1 9 4 0 5 2 6,请写出它的先序遍历结果?思路原理一样,不再重复。
二叉树的操作(代码实现)------- 添加结点(插入)、遍历(查找)
( 遍历包括:广度优先遍历、先序、中序、后序遍历)
16_binary_tree.py代码如下:
class Node(object):
"""树的结点"""
def __init__(self, item):
self.elem = item
self.lchild = None
self.rchild = None
class Tree(object):
"""二叉树"""
def __init__(self):
self.root = None
# 添加结点,我们只考虑往二叉树的末尾插向着完全二叉树努力,不考虑复杂的头插、中间插
def add(self, item):# 往self二叉树里插元素item
node = Node(item)# 把要插入的元素item先变成结点node
if self.root is None:#特殊情况,空树
self.root = node
return
queue = [self.root]
while queue:
cur_node = queue.pop(0)
if cur_node.lchild is None:
cur_node.lchild = node
return
else:
queue.append(cur_node.lchild)
if cur_node.rchild is None:
cur_node.rchild = node
return
else:
queue.append(cur_node.rchild)
def breadth_travel(self):
"""广度遍历(原理思路和上面add函数添加结点相同)"""
if self.root is None:
return
queue = [self.root]
while queue:
cur_node = queue.pop(0)
print(cur_node.elem, end=" ")
if cur_node.lchild is not None:
queue.append(cur_node.lchild)
if cur_node.rchild is not None:
queue.append(cur_node.rchild)
def preorder(self, node):
"""先序遍历(递归法)"""
if node is None:#递归终止条件
return
print(node.elem, end=" ")#根
self.preorder(node.lchild)#左
self.preorder(node.rchild)#右
def inorder(self, node):
"""中序遍历"""
if node is None:
return
self.inorder(node.lchild)#左
print(node.elem, end=" ")#根
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=" ")#根
#test
if __name__ == "__main__":
tree = Tree()
tree.add(0)
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
tree.add(6)
tree.add(7)
tree.add(8)
print("广度遍历结果:")
tree.breadth_travel()
print("\n")#这语句换了两行。print()默认带有换行功能,print(" ")换一行
#先去掉上面print试试,再改成print(" ")试试。换行是因为上面breadth_travel函数代码print(, end=" ")最后结果没有换行。
print("先序遍历结果:")
tree.preorder(tree.root)
print("\n")
print("中序遍历结果:")
tree.inorder(tree.root)
print("\n")
print("后序遍历结果:")
tree.postorder(tree.root)
print(" ")
"""
运行结果:
--------------------------
广度遍历结果:
0 1 2 3 4 5 6 7 8
先序遍历结果:
0 1 3 7 8 4 2 5 6
中序遍历结果:
7 3 8 1 4 0 5 2 6
后序遍历结果:
7 8 3 4 1 5 6 2 0
--------------------------
"""