二叉树的抽象数据类型
一. 理论分析
- 结点是二叉树的基础,通常主要用结点保存与应用有关的信息.
- 作为二叉树的表示,还需要记录二叉树的结构信息,至少需要保证能检查结点的父子关系,例如,能从一个结点找到其左/右子结点
下面 一个基本的二叉树抽象数据类型的定义:
ADT BinTree: # 一个二叉树抽象数据类型
BinTree(self, data, left, right) # 构造操作,创建一个新二叉树
is_empty(self) # 判断self是否为一个空二叉树
num_nodes(self) # 求二叉树的结点个数
data(self) # 获取二叉树根存储的数据
left(self) # 获取二叉树的左子树
right(self) # 获取二叉树的右子树
set_left(self, btree) # 用btree取代原来的左子树
set_right(self, btree) # 用btree取代原来的右子树
traversal(self) # 遍历二叉树中各结点数据的迭代器
forall(self, op) # 对二叉树中的每个结点的数据执行操作op
二叉树的基本操作应该包括创建二叉树.构造一棵二叉树需要基于两棵已有的二叉树和希望保存在树根结点的一项数据. 空二叉树的表示是一个问题,由于空二叉树没有信息室,在实现中可以用某个特殊值表示,例如在python里用None表示.实际实现也可以另外引进一个专门表示二叉树的结构,把树结点置于其管辖之下.
二. 遍历二叉树
-
每棵二叉树有唯一的根结点,可以将其看作这棵二叉树的唯一标识,是基于树结构的处理过程的入口. 从根结点出发应该能找到树中的所有信息,其基础是从父结点找到两个子结点.
因此,实际中常用二叉树的根结点代表这棵二叉树,其左右子树由它们的根结点代表
-
二叉树中的每个结点可能保存一些数据,因此也是一种汇集型的数据结构,对任何汇集结构,都有逐一处理其中所存数据元素的问题,即遍历其中元素,遍历一棵二叉树,就是按某种系统化的方式访问二叉树里的每个结点一次.这一过程可以基于二叉树的基本操作实现,遍历中可能操作结点里的数据.
-
很多复杂的二叉树操作需要基于遍历实现,例如找一个结点的父结点,在二叉树里做这件事就像在单链表里找前一结点
-
如果二叉树每个结点有唯一标识, 给定的二叉树唯一确定了它的先根序咧,后根序列和对称序列. 但给定了一棵二叉树的任一种遍历序列,都无法唯一确定相应的二叉树
-
如果知道一棵二叉树的对称序列,又知道另一遍历序列(无论是先根还是后根序列),就可以唯一确定相应的二叉树
遍历二叉树的基本方式
-
深度优先遍历
- 先根序遍历(DLR)-----> 先根序咧
- 中根序遍历(LDR), 也称为对称序-------->中根序列
- 后根序遍历(LRD)------>后根序列
-
宽度优先遍历, 也称为层次顺序遍历
先根序遍历:ABDHEICFJKG(从最上面的根开始, 一直找一直找到最下面的根)
中根序遍历:DHBEIAJFKCG(从最下面的叶子开始)
后根序遍历:HDIEBJKFGCA(从最下面的叶子开始)
宽度优先遍历:ABCDEFGHIJK
遍历与搜索
一棵二叉树可以看做一个状态空间,根结点对应状态空间的初始状态,父子结点连接状态的邻接关系.
一次二叉树遍历也就是一次覆盖整个状态空间的搜索. 有关状态空间搜索的方法和实现技术都可以原样移植到二叉树遍历问题中,例如:递归的搜索方法,基于栈的非递归搜索(深度优先遍历), 基于队列的宽度优先搜索对应于层次序遍历.
遍历是一种系统化的结点枚举过程,实际中未必需要检查全部结点,有时需要在找到所需信息后结束.在二叉树上也可能需要做这种搜索.
在状态空间的搜索过程中记录从一个状态到另一状态的联系,将其看作结点间链接, 就会发现这种搜索过程实际上构造出了一棵树,称为搜索树,一般而言,这样形成的结构不是二叉树而是一般的树.
二叉树的list实现
- 简单看,二叉树结点就是一个三元组,元素是左右子树和本结点数据.python的list或tuple都可以用于组合这样的三个元素,两者的差异仅在于变动性,如果要实现一种非变动二叉树,可以用tuple作为组合机制,要实现可以修改结构的二叉树就应该用list.
- 设计和实现
- 二叉树是递归结构,python的list也是递归结构,因此基于list类型很容易实现二叉树
- 空树用None表示
- 非空二叉树用包含三个元素的表[d,l,r]表示
[A, [B, None,None], [C,[D,[F,None, None],[G, None, None], [E, [I, None, None], [H, None, None]]]]
# 二叉树的实现和操作
def BinTree(data, left=None, right=None):
return (data, left, right)
def is_empty_BinTree(btree):
return btree is None
def root(btree):
return btree[0]
def left(btree):
return btree[1]
def right(btree):
return btree[2]
def set_root(btree, data):
btree[0]=data
def set_left(btree, left):
btree[1] = left
def set_right(btree, right):
btree[2]= right
构造函数BinTree为后两个参数提供默认值,主要是为了使用方便,表示不给左右子树时这两个子树为空, 基于构造函数的嵌套调用,可以做出任意复杂的二叉树,例如:
t1 = BinTree(2, BinTree(4), BinTree(8))
这相当于写:t1 = [2,[4, None, None], [8, None, None]]
可以修改二叉树中的任何部分,例如
set_left(left(t1), BinTree(5))
其中把t1的左子树的左子树换成了BinTree(5), 使t1的值变成: [2,[4,[5,None, None]], [8,None, None]]
这是一棵高度为2的二叉树,list内部的嵌套层数等于树的高度
t1的左子树的左子树换成了BinTree(5), 使t1的值变成: [2,[4,[5,None, None]], [8,None, None]]
这是一棵高度为2的二叉树,list内部的嵌套层数等于树的高度