一 二叉樹如何表示四則運算
1.1 表達式轉換為二叉樹
上圖是表達式“3+2*9-16/4”轉換成的二叉樹,觀察表達式,可以看出:
(1)操作數都是葉子節點;
(2)運算符都是內部節點;
(3)優先運算的操作符都在樹下方,而相對優先級較低的減法(根節點)運算則最后運算。
從上往下看,這棵二叉樹可以理解如下:
(1)要理解根節點"-"號的結果必須先計算出左子樹"+"和右子樹"/"號的結果。可以看,要想得到"+"號的結果,又必須先計算其右子樹"*"號的結果;
(2)"*"號左右孩子是數字,可以直接計算,2*9=18。接下來計算"+"號,3+18=21,即根節點的左子樹結果為21;
(3)"/"號左右孩子是數字,可以直接計算,16/4=4。於是,根節點的右子樹結果為4。
(4)最后計算根節點的"-"號,21-4=17,於是得出了該表達式的值為17。
1.2 二叉表達式樹的構造過程解析
從上面的解析過程可以看出,這是一個遞歸的過程,正好可以用二叉樹先序遍歷的方法進行計算。下面我們來一步一步地通過圖示來演示一下表達式"3+2*9-16/4"解析生成二叉樹的過程。
(1)首先獲取表達式的第一個字符“3”,由於表達式樹目前還是一棵空樹,所以3成為根節點;
(2)獲取第二個字符“+”,此時表達式樹根節點為數字,需要將新節點作為根節點,原根節點作為新根節點的左孩子。這里需要注意的是:只有第二個節點會出現這樣的可能,因為之后的根節點必定為操作符;
(3)獲取第三個字符“2”,數字將沿着根節點右鏈插入到最右端;
(4)獲取第四個字符“*”,如果判斷到是操作符,則將與根節點比較優先級,如果新節點的優先級高則插入成為根節點的右孩子,而原根節點的右孩子則成為新節點的左子樹;
(5)獲取第五個字符“9”,數字將沿着根節點右鏈插入到最右端;
(6)獲取第六個字符“-”,“-”與根節點“+”比較運算符的優先級,優先級相等則新節點成為根節點,原表達式樹則成為新節點的左子樹;
(7)獲取第7與第8個字符組合為數字16,沿着根節點右鏈插入到最右端;
(8)獲取第九個字符“/”,與根節點比較運算符的優先級,優先級高則成為根節點的右孩子,原根節點右子樹則成為新節點的左子樹;
(9)獲取第十個字符“4”,還是沿着根節點右鏈查到最右端。至此,運算表達式已全部遍歷,一棵表達式樹就已經建立完成。
二 C++代碼實現
#include "stdio.h" #include <iostream> using namespace std; template <typename T> struct BTreeNode { T key; BTreeNode<T> *left; BTreeNode<T> *right; BTreeNode<T> *parent; }; struct Operator { char *cOperatorVal; int nPriority; }; static Operator OPERATOR[] = {{"+", 1},{"-", 1},{"*", 2},{"/", 2}}; #define NUM_PRIORITY 10 // 自定義數字的優先級 template <typename T> class BTree { public: explicit BTree() : m_pTree(NULL),m_pCurrentNode(NULL) { } ~BTree() { DestroyTree(m_pTree); m_pTree = NULL; m_pCurrentNode = NULL; } bool IsOperator(T key) { for (int i = 0; i < sizeof(OPERATOR)/sizeof(OPERATOR[0]); i ++) { if (0 == strcmp(key, OPERATOR[i].cOperatorVal)) { return true; } } return false; } // 根據操作符得到其優先級 int GetPriority(T key) { for (int i = 0; i < sizeof(OPERATOR)/sizeof(OPERATOR[0]); i ++) { if (strcmp(key, OPERATOR[i].cOperatorVal) == 0) { return OPERATOR[i].nPriority; } } return NUM_PRIORITY; } BTreeNode<T>* AddNode(T key) { BTreeNode<T> *pNode = (BTreeNode<T> *)malloc(sizeof(BTreeNode<T>)); pNode->key = key; pNode->parent = NULL; pNode->left = NULL; pNode->right = NULL; if (m_pTree == NULL) { m_pTree = pNode; m_pCurrentNode = pNode; return m_pTree; } // 若是數字,添加到上個結點的左邊,若左邊已有,則加右邊 // 若是運算符,首先比較和頭結點雲算法的優先級 // (1)若為同優先級則當前運算符為新頭結點,之前的為其左子樹 // (2)若為高優先級則為當前非符號的父節點,之前的為其左子樹 if (IsOperator(key)) { if (GetPriority(key) <= GetPriority(m_pTree->key)) { pNode->left = m_pTree; m_pTree->parent = pNode; m_pTree = pNode; } else { // 若操作符優先級很高 pNode->parent = m_pCurrentNode->parent; if (m_pCurrentNode == m_pCurrentNode->parent->left) { m_pCurrentNode->parent->left = pNode; } else { m_pCurrentNode->parent->right = pNode; } pNode->left = m_pCurrentNode; m_pCurrentNode->parent = pNode; } } else { if (m_pCurrentNode->left == NULL) { m_pCurrentNode->left = pNode; } else { m_pCurrentNode->right = pNode; } pNode->parent = m_pCurrentNode; } m_pCurrentNode = pNode; return m_pTree; } void PreOrder(BTreeNode<T> * pNode) { if (pNode != NULL) { cout << pNode->key << " "; PreOrder(pNode->left); PreOrder(pNode->right); } } void MidOrder(BTreeNode<T> * pNode) { if (pNode != NULL) { MidOrder(pNode->left); cout << pNode->key << " "; MidOrder(pNode->right); } } void DestroyTree(BTreeNode<T> * pNode) { if (pNode != NULL) { DestroyTree(pNode->left); DestroyTree(pNode->right); free(pNode); } } // 計算 int PreOrderCalc(BTreeNode<T> * pNode) { int num1, num2, result; if (IsOperator(pNode->key)) { // 遞歸先序遍歷計算num1 num1 = PreOrderCalc(pNode->left); // 遞歸先序遍歷計算num2 num2 = PreOrderCalc(pNode->right); char optr = *(char *)pNode->key; switch (optr) { case '+': result = num1 + num2; break; case '-': result = num1 - num2; break; case '*': result = num1 * num2; break; case '/': if (num2 == 0) { cout << "除數不能為0!" << endl; } result = num1 / num2; break; } return result; } result = atoi(pNode->key); return result; } private: BTreeNode<T> *m_pTree; BTreeNode<T> *m_pCurrentNode; }; void main() { BTree<char *> tree; tree.AddNode("3"); tree.AddNode("+"); tree.AddNode("2"); tree.AddNode("*"); tree.AddNode("9"); tree.AddNode("-"); tree.AddNode("16"); tree.AddNode("/"); BTreeNode<char *> *pNode = tree.AddNode("4"); cout << "前序遍歷:"; tree.PreOrder(pNode); cout << endl; cout << "中序遍歷為表達式:"; tree.MidOrder(pNode); cout << endl; int a = tree.PreOrderCalc(pNode); cout << "計算結果為:" << a <<endl; return; }