参考自 MOOC数据结构与算法Python版

目录

  • 一、什么是树
    • 1.1 树结构相关术语
    • 1.2 树的定义
  • 二、树的实现
    • 2.1 嵌套列表实现
    • 2.2 链表实现
  • 三、树的应用:表达式解析
    • 3.1 解析树(语法树)
    • 3.2 建立表达式解析树
    • 3.3 利用表达式解析树求值
  • 四、树的遍历 Tree Traversals
    • 4.1 树的三种遍历
    • 4.2 利用后序遍历进行表达式求值
    • 4.3 利用中序遍历生成全括号中缀表达式

一、什么是树

树是一种基本的“非线性”数据结构。跟自然界中的树一样, 数据结构树也分为:根、 枝和叶等三个部分。一般数据结构的图示把根放在上方,叶放在下方。
分类树的三个特征:

  1. 分类体系是层次化的
  2. 一个节点的子节点与另一个节点的子节点相互之间是隔离、独立的
  3. 每一个叶节点都具有唯一性

1.1 树结构相关术语

术语 说明
根Root 树中唯一一个没有入边的节点
路径Path 由边依次连接在一起的节点的有序列表
子节点Children 入边均来自于同一个节点的若干节点, 称为这个节点的子节点
父节点Parent 一个节点是其所有出边所连接节点的父节点
兄弟节点Sibling 具有同一个父节点的节点之间称为兄弟节点
子树Subtree 一个节点和其所有子孙节点, 以及相关边的集合
叶节点Leaf 没有子节点的节点称为叶节点
层级Level 从根节点开始到达一个节点的路径,所包含的边的数量, 称为这个节点的层级
高度 树中所有节点的最大层级称为树的高度

1.2 树的定义

树由若干节点, 以及两两连接节点的边组成, 并有如下性质:

  • 其中一个节点被设定为根;
  • 每个节点n(除根节点),都恰连接一条来自节点p的边, p是n的父节点;
  • 每个节点从根开始的路径是唯一的,如果每个节点最多有两个子节点,这样的树称为“二叉树”

二、树的实现

2.1 嵌套列表实现

递归的嵌套列表实现二叉树, 由具有3个元素的 List 列表实现:

  1. 第1个元素为根节点的值;
  2. 第2个元素是左子树(所以也是一个列表);
  3. 第3个元素是右子树(所以也是一个列表)。

[root,left,right][root, left, right][root,left,right]
【例】实现一个6节点的二叉树
根是myTree[0],左子树myTree[1],右子树myTree[2],子树的结构与树相同,是一种递归数据结构。
我们通过定义一系列函数来辅助操作嵌套列表

函数 含义
BinaryTree 创建仅有根节点的二叉树
insertLeft/insertRight 将新节点插入树中作为其直接的左/右子节点
get/setRootVal 则取得或返回根节点
getLeft/RightChild 返回左/右子树

【代码】:

def BinaryTree(r):return [r,[],[]]def insertLeft(root, newBranch):t = root.pop(1)if len(t)>1:root.insert(1,[newBranch,t,[]])else:root.insert(1,[newBranch,[],[]])return root#在根节点与子树节点之间插入
def insertRight(root, newBranch):t = root.pop(2)if len(t) > 1:root.insert(2,[newBranch,[],t])else:root.insert(2,[newBranch,[],[]])return rootdef getRootVal(root):return root[0]def setRootVal(root, newVal):root[0] = newValdef getLeftChild(root):return root[1]def getRightChild(root):return root[2]r = BinaryTree(3)
insertLeft(r,4)
insertLeft(r,5)
insertRight(r,6)
insertRight(r,7)
l = getLeftChild(r)
print(l)  #[5, [4, [], []], []]
setRootVal(l,9)
print(r) #[3, [9, [4, [], []], []],[7, [], [6, [], []]]]
insertLeft(l,11)
print(r) #[3, [9, [11, [4, [], []], []], []], [7, [], [6, [], []]]]
print(getRightChild(getRightChild(r)))#[6, [], []]

图示结果如下:

2.2 链表实现

同样可以用节点链接法来实现树,每个节点保存根节点的数据项,以及指向左右子树的链接。

  • 成员key保存根节点数据项
  • 成员left/rightChild则保存指向左/右子树的引用(同样是BinaryTree对象)
