表達式·表達式樹·表達式求值

描述

衆所周知,任何一個表達式,均可以用一棵表達式樹來表示。例如,表達式a+b*c,能夠表示爲以下的表達式樹:
   +
  /  \
 a    *
     /  \
     b  c
如今,給你一箇中綴表達式,這個中綴表達式用變量來表示(不含數字),請你將這個中綴表達式用表達式二叉樹的形式輸出出來。
ios

輸入

輸入分爲三個部分。
第一部分爲一行,即中綴表達式(長度不大於50)。中綴表達式可能含有小寫字母表明變量(a-z),也可能含有運算符(+、-、*、/、小括號),不含有數字,也不含有空格。
第二部分爲一個整數n(n < 10),表示中綴表達式的變量數。
第三部分有n行,每行格式爲C x,C爲變量的字符,x爲該變量的值。express

輸出

輸出分爲三個部分,第一個部分爲該表達式的逆波蘭式,即該表達式樹的後根遍歷結果。佔一行。
第二部分爲表達式樹的顯示,如樣例輸出所示。若是該二叉樹是一棵滿二叉樹,則最底部的葉子結點,分別佔據橫座標的第一、三、五、7……個位置(最左邊的座標是1),而後它們的父結點的橫座標,在兩個子結點的中間。若是不是滿二叉樹,則沒有結點的地方,用空格填充(但請略去全部的行末空格)。每一行父結點與子結點中隔開一行,用斜槓(/)與反斜槓(\)來表示樹的關係。/出現的橫座標位置爲父結點的橫座標偏左一格,\出現的橫座標位置爲父結點的橫座標偏右一格。也就是說,若是樹高爲m,則輸出就有2m-1行。
第三部分爲一個整數,表示將值代入變量以後,該中綴表達式的值。須要注意的一點是,除法表明整除運算,即捨棄小數點後的部分。同時,測試數據保證不會出現除以0的現象。數組

樣例輸入

a+b*c
3
a 2
b 7
c 5函數

樣例輸出

abc*+
   +
  /  \
 a    *
     /  \
     b  c
37
測試


中綴表達式生成二叉樹

  • 考慮沒有括號的狀況
    對於一箇中綴表達式:\(a+b\),由運算符分爲左右兩個部分。其二叉樹表示形式天然爲:
graph TB root((+))---left((a)) root((+))---right((b))

因此,構建一個表達式樹,關鍵在於找到表達式的根結點,而後分左右兩個部分構建樹;拓展到多個同級運算符的表達式:\(a+b+c+...+n\),能夠這樣構建其表達式樹:lua

  1. 找到最後運算的結點,若以最右邊的一個+爲根結點
  2. 以根結點分爲左右兩個部分
  3. 構建根結點,左邊構建樹,右邊構建樹
  4. 若左邊部分又是一個表達式,執行一、二、3步驟
  5. 若右邊部分又是一個表達式,執行一、二、3步驟

這樣就能夠構建出整個表達式樹:spa

graph TB root((+))---left[a+b+...+n-1] root((+))---right((n)) left-.->|左邊部分又是一個表達式|root_l subgraph root_l((+))---left_l[a+b+...+n-2] root_l((+))---right_l((n-1)) end left_l-.->|n-1次重複後|root_n subgraph root_n((+))---left_n((a)) root_n((+))---right_n((b)) end
  • 考慮有不一樣優先級運算符的狀況:
    \(a+b*c\),由於+是最後運算,因此它必定是樹的根,*先運算,是+號分得的右邊部分的根,這樣獲得其表達式樹:
graph TB root((+))---left((a)) root((+))---right((*)) right((*))---left_((b)) right((*))---right_((c))

因此,含有優先級不一樣的表達式,關鍵在於找到表達式最後運算的運算符,做爲某個表達式樹的根,而後分左右兩個部分構建樹;一樣,同級運算符以最左邊的運算符爲最後運算的運算符,統一第一種狀況:拓展到多個不一樣運算符出現的狀況:\(a+b*c p_1...p_k n(p_i爲第i個運算符)\).net

  1. 找到最後運算的運算符做爲根結點
  2. 以根結點分爲左右兩個部分
  3. 構建根結點,左邊構建樹,右邊構建樹
  4. 若左邊部分又是一個表達式,執行一、二、3步驟
  5. 若右邊部分又是一個表達式,執行一、二、3步驟

這樣就能夠構建出整個表達式樹:指針

graph TB root((+))---left((a)) root((+))---right[b*c p_1...p_k n] right-.->|右邊邊部分又是一個表達式|root_r subgraph 若p_i是最後運算的運算符 root_r((p_i))---left_r[b*c p_1...p_i-1 n-k+i-1] root_r((p_i))---right_r[n-k+i p_i+1...p_k n] end
  • 考慮存在括號的狀況
    若是表達式中存在括號,最後運算的運算符必定在括號外,這樣,須要一個變量記錄括號位置,在括號外找到最後運算的運算符,而後分爲左右兩個部分,重複找括號,建樹操做就能夠了,以\(a*b+(c+d)\)爲例:
  1. 找到最後運算的+號分左右兩個部分:\(a*b\)\((c+d)\)
  2. 左邊部分建樹
  3. 右邊部分建樹:
    • 右邊部分括號外找不到最後運算的運算符,說明整個表達式被括號括起來
    • 去掉括號:c+d 建樹
  4. 若左邊部分又是一個表達式,執行一、二、3步驟
  5. 若右邊部分又是一個表達式,執行一、二、3步驟

因此,構建表達式樹的函數:CreateExpressionTree(char *expression, int start, int end, Bitree &tree)中,傳入了表達式的開始下標和結尾下標,方便分割左邊和右邊部分:
CreateExpressionTree(char *expression, int start, int end, Bitree &tree)函數:code

void CreateExpressionTree(char *expression, int start, int end, Bitree &tree)
{
    /** 
      * 這裏,c1用來記錄括號外最後運算的+或-
      * c2用來記錄括號外最後運算的+或-
      * p記錄括號當讀到一個( p++, ) 時 p--, 只有p==0時才說明c1, c2 
      * 括號外 
      */
    int i, c1 = -1, c2 = -1, p = 0; 
    if (end - start == 1) {
        //只有一個結點,直接創建結點
        tree = new BTNode;
        tree->data = *(expression+start);
        tree->left_child = tree->right_child = NULL;
        return;
    }
    for (i = start; i < end; i++) {
        switch (*(expression+i))
        {
        case '(': p++; break;
        case ')': p--; break;
        case '+': case '-': if (!p) c1 = i; break;
        case '*': case '/': if (!p) c2 = i; break;
        }
    }
    //c1 < 0,說明括號外沒有第一優先級的+或者-,那麼就只能考慮*或者/
    if (c1 < 0) c1 = c2;     
    // 說明括號外沒有第一優先級的*或者/,說明此時表達式被括號括起來, 去掉括號後建樹
    if (c1 < 0) CreateExpressionTree(expression,start+1,end-1,tree);
    else {
        //創建根
        CreateExpressionTree(expression,c1,c1 + 1,tree);  
        //創建左樹  
        CreateExpressionTree(expression,start,c1,tree->left_child);
        //創建右樹
        CreateExpressionTree(expression,c1+1,end,tree->right_child);
    }
}
對於建樹這一部分,參考了表達式樹與前中後綴表達式,詳細訪問連接

打印表達式樹

如題:

若是該二叉樹是一棵滿二叉樹,則最底部的葉子結點,分別佔據橫座標的第一、三、五、7……個位置(最左邊的座標是1),而後它們的父結點的橫座標,在兩個子結點的中間。若是不是滿二叉樹,則沒有結點的地方,用空格填充(但請略去全部的行末空格)。每一行父結點與子結點中隔開一行,用斜槓(/)與反斜槓(\)來表示樹的關係。/出現的橫座標位置爲父結點的橫座標偏左一格,\出現的橫座標位置爲父結點的橫座標偏右一格。也就是說,若是樹高爲m,則輸出就有2m-1行。


用一個graph[MAX_ROW][MAX_COL] 數組保存表達式樹的位置信息。以題例來看:
   +
  /  \
 a    *
     /  \
     b  c
首先,一共有\(2^h-1\)個結點,層層找結點位置,中間位置,即第\(2^{h-1}\)個結點+把該層分爲兩個部分,且左右部分皆是\(2^{h-2}\)長度(ps:由於包含了\/的位置),若是有左結點,左結點就在\(2^{h-2}\)的中間部分,(ps:這裏因爲用到了\(2^x\)次方,在宏定義了一個POW(NUM) 1 << NUM, 1 << NUM 即右移NUM,即\(2^{NUM}\))右結點也如此處理...這樣,處理下一層時,也如同上一層處理:先處理根結點,在處理左右結點。在處理完根結點後下一層應該留給/\,位置爲中間位置左偏一個或右偏一個;

void Print(Bitree root, int row, int col, int len)
{
   if (root == NULL) return;
    graph[row][col-1] = root->data;     //當前層中間位置
    if (root->left_child!=NULL) {
        graph[row+1][col-2] = '/';      //下一層留給/,中間位置偏左一個
        Print(root->left_child,row+2,col-len,len>>1); // 左邊部分
    }
    if (root->right_child!=NULL) {
        graph[row+1][col] = '\\';
        Print(root->right_child,row+2,col+len,len>>1);
    }
}
對於打印表達式樹這一部分,參考了表達式·表達式樹·表達式求值,詳細訪問連接

