解析树(语法树)

  • 将树用于表示语言中句子, 可以分析句子的各种语法成分, 对句子的各种成分进行处理
  • 语法分析树
  • 程序设计语言的编译
    • 词法、语法检查
    • 从语法树生成目标代码
  • 自然语言处理
    • 机器翻译
    • 语义理解

表达式解析

\(((7+3)*(5-2))\)

  • 叶节点保存操作数,内部节点保存操作符
  • 树中每个子树都表示一个子表达式

构建解析树

定义规则

  • 如果当前标记是(,就为当前节点添加一个左子节点,并下沉至该子节点;
  • 如果当前标记在列表['+', '-', '/', '*']中,就将当前节点的值设为当前标记对应的运算符;为当前节点添加一个右子节点,并下沉至该子节点;
  • 如果当前标记是数字,就将当前节点的值设为这个数并返回至父节点;
  • 如果当前标记是),就跳到当前节点的父节点。

步骤

  1. 创建一棵空树。
  2. 读入第一个标记(。根据规则1,为根节点添加一个左子节点。
  3. 读入下一个标记3。根据规则3,将当前节点的值设为3,并回到父节点。
  4. 读入下一个标记+。根据规则2,将当前节点的值设为+,并添加一个右子节点。新节点成为当前节点。
  5. 读入下一个标记(。根据规则1,为当前节点添加一个左子节点,并将其作为当前节点。
  6. 读入下一个标记4。根据规则3,将当前节点的值设为4,并回到父节点。
  7. 读入下一个标记*。根据规则2,将当前节点的值设为*,并添加一个右子节点。新节点成为当前节点。
  8. 读入下一个标记5。根据规则3,将当前节点的值设为5,并回到父节点。
  9. 读入下一个标记)。根据规则4,将*的父节点作为当前节点。
  10. 读入下一个标记)。根据规则4,将+的父节点作为当前节点。因为+没有父节点,所以工作完成。

思路

  • 创建左右子树可调用insertLeft/Right
  • 当前节点设置值,可以调用setRootVal
  • 下降到左右子树可调用getLeft/RightChild
  • 上升到父节点,这个没有方法支持,用一个栈来记录跟踪父节点
    • 当前节点下降时,将下降前的节点push入栈
    • 当前节点需要上升到父节点时,上升到pop出栈的节点即可!

代码