class BinaryTree:def __init__(self,rootObj):self.key = rootObjself.leftChild = Noneself.rightChild = Nonedef insertLeft(self,newNode):if self.leftChild == None:self.leftChild = BinaryTree(newNode)else: #在根节点与原左子树之间插入t = BinaryTree(newNode)t.leftChild = self.leftChildself.leftChild = tdef insertRight(self, newNode):if self.rightChild == None:self.rightChild = BinaryTree(newNode)else:t = BinaryTree(newNode)t.rightChild = self.rightChildself.rightChild = tdef getRightChild(self):return self.rightChilddef getLeftChild(self):return self.leftChilddef setRootVal(self,obj):self.key = objdef getRootVal(self):return self.keyr = BinaryTree('a')
r.insertLeft('b')
r.insertRight('c')
r.getRightChild().setRootVal('hello')
r.getLeftChild().insertRight('d')

上述代码用图表示如下:

三、树的应用:表达式解析

3.1 解析树(语法树)

将树用于表示语言中句子, 可以分析句子的各种语法成分, 对句子的各种成分进行处理。

  • 语法分析树
    主谓宾,定状补
  • 程序设计语言的编译
    词法、语法检查
    从语法树生成目标代码
  • 自然语言处理
    机器翻译、语义理解
  • 我们还可以将表达式表示为树结构
    叶节点保存操作数,内部节点保存操作符
    由于括号的存在,需要计算*的话,就必须先计算7+3和5-2,表达式层次决定计算的优先级:越底层的表达式,优先级越高。树中每个子树都表示一个子表达式:
    我们可以利用树结构实现如下功能:
  • 从全括号表达式构建表达式解析树
  • 利用表达式解析树对表达式求值
  • 从表达式解析树恢复原表达式的字符串形式

3.2 建立表达式解析树

(1) 全括号表达式要分解为单词Token列表
其单词分为括号“() ”、操作符“±*/”和操作数“0~9”这几类,左括号就是表达式的开始,而右括号是表达式的结束。
(3+(4∗5))(3+(4*5))(3+(4∗5))分解为单词表
[′(′,′3′,′+′,′(′,′4′,′∗′,′5′,′)′,′)′]['(', '3', '+', '(', '4', '*', '5',')', ')'][′(′,′3′,′+′,′(′,′4′,′∗′,′5′,′)′,′)′]

(2)创建表达式解析树过程
创建空树,当前节点为根节点
读入’(’, 创建了左子节点,当前节点下降
读入’3’,当前节点设置为3, 上升到父节点
读入’+’,当前节点设置为+, 创建右子节点,当前节点下降

读入’(’, 创建左子节点,当前节点下降
读入’4’,当前节点设置为4, 上升到父节点
读入’’,当前节点设置为, 创建右子节点,当前节点下降
读入’5’,当前节点设置为5, 上升到父节点
读入’)’, 上升到父节点
读入’)’,再上升到父节点
总结:从左到右扫描全括号表达式的每个单词,依据规则建立解析树。

  • 如果当前单词是"(":为当前节点添加一个新节点作为其左子节点, 当前节点下降为这个新节点;
  • 如果当前单词是操作符"+,-,/,":将当前节点的值设为此符号,为当前节点添加一个新节点作为其右子节点, 当前节点下降为这个新节点;
  • 如果当前单词是操作数:将当前节点的值设为此数, 当前节点上升到父节点;
  • 如果当前单词是")":则当前节点上升到父节点。

从图示过程中我们看到, 创建树过程中关键的是对当前节点的跟踪:

  • 创建左右子树可调用insertLeft/Right
  • 当前节点设置值,可以调用setRootVal
  • 下降到左右子树可调用getLeft/RightChild

但是, 上升到父节点,这个没有方法支持!我们可以用一个来记录跟踪父节点:

  • 当前节点下降时,将下降前的节点push入栈
  • 当前节点需要上升到父节点时,上升到pop出栈的节点即可!

【代码】:

def buildParseTree(fpexp):flist = fpexp.split()pStack = Stack()eTree = BinaryTree('')pStack.push(eTree)  #入栈下降for i in fplist:if i == '(': #表达式开始currentTree.insertLeft('')pStack.push(currentTree)  #入栈下降currentTree = currrentTree.getLeftChild()elif i not in ['+','-','*','/',')']: #操作数currentTree.setRootval(int(i))parent = pStack.pop() #出栈上升currentTree = parentelif i in ['+','-','*','/']: #操作符currentTree.setRootVal(i)currentTree.insertRight('')pStack.push(currentTree) #入栈下降currentTree = currentTree.getRightChild()elif i == ')': #表达式结束currentTree = pStack.pop() #出栈上升else:raise ValueErrorreturn eTree

3.3 利用表达式解析树求值

由前述对子表达式的描述,可从树的底层子树开始,逐步向上层求值(递归算法),最终得到整个表达式的值。
【递归三要素】

  1. **基本结束条件:**叶节点是最简单的子树,没有左右子节点,其根节点的数据项即为子表达式树的值;
  2. **缩小规模:**将表达式树分为左子树、右子树,即为缩小规模;
  3. **调用自身:**分别调用evaluate计算左子树和右子树的值,然后将左右子树的值依根节点的操作符进行计算,从而得到表达式的值.
import operator
def evaluate(parseTree):opers = {'+':operator.add, '-':operator.sub,'*':operator.mul,'/':operator.truediv}#缩小规模leftC = praseTree.getLeftChild()rightC = parseTree.getRightChild()if leftC and rightC:fn = opers[parseTree.getRootVal()]#递归调用return fn(evaluate(leftC),evaluate(rightC))else:return parseTree.getRootVal()  #基本结束条件

四、树的遍历 Tree Traversals

4.1 树的三种遍历

对一个数据集中的所有数据项进行访问的
操作称为“遍历Traversal”,线性数据结构中, 对其所有数据项的访问比较简单直接,按照顺序依次进行即可。树的非线性特点, 使得遍历操作较为复杂,我们按照对节点访问次序的不同来区分3种遍历:

  1. 前序遍历(preorder):先访问根节点,再递归地前序访问左子树、最后前序访问右子树;(根左右)
    ABDGHCEIF
  2. 中序遍历(inorder):先递归地中序访问左子树,再访问根节点,最后中序访问右子树;(左根右)
    GDHBAEICF
  3. 后序遍历(postorder):先递归地后序访问左子树,再后序访问右子树,最后访问根节点。(左右根)
    GHDBIEFCA

【代码】

def preorder(tree):if tree:print(tree.getRootVal())preorder(tree.getLeftChild())preorder(tree.getRightChild())     def posorder(tree):if tree !=None:posorder(tree.getLeftChild())posorder(tree.getRightChild())print(tree.getRootVal())def inorder(tree):if tree !=None:inorder(tree.getLeftChild())print(tree.getRootVal())inorder(tree.getRightChild())

4.2 利用后序遍历进行表达式求值

采用后序遍历法重写表达式求值代码

def postordereval(tree):opers = {'+':operator.add, '-':operator.sub,'*':operator.mul,'/':operator.truediv}res1 = Noneres2 = Noneif tree:  #基本结束条件#缩小规模res1 = postordereval(tree.getLeftChild())res2 = postordereval(tree.getRightChild())if res1 and res2:     #递归调用return opers[tree.getRootVal()](res1,res2)else:return tree.getRootVal()  #基本结束条件

4.3 利用中序遍历生成全括号中缀表达式

下列代码中对每个数字也加了括号,请自行修改代码去除。

def printexp(tree):sVal = ""if tree:sVal = '(' + printexp(tree.getLeftChild())sVal += str(tree.getRoolVal())sVal += printexp(tree.getRightChild()) +')'return sVal

