二叉树的应用:求解四则运算

Posted Fate0729

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树的应用:求解四则运算相关的知识,希望对你有一定的参考价值。

一 二叉树如何表示四则运算

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

以上是关于二叉树的应用:求解四则运算的主要内容,如果未能解决你的问题,请参考以下文章

(二叉树)原来Java 求解二叉树的最大深度如此简单 !!!

Java 求解二叉树的深度

Java 求解二叉树的最近公共祖先

求解具有n个结点的完全二叉树的深度,写出计算过程

求解具有n个结点的完全二叉树的深度,写出计算过程

求解完全二叉树的节点总数 Python实现