python数据结构与算法分析(三)

数据结构与算法分析(一)
数据结构与算法分析(二)

节点
节点是树的基础部分。它可以有自己的名字,我们称作“键”。节点也可以带有附加信息,我们称作“有效载荷”。有效载荷信息对于很多树算法来说不是重点,但它常常在使用树的应用中很重要。

边是树的另一个基础部分。两个节点通过一条边相连,表示它们之间存在关系。除了根节点以外,其他每个节点都仅有一条入边,出边则可能有多条。
根节点
根节点是树中唯一没有入边的节点。
路径
路径是由边连接的有序节点列表。比如,哺乳纲→食肉目→猫科→猫属→家猫就是一条路径。
子节点
一个节点通过出边与子节点相连。
父节点
一个节点是其所有子节点的父节点。
兄弟节点
具有同一父节点的节点互称为兄弟节点。
子树
一个父节点及其所有后代的节点和边构成一棵子树。
叶子节点
叶子节点没有子节点。
层数
节点n的层数是从根节点到n的唯一路径长度。
高度
树的高度是其中节点层数的最大值。

定义一:树由节点及连接节点的边构成。树有以下属性:
有一个根节点;
除根节点外,其他每个节点都与其唯一的父节点相连;
从根节点到其他每个节点都有且仅有一条路径;
如果每个节点最多有两个子节点,我们就称这样的树为二叉树
在这里插入图片描述
定义二:一棵树要么为空,要么由一个根节点和零棵或多棵子树构成,子树本身也是一棵树。每棵子树的根节点通过一条边连到父树的根节点。下图展示了树的递归定义。
从树的递归定义可知,图中的树至少有4个节点,因为三角形代表的子树必定有一个根节点。这棵树或许有更多的节点,但必须更深入地查看子树后才能确定。
在这里插入图片描述
创建并操作二叉树
BinaryTree()创建一个二叉树实例。
getLeftChild()返回当前节点的左子节点所对应的二叉树。
getRightChild()返回当前节点的右子节点所对应的二叉树。
setRootVal(val)在当前节点中存储参数val中的对象。
getRootVal()返回当前节点存储的对象。
insertLeft(val)新建一棵二叉树,并将其作为当前节点的左子节点。
insertRight(val)新建一棵二叉树,并将其作为当前节点的右子节点。

实现树的关键在于选择一个好的内部存储技巧。Python提供两种有意思的方式:第一种称作“列表之列表”,第二种称作“节点与引用”。

列表之列表
用“列表之列表”表示树时,先从Python的列表数据结构开始,编写前面定义的函数。尽管为列表编写一套操作的接口与已经实现的其他抽象数据类型有些不同,但是做起来很有意思,因为这会给我们提供一个简单的递归数据类型,供我们直接查看和检查。在“列表之列表”的树中,我们将根节点的值作为列表的第一个元素;第二个元素是代表左子树的列表;第三个元素是代表右子树的列表。下图展示了一棵简单的树及其对应的列表实现。
在这里插入图片描述
注意,可以通过标准的列表切片操作访问子树。树的根节点是myTree[0],左子树是myTree[1],右子树是myTree[2]。以下会话展示了如何使用列表创建树。一旦创建完成,就可以访问它的根节点、左子树和右子树。“列表之列表”表示法有个很好的性质,那就是表示子树的列表结构符合树的定义,这样的结构是递归的!由一个根节点和两个空列表构成的子树是一个叶子节点。还有一个很好的性质,那就是这种表示法可以推广到有很多子树的情况。如果树不是二叉树,则多一棵子树只是多一个列表。
在这里插入图片描述
在这里插入图片描述
BinaryTree函数构造一个简单的列表,它仅有一个根节点和两个作为子节点的空列表,如上述代码所示。要给树添加左子树,需要在列表的第二个位置加入一个新列表。请务必当心:如果列表的第二个位置上已经有内容了,我们要保留已有内容,并将它作为新列表的左子树。下图代码给出了插入左子树的Python代码。
在这里插入图片描述
在插入左子树时,先获取当前的左子树所对应的列表(可能为空),然后加入新的左子树,将旧的左子树作为新节点的左子树。这样一来,就可以在树的任意位置插入新节点。insertRight与insertLeft类似,如下代码所示。
在这里插入图片描述
为了完整地创建树的函数集,让我们来编写一些访问函数,用于读写根节点与左右子树,如下代码所示。
在这里插入图片描述

节点与引用
树的第二种表示法是利用节点与引用。我们将定义一个类,其中有根节点和左右子树的属性。这种表示法遵循面向对象编程范式。
采用“节点与引用”表示法时,可以将树想象成如图所示的结构。
在这里插入图片描述
首先定义一个简单的类,代码如图所示。“节点与引用”表示法的要点是,属性left和right会指向BinaryTree类的其他实例。举例来说,在向树中插入新的左子树时,我们会创建另一个BinaryTree实例,并将根节点的self.leftChild改为指向新树。
在这里插入图片描述
在代码中,构造方法接受一个对象,并将其存储到根节点中。正如能在列表中存储任何对象,根节点对象也可以成为任何对象的引用。就之前的例子而言,我们将节点名作为根的值存储。采用“节点与引用”法表示上图中的树,将创建6个BinaryTree实例。

基于根节点构建树所需要的函数。为了给树添加左子树,新建一个二叉树对象,将根节点的left属性指向新对象。代码给出了insertLeft函数的代码。
在这里插入图片描述
在插入左子树时,必须考虑两种情况。第一种情况是原本没有左子节点。此时,只需往树中添加一个节点即可。第二种情况是已经存在左子节点。此时,插入一个节点,并将已有的左子节点降一层。上述代码中的else语句处理的就是第二种情况。
insertRight函数也要考虑相应的两种情况:要么原本没有右子节点,要么必须在根节点和已有的右子节点之间插入一个节点。下面的代码给出了insertRight函数的代码。
在这里插入图片描述
为了完成对二叉树数据结构的定义,需要编写一些访问左右子节点与根节点的函数。
在这里插入图片描述

二叉树的应用
①解析树
将((7 + 3) ∗ (5-2))这样的数学表达式表示成解析树,如图所示。这是完全括号表达式,乘法的优先级高于加法和减法,但因为有括号,所以在做乘法前必须先做括号内的加法和减法。树的层次性有助于理解整个表达式的计算次序。在计算顶层的乘法前,必须先计算子树中的加法和减法。加法(左子树)的结果是10,减法(右子树)的结果是3。利用树的层次结构,在计算完子树的表达式后,只需用一个节点代替整棵子树即可。应用这个替换过程后,便得到如图所示的简化树。
在这里插入图片描述
在这里插入图片描述
构建解析树的第一步是将表达式字符串拆分成标记列表。需要考虑4种标记:左括号、右括号、运算符和操作数。
左括号代表新表达式的起点,所以应该创建一棵对应该表达式的新树。反之,遇到右括号则意味着到达该表达式的终点。操作数既是叶子节点,也是其运算符的子节点。此外,每个运算符都有左右子节点。
定义以下4条规则:
(1) 如果当前标记是(,就为当前节点添加一个左子节点,并下沉至该子节点;
(2) 如果当前标记在列表[’+’, ‘-’, ‘/’, ‘*’]中,就将当前节点的值设为当前标记对应的运算符;为当前节点添加一个右子节点,并下沉至该子节点;
(3) 如果当前标记是数字,就将当前节点的值设为这个数并返回至父节点;
(4) 如果当前标记是),就跳到当前节点的父节点。
编写Python代码前,我们先通过一个例子来理解上述规则。将表达式(3 + (4 ∗ 5))拆分成标记列表[’(’, ‘3’, ‘+’, ‘(’, ‘4’, ‘*’, ‘5’,’)’, ‘)’]。起初,解析树只有一个空的根节点,随着对每个标记的处理,解析树的结构和内容逐渐充实,如图所示。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值