数据结构与算法(python):树结构
参考自 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.1 树结构相关术语
术语 | 说明 |
---|---|
根Root | 树中唯一一个没有入边的节点 |
路径Path | 由边依次连接在一起的节点的有序列表 |
子节点Children | 入边均来自于同一个节点的若干节点, 称为这个节点的子节点 |
父节点Parent | 一个节点是其所有出边所连接节点的父节点 |
兄弟节点Sibling | 具有同一个父节点的节点之间称为兄弟节点 |
子树Subtree | 一个节点和其所有子孙节点, 以及相关边的集合 |
叶节点Leaf | 没有子节点的节点称为叶节点 |
层级Level | 从根节点开始到达一个节点的路径,所包含的边的数量, 称为这个节点的层级 |
高度 | 树中所有节点的最大层级称为树的高度 |
1.2 树的定义
树由若干节点, 以及两两连接节点的边组成, 并有如下性质:
- 其中一个节点被设定为根;
- 每个节点n(除根节点),都恰连接一条来自节点p的边, p是n的父节点;
- 每个节点从根开始的路径是唯一的,如果每个节点最多有两个子节点,这样的树称为“二叉树”
二、树的实现
2.1 嵌套列表实现
递归的嵌套列表实现二叉树, 由具有3个元素的 List 列表实现:
- 第1个元素为根节点的值;
- 第2个元素是左子树(所以也是一个列表);
- 第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 利用表达式解析树求值
由前述对子表达式的描述,可从树的底层子树开始,逐步向上层求值(递归算法),最终得到整个表达式的值。
【递归三要素】
- **基本结束条件:**叶节点是最简单的子树,没有左右子节点,其根节点的数据项即为子表达式树的值;
- **缩小规模:**将表达式树分为左子树、右子树,即为缩小规模;
- **调用自身:**分别调用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种遍历:
- 前序遍历(preorder):先访问根节点,再递归地前序访问左子树、最后前序访问右子树;(根左右)
ABDGHCEIF - 中序遍历(inorder):先递归地中序访问左子树,再访问根节点,最后中序访问右子树;(左根右)
GDHBAEICF - 后序遍历(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):树结构相关推荐
- 数据结构与算法python版 MOOC 第九周
九.树及算法-上 本系列博客基于" (北京大学)数据结构与算法python版"慕课,课程在中国大学慕课和bilibili上均可找到. 1. 内容 树结构的相关术语 树的表示方法:嵌 ...
- mooc数据结构与算法python版期末考试_数据结构与算法Python版-中国大学mooc-试题题目及答案...
数据结构与算法Python版-中国大学mooc-试题题目及答案 更多相关问题 婴儿出生一两天后就有笑的反应,这种笑的反应属于(). [判断题]填制原始凭证,汉字大写金额数字一律用正楷或草书书写,汉字大 ...
- python数据结构算法 北京大学_北京大学公开课《数据结构与算法Python版》
之前我分享过一个数据结构与算法的课程,很多小伙伴私信我问有没有Python版. 看了一些公开课后,今天特向大家推荐北京大学的这门课程:<数据结构与算法Python版>. 课程概述 很多同学 ...
- 数据结构与算法python描述_数据结构与算法——Python语言描述.pdf
数据结构与算法--Python语言描述.pdf 欢迎加入非盈利Python编学习交流程QQ群783462347,群里免费提供500+本Python书籍! 欢迎加入非盈利Python编程学习交流程QQ群 ...
- 《数据结构与算法 Python语言描述》 读书笔记
已经发布博客 <数据结构与算法 Python语言描述> 读书笔记 第二章 抽象数据类型和Python类 2.1 抽象数据类型abstract data type:ADT 2.1.1 使用编 ...
- 数据结构与算法 python版 之 递归三定律
#数据结构与算法 python版 之 谢尔宾斯基三角形 , 树叉 1.为了向阿西莫夫的"机器人三定律"直径,递归算法也总结出"三定律" 1递归算法必须有一个基本 ...
- 《数据结构与算法 Python语言实现》书评与学习心得
做为Python小白,本人几个月前读完了Mark Lutz的1400页巨著<Learning Python>(太TM啰嗦了,读过的请举手),本打算继续学习下一步<Programmin ...
- 数据结构与算法python语言实现-第四章答案
数据结构与算法python语言实现-第四章答案 4.1 def findmax(S, index=0):if index == len(S) - 1:return S[index]max=findma ...
- 数据结构python版 答案,中国大学 MOOC_数据结构与算法Python版_章节测验答案
中国大学 MOOC_数据结构与算法Python版_章节测验答案 更多相关问题 认识的本质是()A.肯定世界是可知的B.主体对客体的能动反映C.主体对客体的直观反映D.实践是 水灰比是影响混凝土()的主 ...
- mooc数据结构与算法python版期末测验_中国大学数据结构与算法Python版答案_MOOC慕课章节期末答案...
中国大学数据结构与算法Python版答案_MOOC慕课章节期末答案 更多相关问题 java.lang 包的 Character 类的 isJavaIdentifierStart 方法的功能是用来判断某 ...
最新文章
- 2020年人工神经网络第二次作业-参考答案第四题
- 关于js中的时间——计算时间差等
- C++学习路线和参考资料
- 高级语言程序设计用c语言描述答案,清华大学出版社-图书详情-《高级语言程序设计(C语言描述)第2版》...
- 华为鸿蒙消费者,王成录谈鸿蒙挑战和华为消费者业务崛起
- 后缀表达式的值(信息学奥赛一本通-T1331)
- oracle如何添加undo,ORACLE RAC 11G 添加以及删除UNDO表空间
- 如你以安全模式启动计算机,如何以安全模式启动计算机?
- android 上下文菜单详解
- 推荐 几个神级 Java 开源学习项目
- Ceph 知识摘录(Ceph对象存储网关中的索引工作原理)
- VirtualBox 删除虚拟机
- P1075 质因数分解
- 基于单片机的篮球计数器设计
- 同一个网段win10远程linux,Win10专业版系统在局域网内远程另外一台电脑教程
- 如何电脑开机自动宽带链接网络连接服务器,电脑开机怎么设置宽带自动连接_宽带连接设置方法 - 驱动管家...
- Apple MacPad Pro会是什么样?
- 联想原装系统OEM系统联想出厂系统联想原装系统Lenovo ThinkPad ThinkBook出厂预装系统原厂系统
- Android加密 看雪,Android加密与解密入门两题
- simpleJson处理api返回数据结构不确定的情况
热门文章
- 时间序列转二维图像方法及其应用研究综述
- 2022亚太数学杯数学建模竞赛C题(思路、程序......)
- c3p0和dbcp的使用和区别
- 熊市定投,牛市才有收获
- mysql数据漂移_数据库漂移-和数据库漂移相关的内容-阿里云开发者社区
- JS与DOM的兼容性
- charles ios设备 https代理配置
- HDU 6379 Invoker (2019-CCPC-秦皇岛站)DP
- [音乐下载] [115永久续期] 最终心跳回忆。。。(11G)
- linux服务器http进程CPU异常飙高(轮为免费矿工)