python數據結構學習筆記-2016-11-28-01-表達式樹

        13.3 表達式樹

        算術表達式可以用二叉樹的形式表示,稱爲表達式樹(expression tree)。其操作符位於內結點,而操作數位於葉結點。表達式樹可以用於對表達式本身求值,也可以將中序表達式轉變成前序表達式或者後序表達式。


        13.3.1 表達式樹ADT

        算術操作符可分爲一元操作符(-a, a!)和二元操作符(a + b)。這裏,先只考慮二元操作符。將操作符儲存在根結點,而操作符左邊部分則儲存在左子樹中,右邊部分則儲存在右子樹中。

        其所具有方法包括:

  • ExpressionTree(expStr):將輸入的表達式字符串轉變成表達式樹,假定輸入的表達式字符串是合法的,並帶有全括號的表達式;
  • evaluate(varDict):對表達式樹進行求值,並返回數值結果,若表達式樹中含有變量,則從輸入的字典中,對變量進行取值,除以零或使用未定義變量將會引發異常;
  • toString():將表達式樹轉變成表達式字符串。

        將表達式樹轉變成表達式字符串

        對表達式進行後序遍歷,即可發現後序遍歷得到的正好就是表達式的後綴表示法。相應的,前序遍歷,得到的正好就是表達式的前綴表示法。

        對於中序遍歷,它能得到表達式對於其內操作符以及操作數的正確運算順序,只是我們需要將其用括號括起來。

        

        利用表達式樹對表達式求值

        每一個子樹代表了表達式中的某一個子表達式,且其所處的層數越接近葉結點,則其運算優先級越高。所以對於一個根結點,可以先求出左子樹的值和右子樹的值,再用這兩個值與根結點處的運算符進行運算。

          表達式樹的構建

          爲了簡單起見,假定傳入的表達式滿足以下條件:

  • 表達式內無空格存在;
  • 表達式合法且,全括號;
  • 每一個操作數均是一位數或者一個字母;
  • 運算符爲 + - * / %。
           以‘(8+5)’爲例。

           當遍歷至'(‘時,創建一個新的結點,並將其作爲根結點的左子結點,同時將外部指針current移動至新創建的左子結點處。


          當遍歷至數字時,將外部指針current所指向的結點處的值修改爲相應的數字,然後將外部指針current返回至根結點處。


         當遍歷至操作符時,創建一個新結點,並將其作爲根結點的右子結點,同時將外部指針current移動至新創建的右子結點處。


         再次遍歷至操作數時,將外部指針current所指向的結點,其值修改爲相應值,同時將外部指針current返回至該結點的根結點。


         當遍歷至')'時,將外部指針current指向None即可。


         當有多重運算時,例如:((2*7)+8)



#-*-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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章