解析树(语法树)

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

表达式解析

\(((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出栈的节点即可!

代码

  1. class Stack:
  2. def __init__(self):
  3. self.items = []
  4. def isEmpty(self):
  5. return self.items == []
  6. def push(self, item): # 将item加入栈顶,无返回值
  7. return self.items.append(item)
  8. def pop(self): # 将栈顶数据项移除,并返回,栈被修改
  9. return self.items.pop()
  10. def peek(self): # "窥视"栈顶数据项,返回栈顶的数但不移除,栈不被修改
  11. return self.items[len(self.items)-1]
  12. def size(self):
  13. return len(self.items)
  14. class BinaryTree:
  15. def __init__(self, rootObj):
  16. self.key = rootObj
  17. self.leftChild = None
  18. self.rightChild = None
  19. def insertLeft(self, newNode):
  20. if self.leftChild == None:
  21. self.leftChild = BinaryTree(newNode)
  22. else:
  23. t = BinaryTree(newNode)
  24. t.leftChild = self.leftChild
  25. self.leftChild = t
  26. def insertRignt(self, newNode):
  27. if self.rightChild == None:
  28. self.rightChild = BinaryTree(newNode)
  29. else:
  30. t = BinaryTree(newNode)
  31. t.rightChild = self.rightChild
  32. self.rightChild = t
  33. def getRightChild(self):
  34. return self.rightChild
  35. def getLeftChild(self):
  36. return self.leftChild
  37. def setRootVal(self, obj):
  38. self.key = obj
  39. def getRootVal(self):
  40. return self.key
  41. def buildParseTree(fpexp):
  42. fplist = fpexp.split()
  43. pstack = Stack()
  44. eTree = BinaryTree('')
  45. # 入栈下降
  46. pstack.push(eTree)
  47. currentTree = eTree
  48. for i in fplist:
  49. # 表达式开始
  50. if i == '(':
  51. currentTree.insertLeft('')
  52. pstack.push(currentTree) # 入栈下降
  53. currentTree = currentTree.getLeftChild
  54. elif i not in ['+', '-', '*', '/', ')']:
  55. currentTree.setRootVal(int(i))
  56. parent = pstack.pop() # 出栈上升
  57. currentTree = parent
  58. elif i in ['+', '-', '*', '/']:
  59. currentTree.setRootVal(i)
  60. currentTree.insertRignt('')
  61. pstack.push(currentTree)
  62. currentTree = currentTree.getRightChild()
  63. elif i == ')':
  64. currentTree = pstack.pop() # 出栈上升
  65. else:
  66. raise ValueError
  67. return eTree

表达式解析树求值

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

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

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

    1. import operator
    2. op= operator.add

代码

  1. def evaluate(parseTree):
  2. opers = {
  3. '+': operator.add,
  4. '-': operator.sub,
  5. '*': operator.mul,
  6. '/': operator.truediv
  7. }
  8. # 缩小规模
  9. leftC = parseTree.getLeftChild()
  10. rightC = parseTree.getRightChild()
  11. if leftC and rightC:
  12. fn = opers[parseTree.getRootVal()]
  13. # 递归调用
  14. return fn(evaluate(leftC), evaluate(rightC))
  15. else:
  16. # 基本结束条件
  17. 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. springcloud3(五) spring cloud gateway动态路由的四类实现方式

    写这篇博客主要是为了汇总下动态路由的多种实现方式,没有好坏之分,任何的方案都是依赖业务场景需求的,现在网上实现方式主要有: 基于Nacos, 基于数据库(PosgreSQL/Redis), 基于Mem ...

  2. ajax获取图片

    <img id="contents2_img" alt="" src="images/hope.png" style="wi ...

  3. Filter案例之敏感词过滤和代理模式

    一.需求分析 二 .代理模式 1.概念 2.代码实现 代理对象可以强转为真实对象,即对应的接口类: 3.通过代理增强方法 其中,方法对象invoke真实对象,反射原理: 三.过滤敏感词汇案例代码实现 ...

  4. Spring Boot 入门系列(二十八) JPA 的实体映射关系,一对一,一对多,多对多关系映射!

    前面讲了Spring Boot 使用 JPA,实现JPA 的增.删.改.查的功能,同时也介绍了JPA的一些查询,自定义SQL查询等使用.JPA使用非常简单,功能非常强大的ORM框架,无需任何数据访问层 ...

  5. iOS之多语言开发

    前要:iOS多语言开发,可以分为两种 系统设置,通过在手机设置中切换语言,进而改变app中语言: app中手动切换,用户在app中,手动选择语言,进行切换. 一.添加需要的语言 不管使用哪种方法,都需 ...

  6. [PhpStorm]解决Cannot find declaration to go to

    1.问题重现 使用单例模式访问类方法,PhpStorm提示类方法Cannot find declaration to go to 2.解决方法 加一句代码注释 注意:注释不能省略变量名 注:成员变量实 ...

  7. Linux系列(32) - rpm命令管理之RPM查询(4)

    RPM包默认安装位置 RPM包默认安装路径 /etc/ 配置文件安装目录 /usr/bin/ 可执行的命令安装目录 /usr/lib/ 程序所使用的函数库保存位置 /usr/share/doc/ 基本 ...

  8. python风格对象

    对象表示形式 python提供了两种获取对象字符串表示形式的标准方式 repr()         //便于开发者理解的方式返回对象的字符串表示形式(一般来说满足obj==eval(repr(obj) ...

  9. re.findall用法

    其中,re.findall() 函数可以遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表. 在python源代码中,展示如下: 搜索string,返回一个顺序访问每一个匹配结果(Match对象 ...

  10. P4258-[WC2016]挑战NPC【带花树】

    正题 题目链接:https://www.luogu.com.cn/problem/P4258 题目大意 给出\(n\)个球,\(m\)个篮筐,每个球都可以被放入一些特定的篮筐,每个球都要放,要求球的个 ...