热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

一二叉树如何表示四则运算1.1表达式转换为二叉树上图是表达式“3+2*9-164”转换成的二叉树,观察表达式,可以看出:(1)操作数都是叶子节点;(2)运算符都是
一 二叉树如何表示四则运算

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 
using namespace std;

template 
struct BTreeNode
{
    T key;
    BTreeNode *left;
    BTreeNode *right;
    BTreeNode *parent;
};

struct Operator
{
    char *cOperatorVal;
    int nPriority;
};

static Operator OPERATOR[] = {{"+", 1},{"-", 1},{"*", 2},{"/", 2}};
#define NUM_PRIORITY 10    // 自定义数字的优先级

template 
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* AddNode(T key)
    {
        BTreeNode *pNode = (BTreeNode *)malloc(sizeof(BTreeNode));
        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 * pNode)
    {
        if (pNode != NULL)
        {
            cout <key <<" ";
            PreOrder(pNode->left);
            PreOrder(pNode->right);
        }
    }

    void MidOrder(BTreeNode * pNode)
    {
        if (pNode != NULL)
        {
            MidOrder(pNode->left);
            cout <key <<" ";
            MidOrder(pNode->right);
        }
    }

    void DestroyTree(BTreeNode * pNode)
    {
        if (pNode != NULL)
        {
            DestroyTree(pNode->left);
            DestroyTree(pNode->right);
            free(pNode);
        }
    }
    
    // 计算
    int PreOrderCalc(BTreeNode * 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 *m_pTree;
    BTreeNode *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 <<"计算结果为:" <endl;

    return;
}


推荐阅读
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本实验主要探讨了二叉排序树(BST)的基本操作,包括创建、查找和删除节点。通过具体实例和代码实现,详细介绍了如何使用递归和非递归方法进行关键字查找,并展示了删除特定节点后的树结构变化。 ... [详细]
  • 本教程涵盖OpenGL基础操作及直线光栅化技术,包括点的绘制、简单图形绘制、直线绘制以及DDA和中点画线算法。通过逐步实践,帮助读者掌握OpenGL的基本使用方法。 ... [详细]
  • 本题涉及一棵由N个节点组成的树(共有N-1条边),初始时所有节点均为白色。题目要求处理两种操作:一是改变某个节点的颜色(从白变黑或从黑变白);二是查询从根节点到指定节点路径上的第一个黑色节点,若无则输出-1。 ... [详细]
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • 在多线程编程环境中,线程之间共享全局变量可能导致数据竞争和不一致性。为了解决这一问题,Linux提供了线程局部存储(TLS),使每个线程可以拥有独立的变量副本,确保线程间的数据隔离与安全。 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
  • Splay Tree 区间操作优化
    本文详细介绍了使用Splay Tree进行区间操作的实现方法,包括插入、删除、修改、翻转和求和等操作。通过这些操作,可以高效地处理动态序列问题,并且代码实现具有一定的挑战性,有助于编程能力的提升。 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 数据结构入门:栈的基本概念与操作
    本文详细介绍了栈这一重要的数据结构,包括其基本概念、顺序存储结构、栈的基本操作(如入栈、出栈、清空栈和销毁栈),以及如何利用栈实现二进制到十进制的转换。通过具体代码示例,帮助读者更好地理解和应用栈的相关知识。 ... [详细]
  • Redis Hash 数据结构详解
    本文详细介绍了 Redis 中的 Hash 数据类型及其常用命令。Hash 类型用于存储键值对集合,支持多种操作如插入、查询、更新和删除字段值。此外,文章还探讨了 Hash 类型在实际业务场景中的应用,并提供了优化建议。 ... [详细]
  • 本文深入探讨了POJ2762问题,旨在通过强连通分量缩点和单向连通性的判断方法,解决有向图中任意两点之间的可达性问题。文章详细介绍了算法原理、实现步骤,并附带完整的代码示例。 ... [详细]
  • 本文将详细探讨Linux pinctrl子系统的各个关键数据结构,帮助读者深入了解其内部机制。通过分析这些数据结构及其相互关系,我们将进一步理解pinctrl子系统的工作原理和设计思路。 ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 深入解析Redis内存对象模型
    本文详细介绍了Redis内存对象模型的关键知识点,包括内存统计、内存分配、数据存储细节及优化策略。通过实际案例和专业分析,帮助读者全面理解Redis内存管理机制。 ... [详细]
author-avatar
杭ai君浩
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有