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


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

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

  • 相关阅读:
    Github使用手册2——Github使用自己的远程仓库
    GitHub使用手册1——fork and pull request
    如何申请Pycharm学生免费激活码
    博客园入园手册2——Markdown编辑器1
    博客园入园手册1——TinyMEC编辑器
    实验一 GIT 代码版本管理
    结对项目-数独程序扩展(要求细化更新)
    个人作业Week2-代码复审(修改明确了要求)
    个人作业-Week1(新增详细说明)
    个人项目-数独
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/8894253.html
Copyright © 2020-2023  润新知