class Stack:
def __init__(self):
self.items = [] def isEmpty(self):
return self.items == [] def push(self, item): # 将item加入栈顶,无返回值
return self.items.append(item) def pop(self): # 将栈顶数据项移除,并返回,栈被修改
return self.items.pop() def peek(self): # "窥视"栈顶数据项,返回栈顶的数但不移除,栈不被修改
return self.items[len(self.items)-1] def size(self):
return len(self.items) class BinaryTree:
def __init__(self, rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None def insertLeft(self, newNode):
if self.leftChild == None:
self.leftChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t def insertRignt(self, newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.rightChild = self.rightChild
self.rightChild = t def getRightChild(self):
return self.rightChild def getLeftChild(self):
return self.leftChild def setRootVal(self, obj):
self.key = obj def getRootVal(self):
return self.key def buildParseTree(fpexp):
fplist = fpexp.split()
pstack = Stack()
eTree = BinaryTree('')
# 入栈下降
pstack.push(eTree)
currentTree = eTree
for i in fplist:
# 表达式开始
if i == '(':
currentTree.insertLeft('')
pstack.push(currentTree) # 入栈下降
currentTree = currentTree.getLeftChild
elif i not in ['+', '-', '*', '/', ')']:
currentTree.setRootVal(int(i))
parent = pstack.pop() # 出栈上升
currentTree = parent
elif i in ['+', '-', '*', '/']:
currentTree.setRootVal(i)
currentTree.insertRignt('')
pstack.push(currentTree)
currentTree = currentTree.getRightChild()
elif i == ')':
currentTree = pstack.pop() # 出栈上升
else:
raise ValueError
return eTree

表达式解析树求值

  • 由于二叉树BinaryTree是一个递归数据结构, 自然可以用递归算法来处理

  • 求值函数evaluate的递归三要素

    • 基本结束条件:叶节点是最简单的子树,没有左右子节点,其根节点的数据项即为子表达式树的值
    • 缩小规模:将表达式树分为左子树、右子树,即为缩小规模
    • 调用自身:分别调用evaluate计算左子树和右子树的值,然后将左右子树的值依根节点的操作符进行计算,从而得到表达式的值
  • 一个增加程序可读性的技巧:函数引用

    import operator
    op= operator.add

代码

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

【数据结构与算法Python版学习笔记】树——二叉树的应用:解析树的更多相关文章

  1. 【数据结构与算法Python版学习笔记】引言

    学习来源 北京大学-数据结构与算法Python版 目标 了解计算机科学.程序设计和问题解决的基本概念 计算机科学是对问题本身.问题的解决.以及问题求解过程中得出的解决方案的研究.面对一 个特定问题,计 ...

  2. 【数据结构与算法Python版学习笔记】目录索引

    引言 算法分析 基本数据结构 概览 栈 stack 队列 Queue 双端队列 Deque 列表 List,链表实现 递归(Recursion) 定义及应用:分形树.谢尔宾斯基三角.汉诺塔.迷宫 优化 ...

  3. 【数据结构与算法Python版学习笔记】递归(Recursion)——定义及应用:分形树、谢尔宾斯基三角、汉诺塔、迷宫

    定义 递归是一种解决问题的方法,它把一个问题分解为越来越小的子问题,直到问题的规模小到可以被很简单直接解决. 通常为了达到分解问题的效果,递归过程中要引入一个调用自身的函数. 举例 数列求和 def ...

  4. 【数据结构与算法Python版学习笔记】树——相关术语、定义、实现方法

    概念 一种基本的"非线性"数据结构--树 根 枝 叶 广泛应用于计算机科学的多个领域 操作系统 图形学 数据库 计算机网络 特征 第一个属性是层次性,即树是按层级构建的,越笼统就越 ...

  5. 【数据结构与算法Python版学习笔记】树——利用二叉堆实现优先级队列

    概念 队列有一个重要的变体,叫作优先级队列. 和队列一样,优先级队列从头部移除元素,不过元素的逻辑顺序是由优先级决定的. 优先级最高的元素在最前,优先级最低的元素在最后. 实现优先级队列的经典方法是使 ...

  6. 【数据结构与算法Python版学习笔记】树——平衡二叉搜索树(AVL树)

    定义 能够在key插入时一直保持平衡的二叉查找树: AVL树 利用AVL树实现ADT Map, 基本上与BST的实现相同,不同之处仅在于二叉树的生成与维护过程 平衡因子 AVL树的实现中, 需要对每个 ...

  7. 【数据结构与算法Python版学习笔记】树——二叉查找树 Binary Search Tree

    二叉搜索树,它是映射的另一种实现 映射抽象数据类型前面两种实现,它们分别是列表二分搜索和散列表. 操作 Map()新建一个空的映射. put(key, val)往映射中加入一个新的键-值对.如果键已经 ...

  8. 【数据结构与算法Python版学习笔记】树——树的遍历 Tree Traversals

    遍历方式 前序遍历 在前序遍历中,先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树. 中序遍历 在中序遍历中,先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树. 后序遍 ...

  9. 【数据结构与算法Python版学习笔记】查找与排序——散列、散列函数、区块链

    散列 Hasing 前言 如果数据项之间是按照大小排好序的话,就可以利用二分查找来降低算法复杂度. 现在我们进一步来构造一个新的数据结构, 能使得查找算法的复杂度降到O(1), 这种概念称为" ...

随机推荐

  1. Mybatis源码解析4——SqlSession

    上一篇文章中,我们介绍了 SqlSessionFactory 的创建过程,忘记了的,可以回顾一下,或者看下下面这张图也行. 接下来,可乐讲给大家介绍 Mybatis 中另一个重量级嘉宾--SqlSes ...

  2. Python习题集(五)

    每天一习题,提升Python不是问题!!有更简洁的写法请评论告知我! https://www.cnblogs.com/poloyy/category/1676599.html 题目 打印99乘法表 解 ...

  3. Java - 注释、标识符、关键字

    背景 要开始磕 Java 了,虽然以前学过用过,但是差不多忘光光了... 现在直接搬狂神的视频素材,不再自己总结,要学的东西太多了... 注释 单行注释 // 多行注释 /* */ 文档注释 /** ...

  4. shell脚本测试变量是否为空,测试文件是否存在,sed修改配置文件参数,分支语句

    Shell脚本 1. 基本的几个变量 使用$?获取最近一次的执行结果: 使用$#获取传递的参数个数,类似C语言中的int argc; 使用$@获取所有的传参,类似C语言的char **argv 2. ...

  5. 转:C#读取PDF、TXT内容

    //读取PDF内容 private void button2_Click(object sender, EventArgs e) { label3.Text = OnCreated("D:\ ...

  6. centos7修改服务器时区

    查看时区设置 timedatectl 列出所有时区,通过键盘上下键进行浏览 timedatectl list-timezones 修改服务器时区为Africa/Lagos # 拉各斯的时区,UTC+1 ...

  7. JEECG代码审计之文件上传

    JEECG代码审计之文件上传 0x01 简述 JEECG(J2EE Code Generation)是一款基于代码生成器JEE的智能开发平台.引领新的开发模式(Online Coding->代码 ...

  8. grep、cut、awk、sed的使用

    grep.cut.awk.sed 常常应用在查找日志.数据.输出结果等等,并对我们想要的数据进行提取.通常grep,sed命令是对行进行提取,cut跟awk是对列进行提取 处理海量数据之grep命令 ...

  9. PyTorch学习笔记6--案例2:PyTorch神经网络(MNIST CNN)

    上一节中,我们使用autograd的包来定义模型并求导.本节中,我们将使用torch.nn包来构建神经网络. 一个nn.Module包含各个层和一个forward(input)方法,该方法返回outp ...

  10. PHP的OpenSSL加密扩展学习(二):非对称加密

    上篇文章,我们了解了关于对称和非对称加密的一些相关的理论知识,也学习了使用 OpenSSL 来进行对称加密的操作.今天,我们就更进一步,学习 OpenSSL 中的非对称加密是如何实现的. 生成私钥 通 ...