热门标签 | 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;
}


推荐阅读
  • Gradle 是 Android Studio 中默认的构建工具,了解其基本配置对于开发效率的提升至关重要。本文将详细介绍如何在 Gradle 中定义和使用共享变量,以确保项目的一致性和可维护性。 ... [详细]
  • 本文探讨了如何使用Scrapy框架构建高效的数据采集系统,以及如何通过异步处理技术提升数据存储的效率。同时,文章还介绍了针对不同网站采用的不同采集策略。 ... [详细]
  • 本文探讨了Linux环境下线程私有数据(Thread-Specific Data, TSD)的概念及其重要性,介绍了如何通过TSD技术避免多线程间全局变量冲突的问题,并提供了具体的实现方法和示例代码。 ... [详细]
  • 尽管在WPF中工作了一段时间,但在菜单控件的样式设置上遇到了一些基础问题,特别是关于如何正确配置前景色和背景色。 ... [详细]
  • Hadoop MapReduce 实战案例:手机流量使用统计分析
    本文通过一个具体的Hadoop MapReduce案例,详细介绍了如何利用MapReduce框架来统计和分析手机用户的流量使用情况,包括上行和下行流量的计算以及总流量的汇总。 ... [详细]
  • 本文介绍了使用Python和C语言编写程序来计算一个给定数值的平方根的方法。通过迭代算法,我们能够精确地得到所需的结果。 ... [详细]
  • 本报告记录了嵌入式软件设计课程中的第二次实验,主要探讨了使用KEIL V5开发环境和ST固件库进行GPIO控制及按键响应编程的方法。通过实际操作,加深了对嵌入式系统硬件接口编程的理解。 ... [详细]
  • 本文详细介绍如何在SSM(Spring + Spring MVC + MyBatis)框架中实现分页功能。包括分页的基本概念、数据准备、前端分页栏的设计与实现、后端分页逻辑的编写以及最终的测试步骤。 ... [详细]
  • 【MySQL】frm文件解析
    官网说明:http:dev.mysql.comdocinternalsenfrm-file-format.htmlfrm是MySQL表结构定义文件,通常frm文件是不会损坏的,但是如果 ... [详细]
  • 编程解析:CF989C 花朵之雾 (构造算法)
    本文深入探讨了CF989C '花朵之雾'问题的构造算法,提供了详细的解题思路和代码实现。 ... [详细]
  • 本文探讨了互联网服务提供商(ISP)如何可能篡改或插入用户请求的数据流,并提供了有效的技术手段来防止此类劫持行为,确保网络环境的安全与纯净。 ... [详细]
  • 深入解析 C++ 中的 String 和 Vector
    本文详细介绍了 C++ 编程语言中 String 和 Vector 的使用方法及特性,旨在帮助开发者更好地理解和应用这两个重要的容器。 ... [详细]
  • 本文详细介绍了在Luat OS中如何实现C与Lua的混合编程,包括在C环境中运行Lua脚本、封装可被Lua调用的C语言库,以及C与Lua之间的数据交互方法。 ... [详细]
  • 本文详细介绍了 Redis 中的主要数据类型,包括 String、Hash、List、Set、ZSet、Geo 和 HyperLogLog,并提供了每种类型的基本操作命令和应用场景。 ... [详细]
  • 想把一组chara[4096]的数组拷贝到shortb[6][256]中,尝试过用循环移位的方式,还用中间变量shortc[2048]的方式。得出的结论:1.移位方式效率最低2. ... [详细]
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社区 版权所有