二叉樹的應用:求解四則運算


一 二叉樹如何表示四則運算

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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM