python数据结构学习笔记-2016-11-28-01-表达式树

        13.3 表达式树

        算术表达式可以用二叉树的形式表示,称为表达式树(expression tree)。其操作符位于内结点,而操作数位于叶结点。表达式树可以用于对表达式本身求值,也可以将中序表达式转变成前序表达式或者后序表达式。

python数据结构学习笔记-2016-11-28-01-表达式树_第1张图片

        13.3.1 表达式树ADT

        算术操作符可分为一元操作符(-a, a!)和二元操作符(a + b)。这里,先只考虑二元操作符。将操作符储存在根结点,而操作符左边部分则储存在左子树中,右边部分则储存在右子树中。

        其所具有方法包括:

  • ExpressionTree(expStr):将输入的表达式字符串转变成表达式树,假定输入的表达式字符串是合法的,并带有全括号的表达式;
  • evaluate(varDict):对表达式树进行求值,并返回数值结果,若表达式树中含有变量,则从输入的字典中,对变量进行取值,除以零或使用未定义变量将会引发异常;
  • toString():将表达式树转变成表达式字符串。

        将表达式树转变成表达式字符串

        对表达式进行后序遍历,即可发现后序遍历得到的正好就是表达式的后缀表示法。相应的,前序遍历,得到的正好就是表达式的前缀表示法。

        对于中序遍历,它能得到表达式对于其内操作符以及操作数的正确运算顺序,只是我们需要将其用括号括起来。

        python数据结构学习笔记-2016-11-28-01-表达式树_第2张图片

        利用表达式树对表达式求值

        每一个子树代表了表达式中的某一个子表达式,且其所处的层数越接近叶结点,则其运算优先级越高。所以对于一个根结点,可以先求出左子树的值和右子树的值,再用这两个值与根结点处的运算符进行运算。

python数据结构学习笔记-2016-11-28-01-表达式树_第3张图片

          表达式树的构建

          为了简单起见,假定传入的表达式满足以下条件:

  • 表达式内无空格存在;
  • 表达式合法且,全括号;
  • 每一个操作数均是一位数或者一个字母;
  • 运算符为 + - * / %。
           以‘(8+5)’为例。

           当遍历至'(‘时,创建一个新的结点,并将其作为根结点的左子结点,同时将外部指针current移动至新创建的左子结点处。

python数据结构学习笔记-2016-11-28-01-表达式树_第4张图片

          当遍历至数字时,将外部指针current所指向的结点处的值修改为相应的数字,然后将外部指针current返回至根结点处。

python数据结构学习笔记-2016-11-28-01-表达式树_第5张图片

         当遍历至操作符时,创建一个新结点,并将其作为根结点的右子结点,同时将外部指针current移动至新创建的右子结点处。

python数据结构学习笔记-2016-11-28-01-表达式树_第6张图片

         再次遍历至操作数时,将外部指针current所指向的结点,其值修改为相应值,同时将外部指针current返回至该结点的根结点。

python数据结构学习笔记-2016-11-28-01-表达式树_第7张图片

         当遍历至')'时,将外部指针current指向None即可。

python数据结构学习笔记-2016-11-28-01-表达式树_第8张图片

         当有多重运算时,例如:((2*7)+8)

python数据结构学习笔记-2016-11-28-01-表达式树_第9张图片


#-*-coding: utf-8-*-

# 表达式树ADT

from llistqueue import Queue

class ExpressionTree(object):
    def __init__(self, expStr):
        self._expTree = None
        self._bulidTree(expStr)

    def evaluate(self, varMap):
        return self._evalTree(self._expTree, varMap)

    def __str__(self):
        return self._buildString(self._expTree)

    # 对于中序遍历,它能得到表达式对于其内操作符以及操作数的正确运算顺序,只是我们需要将其用括号括起来。
    def _buildString(self, treeNode):
        if treeNode.left is None and treeNode.right is None:
            return str(treeNode.element)
        else:
            expStr = '('
            expStr += self._buildString(treeNode.left)
            expStr += str(treeNode.element)
            expStr += self._buildString(treeNode.right)
            expStr += ')'
            return expStr
    
    # 这里使用的是后序遍历,只是访问操作变成了求值而已
    def _evalTree(self, subtree, varDict):
        if subtree.left is None and subtree.right is None: # 判断是否是叶结点
            if '0' <= subtree.element <= '9': # 叶结点处肯定是操作数
                return int(subtree.element)
            else:
                assert subtree.element in varDict, "Invalid variable."
                return varDict[subtree.element]
        else:
            lvalue = _evalTree(subtree.left) # 左子树的操作数
            rvalue = _evalTree(subtree.right) # 右子树的操作数
            return self._computeOp(lvalue, subtree.element, rvalue) # 将两个操作数与根结点处的操作符进行运算
     
    # 辅助方法
    def _computeOp(self, left, op, right):
        if op == '+':
            return left + right
        elif op == '-':
            return left - right
        elif op == '*':
            return left * right
        elif op == '/':
            assert right != 0, "divisor cannot be zero."
            return 1.0 * left / right
        elif op == '%':
            assert right != 0, "divisor cannot be zero."
            return left % right

    def _bulidTree(self, expStr):
        # 创建相应的队列
        expQ = Queue()
        for token in expStr:
            expQ.enqueue(token)
        # 创建一个空结点,并作为根结点
        self._expTree = _ExpTreeNode(None)
        # 调用递归函数创建表达式树
        self._recBuildTree(self._expTree, expQ)

    # 用于创建表达式树的递归函数
    def _recBuildTree(self, curNode, expQ):
        # 先从表达式中提取一个字符
        token = expQ.dequeue()
        # 查看相应的字符是否是'(',因为'
        if token == '(': 
            curNode.left = _ExpTreeNode(None) # 创建空结点作为根结点的左子结点
            self._recBuildTree(curNode.left, expQ) # 创建左子树
            # 此时下一个字符肯定是运算符
            curNode.data = expQ.dequeue()
            curNode.right = _ExpTreeNode(None) # 创建空结点作为根结点的右子结点
            self._recBuildTree(curNode.right, expQ) # 创建右子树
            # 此时下一个结点肯定是')',仅仅将它出队即可
            expQ.dequeue()
        # 如果碰到一个数字或者字母,则修改相应结点值
        else:
            curNode.element = token

# 表达式树结点的储存类
class _ExpTreeNode(object):
    def __init__(self, data):
        self.element = data
        self.left = None
        self.right = None


你可能感兴趣的:(python数据结构)