計算部分

對於這一部分,因爲已經有了表達式樹了,只須要一層層求解便可:

  1. 若是結點是運算符,將左樹和右樹的計算結果和根運算
  2. 若是結點不是運算符,直接返回變量值
    其中左樹和右樹的計算結果就是一個遞歸過程,反覆執行一、2步;
這一部分因爲要用到變量的值, 使用了 map<char,int> to_value的一個映射,給一個結點變量,對應一個變量值
對於計算部分,參考了表達式·表達式樹·表達式求值,詳細訪問連接

其它

其餘部分根據題意求解便可,關於表達式輸入,使用了一個char*指針,可是我在這個地方初始化 expression = new char [52];一開始記成 expression = new char (52);致使一直不能過...


完整代碼

/*
@File     :   expression_tree.cpp
@Time     :   2020/04/27
@Desc     :   表達式·表達式樹·表達式求值
*/
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#define POW(NUM) 1<<NUM
#define MAX_ROW 70
#define MAX_COL 300

using namespace std;

typedef struct BTNode
{
    char data;
    struct BTNode *left_child;
    struct BTNode *right_child;
}*Bitree;
char graph[MAX_ROW][MAX_COL];
map <char,int> to_value;
void CreateExpressionTree(char *expression, int start, int end, Bitree &tree)
{
    int i, c1 = -1, c2 = -1, p = 0;
    if (end - start == 1) {
        tree = new BTNode;
        tree->data = *(expression+start);
        tree->left_child = tree->right_child = NULL;
        return;
    }
    for (i = start; i < end; i++) {
        switch (*(expression+i))
        {
        case '(': p++; break;
        case ')': p--; break;
        case '+': case '-': if (!p) c1 = i; break;
        case '*': case '/': if (!p) c2 = i; break;
        }
    }
    if (c1 < 0) c1 = c2;
    if (c1 < 0) CreateExpressionTree(expression,start+1,end-1,tree);
    else {
        CreateExpressionTree(expression,c1,c1 + 1,tree);
        CreateExpressionTree(expression,start,c1,tree->left_child);
        CreateExpressionTree(expression,c1+1,end,tree->right_child);
    }
}
void PostOrder(Bitree tree)
{
    if (tree == NULL) return;
    PostOrder(tree->left_child);
    PostOrder(tree->right_child);
    cout << tree->data;
}
int GetHeight(Bitree tree)
{
    int left_height, right_height;
    if (tree == NULL) return 0;
    else {
        left_height = GetHeight(tree->left_child);
        right_height = GetHeight(tree->right_child);
        return (left_height > right_height)?(left_height+1):(right_height+1);
    }  
}
void Print(Bitree root, int row, int col, int len)
{
   if (root == NULL) return;
    graph[row][col-1] = root->data;
    if (root->left_child!=NULL) {
        graph[row+1][col-2] = '/';
        Print(root->left_child,row+2,col-len,len>>1);
    }
    if (root->right_child!=NULL) {
        graph[row+1][col] = '\\';
        Print(root->right_child,row+2,col+len,len>>1);
    }
}
int Calculate(Bitree tree)
{
    switch (tree->data) 
    {
    case '+':
        return Calculate(tree->left_child) + Calculate (tree->right_child);
        break;
    case '-':
        return Calculate(tree->left_child) - Calculate (tree->right_child);
        break;
    case '*':
        return Calculate(tree->left_child) * Calculate (tree->right_child);
        break;
    case '/':
        return Calculate(tree->left_child) / Calculate (tree->right_child);
        break;
    default:
        return to_value[tree->data];
        break;
    }
}
int main(int argc, char const *argv[])
{
    int n, value;
    char *expression, valuable;
    Bitree tree;
    // expression = (char*)malloc(sizeof(char)*52);
    // expression = new char(52); !!!!!
    expression = new char[52];
    cin >> expression;
    cin >> n;
    while (n--) {
        cin >> valuable >> value;
        to_value[valuable] = value;
    }
    CreateExpressionTree(expression,0,strlen(expression),tree); 
    PostOrder(tree); cout << endl;
    memset(graph,' ',sizeof(graph));
    Print(tree,0,POW(GetHeight(tree)-1),POW(GetHeight(tree)-2)); 
    int j = 0, l = 0;
    for (int i = 0; i < MAX_ROW; ++i) {
        j = MAX_COL - 1;
        while (j >= 0 && graph[i][j] == ' ') --j;
        if (j > -1) {
            ++l;
            graph[i][j+1] = '\0';
        }
        else break;
    }
    for (int i = 0; i < l; ++i) cout << graph[i] << endl;
    cout << Calculate(tree) << endl;
    system("pause");
    return 0;
}
相關文章
相關標籤/搜索