数据结构与算法(python):树结构相关推荐

  1. 数据结构与算法python版 MOOC 第九周

    九.树及算法-上 本系列博客基于" (北京大学)数据结构与算法python版"慕课,课程在中国大学慕课和bilibili上均可找到. 1. 内容 树结构的相关术语 树的表示方法:嵌 ...

  2. mooc数据结构与算法python版期末考试_数据结构与算法Python版-中国大学mooc-试题题目及答案...

    数据结构与算法Python版-中国大学mooc-试题题目及答案 更多相关问题 婴儿出生一两天后就有笑的反应,这种笑的反应属于(). [判断题]填制原始凭证,汉字大写金额数字一律用正楷或草书书写,汉字大 ...

  3. python数据结构算法 北京大学_北京大学公开课《数据结构与算法Python版》

    之前我分享过一个数据结构与算法的课程,很多小伙伴私信我问有没有Python版. 看了一些公开课后,今天特向大家推荐北京大学的这门课程:<数据结构与算法Python版>. 课程概述 很多同学 ...

  4. 数据结构与算法python描述_数据结构与算法——Python语言描述.pdf

    数据结构与算法--Python语言描述.pdf 欢迎加入非盈利Python编学习交流程QQ群783462347,群里免费提供500+本Python书籍! 欢迎加入非盈利Python编程学习交流程QQ群 ...

  5. 《数据结构与算法 Python语言描述》 读书笔记

    已经发布博客 <数据结构与算法 Python语言描述> 读书笔记 第二章 抽象数据类型和Python类 2.1 抽象数据类型abstract data type:ADT 2.1.1 使用编 ...

  6. 数据结构与算法 python版 之 递归三定律

    #数据结构与算法 python版 之 谢尔宾斯基三角形 , 树叉 1.为了向阿西莫夫的"机器人三定律"直径,递归算法也总结出"三定律" 1递归算法必须有一个基本 ...

  7. 《数据结构与算法 Python语言实现》书评与学习心得

    做为Python小白,本人几个月前读完了Mark Lutz的1400页巨著<Learning Python>(太TM啰嗦了,读过的请举手),本打算继续学习下一步<Programmin ...

  8. 数据结构与算法python语言实现-第四章答案

    数据结构与算法python语言实现-第四章答案 4.1 def findmax(S, index=0):if index == len(S) - 1:return S[index]max=findma ...

  9. 数据结构python版 答案,中国大学 MOOC_数据结构与算法Python版_章节测验答案

    中国大学 MOOC_数据结构与算法Python版_章节测验答案 更多相关问题 认识的本质是()A.肯定世界是可知的B.主体对客体的能动反映C.主体对客体的直观反映D.实践是 水灰比是影响混凝土()的主 ...

  10. mooc数据结构与算法python版期末测验_中国大学数据结构与算法Python版答案_MOOC慕课章节期末答案...

    中国大学数据结构与算法Python版答案_MOOC慕课章节期末答案 更多相关问题 java.lang 包的 Character 类的 isJavaIdentifierStart 方法的功能是用来判断某 ...

最新文章

  1. 2020年人工神经网络第二次作业-参考答案第四题
  2. 关于js中的时间——计算时间差等
  3. C++学习路线和参考资料
  4. 高级语言程序设计用c语言描述答案,清华大学出版社-图书详情-《高级语言程序设计(C语言描述)第2版》...
  5. 华为鸿蒙消费者,王成录谈鸿蒙挑战和华为消费者业务崛起
  6. 后缀表达式的值(信息学奥赛一本通-T1331)
  7. oracle如何添加undo,ORACLE RAC 11G 添加以及删除UNDO表空间
  8. 如你以安全模式启动计算机,如何以安全模式启动计算机?
  9. android 上下文菜单详解
  10. 推荐 几个神级 Java 开源学习项目
  11. Ceph 知识摘录(Ceph对象存储网关中的索引工作原理)
  12. VirtualBox 删除虚拟机
  13. P1075 质因数分解
  14. 基于单片机的篮球计数器设计
  15. 同一个网段win10远程linux,Win10专业版系统在局域网内远程另外一台电脑教程
  16. 如何电脑开机自动宽带链接网络连接服务器,电脑开机怎么设置宽带自动连接_宽带连接设置方法 - 驱动管家...
  17. Apple MacPad Pro会是什么样?
  18. 联想原装系统OEM系统联想出厂系统联想原装系统Lenovo ThinkPad ThinkBook出厂预装系统原厂系统
  19. Android加密 看雪,Android加密与解密入门两题
  20. simpleJson处理api返回数据结构不确定的情况

热门文章

  1. 时间序列转二维图像方法及其应用研究综述
  2. 2022亚太数学杯数学建模竞赛C题(思路、程序......)
  3. c3p0和dbcp的使用和区别
  4. 熊市定投,牛市才有收获
  5. mysql数据漂移_数据库漂移-和数据库漂移相关的内容-阿里云开发者社区
  6. JS与DOM的兼容性
  7. charles ios设备 https代理配置
  8. HDU 6379 Invoker (2019-CCPC-秦皇岛站)DP
  9. [音乐下载] [115永久续期] 最终心跳回忆。。。(11G)
  10. linux服务器http进程CPU异常飙高(轮为免费矿工)