#01、引言,我们知道算式计算的问题是栈里面一个非常经典的题目。但是用栈来实现是一个非常麻烦的过程,第一要解决算式判断,是否为符合规则的算式,第二要由中最表达式转化为后缀表达式。这两个部分是栈实现计算算式表达式的比较复杂的地方。不仅如此,栈实现里面的各种运算符的优先级,各种条件判断,可以说是麻烦的要命。但是,实际上有一种数据结构比栈更适合解决这类问题。可以说是得天独厚的优势。对,就是二叉树。例如一个表达式:1+2*3-4/5

我们构造这样一个二叉树

当构造这样一个二叉树之后,解决表达式的值的方法,也就浮出水面了,把2和3相乘,存到*的节点中,然后再和1相加,存到+的节点中.....最后根节点-节点中存放的就是最后的计算结果。就是叶子节点执行其双亲节点的运算,结果存到双亲节点中。
#02、选二叉树作为算法的存储结构有什么好处。
       这主要有两个方面的好处,这也是针对于栈算法的两个麻烦的地方。
<1>//免除了算式表达式的检查过程。为什么能免除检查,表达式的规范性呢?并不是不需要检查,而是检查的过程就包含在创建二叉树的过程。认真分析这棵二叉树,我们会发现,所有的叶子节点必须是操作数节点,而所有的非叶子节点必须是运算符节点,否则表达式的结构一点不正确,创建二叉树的过程就可以对表达式经行检查。表达式是否正确也只取决于两个方面,第一、表达式的结构是否正确,比如不能出现2*+6这样的表达式,第二、表达式的数据是否正确,例如不能出现1+2.2.3这样的表达式,2.2.3不是一个符合规则的数据。而数据的检查,也可以在给叶子节点赋值的时候检查。所以避免的单独经行表达式检查的繁琐。
<2>//不需要转化为后缀表达式再经行表达式结果的计算,这也是得益于二叉树这种结构的天然优势,自我感觉就完全是为这种算法题设计的,天造地设嘛!
#03、算法实现
       0x001、数据结构的定义:

 #define Maxsize 100
//定义数据元素类型
typedef char elemtype;
//定义二叉树数据变量
typedef union
{
char Operator;
double date;
}perdate;
//定义二叉树链式存储结构
typedef struct node
{
perdate DATE;//用union类型存运算符或操作数
struct node *lchild;
struct node *rchild;
}btnode;    


 struct op
{
char opration;
int index;//括号层数//当这个index被标记为-1时,就不会再次被查找到
int locate;//op的位置
};

用union定义一个perdate类型,用来分别记录操作数和运算符。op是查找运算符时用,从后往前查找,括号级数最低的作为根节点来创建二叉树。

0x002、实现的函数

 
//查找op,并填充Aop数组
int Sortop(char str[], op Aop[], int &index);
//将字符串转化为浮点数
double str_to_flaot(char strpoly[], int p,int q);
//判断数组是不是1.2类型,就是只有数据
bool isdate(char str[],int p,int q);//p,q指向str的开始和结尾处
//判断str是否为运算符和括号
bool isoprater(char str[],int p,int q);//p,q指向str的开始和结尾处
//用算数表达式创建二叉树
void Createbtnode(btnode *b, char *str, int p, int q,int tail);//p,q指向str的开始和结尾处;tail是Aop的尾指针
//计算二叉树算式的结果
double Comp(btnode *b);

0x003、main函数,整个算法过程简述

#include"标头.h"
int index = ;//记录最大的括号层数
struct op Aop[Maxsize];
 int main()
{
btnode * b;
b = new btnode;
char str[Maxsize];
cout << "算式计算器[张安源]" << endl;
while(true)
{
cout << "[Type \"exit\" to exit]" << endl << "请输入你要求的表达式:" << endl;
cin.getline(str, Maxsize);
if (strcmp("exit", str) == ) break;//如果输入的是exit则退出
else
{
int tail = Sortop(str, Aop, index);//整理得到Aop的结构数组
Createbtnode(b, str, , strlen(str) - , tail);
double result = Comp(b);
cout << result << endl;
}
}
}

一直循环,让用户输入一个表达式,当输入为exit时,退出循环。Sortop函数将表达式的操作符的括号层数和其在表达式的位置经行记录到Aop数组里面,返回值是最大的括号层数。然后由Createbtnode函数创建一个二叉树b。comp求出二叉树表达式的结构,然后输出结果。大致的过程是这样,但是里面却还包含了一些实现的细节,具体代码是怎么实现的就不啰嗦了,看代码比讲解跟方便。

0x004、整个project。

<1>Header.h

 #pragma once
#include<iostream>
using namespace std;
#define Maxsize 100
//定义数据元素类型
//*********int check = 0;//作为判断表达式是否正确的标记
typedef char elemtype;
//定义二叉树数据变量
typedef union
{
char Operator;
double date;
}perdate;
//定义二叉树链式存储结构
typedef struct node
{
perdate DATE;//用union类型存运算符或操作数
struct node *lchild;
struct node *rchild;
}btnode;
//定义查找运算符的结构数组
struct op
{
char opration;
int index;//括号层数//当这个index被标记为-1时,就不会再次被查找到
int locate;//op的位置
};
extern int index;
extern struct op Aop[Maxsize];
//******************************************************
//查找op,并填充Aop数组
int Sortop(char str[], op Aop[], int &index);
//将字符串转化为浮点数
double str_to_flaot(char strpoly[], int p,int q);
//判断数组是不是1.2类型,就是只有数据
bool isdate(char str[],int p,int q);//p,q指向str的开始和结尾处
//判断str是否为运算符和括号
bool isoprater(char str[],int p,int q);//p,q指向str的开始和结尾处
//用算数表达式创建二叉树
void Createbtnode(btnode *b, char *str, int p, int q,int tail);//p,q指向str的开始和结尾处;tail是Aop的尾指针
//计算二叉树算式的结果
double Comp(btnode *b);

<2>op.cpp

 #include"标头.h"
//查找op,并填充Aop数组
int Sortop(char str[], op Aop[], int &index)
{
int j = ;//记录Aop的top
int i;
int ind = ;//记录括号层数
for (i = ; str[i] != '\0'; i++)
{
if (str[i] == '(')
ind++;
else if (str[i] == ')')
ind--;
else if (str[i] == '+' || str[i] == '-' || str[i] == '*'||str[i]=='/' || str[i] == '^')
{
Aop[j].opration = str[i];
Aop[j].index = ind;
Aop[j].locate = i;
j++;
}
index = (index > ind) ? index : ind;
}
return j;
}
//将字符串转化为浮点数
double str_to_flaot(char strpoly[], int p,int q)
{
if (strpoly[p] == '(')
p++;
if (strpoly[q] == ')')
q--;
//判断小数点前有几位数字
int index = ;
int temp = p;//保存原来的p值
double n = ;//最后的浮点数
for (;( p <= q)&&(strpoly[p]!='.'); p++) index++;
p = temp;
for (; p<=q; p++)
{
if (strpoly[p] == '.') continue;
index--;
n = n + ((double)(strpoly[p] - ''))*(pow(, index)); }
return n;
}
//判断数组是不是1.2类型,就是只有数据//忽略括号
bool isdate(char str[],int p,int q)
{
int i;
int index = ;
for (i = p; i<=q; i++)
{
if (str[i] == '.')
index++;
if (str[i] == '+' || str[i] == '-' || str[i] == '*' ||str[i]=='/' || str[i] == '^')
return false;
}
if (index== || index == )
{
return true;
}
else
abort();
}
//判断str是否为运算符和括号
bool isoprater(char str[],int p,int q)
{
if ((p==q)&&(str[p] == '(' || str[p] == ')' || str[p] == '*'||str[p]=='/' || str[p] == '^' || str[p] == '+' || str[p] == '-'))
return true;
else
return false;
}
//用算数表达式创建二叉树
void Createbtnode(btnode *b, char *str, int p, int q,int tail) //由str串创建二叉链
{ //p,q分别标志Aop的首尾
int i = ;
int j = ;//
int find=;
if (isdate(str,p,q))//str为1.3类型
{
//创建头节点,并将数据位置为str_to_double
b->DATE.date = str_to_flaot(str,p,q);
b->lchild = NULL;
b->rchild = NULL;
}
else if (isoprater(str,p,q))//str为+、—、^、(、)、*
{
abort();
b->DATE.Operator = str[i];
b->lchild = NULL;
b->rchild = NULL;
}
///***************************************************************
else
for (int temp = ; temp <= index; temp++)
{
for (j = tail; j >=; j--)//从后往前找,才符合运算的法则,前面先算后面后算
{
if (Aop[j].index == temp && ((Aop[j].opration == '+')||(Aop[j].opration == '-')) && Aop[j].locate >= p&&Aop[j].locate <= q)
{
find++;
Aop[j].index = -;//标志这个已经被找过了
btnode *lt, *rt;
lt = new btnode;
rt = new btnode;
b->lchild = lt;
b->rchild = rt;
b->DATE.Operator = Aop[j].opration;
Createbtnode(b->lchild, str, p, Aop[j].locate - ,tail);
Createbtnode(b->rchild, str, Aop[j].locate+, q,tail);
}
}
if(find==)
for (j = tail; j >=; j--)
{
if (Aop[j].index == temp && ((Aop[j].opration == '*')||(Aop[j].opration=='/')) && Aop[j].locate >= p&&Aop[j].locate <= q)
{
find++;
Aop[j].index = -;//标志这个已经被找过了
btnode *lt, *rt;
lt = new btnode;
rt = new btnode;
b->lchild = lt;
b->rchild = rt;
b->DATE.Operator = Aop[j].opration;
Createbtnode(b->lchild, str, p, Aop[j].locate - ,tail);
Createbtnode(b->rchild, str, Aop[j].locate+, q,tail);
}
}
if(find==)
for (j = tail; j >=; j--)
{
if (Aop[j].index == temp && (Aop[j].opration == '^') && Aop[j].locate >= p&&Aop[j].locate <= q)
{
Aop[j].index = -;//标志这个已经被找过了
btnode *lt, *rt;
lt = new btnode;
rt = new btnode;
b->lchild = lt;
b->rchild = rt;
b->DATE.Operator = Aop[j].opration;
Createbtnode(b->lchild, str, p, Aop[j].locate - ,tail);
Createbtnode(b->rchild, str, Aop[j].locate+, q,tail);
}
}
}
}
//计算二叉树算式的结果
double Comp(btnode *b)
{
double v1, v2;
if (b == NULL) return ;
if (b->lchild == NULL && b->rchild == NULL)
return b->DATE.date; //叶子节点直接返回节点值
v1 = Comp(b->lchild);
v2 = Comp(b->rchild);
switch (b->DATE.Operator)
{
case '+':
return v1 + v2;
case '-':
return v1 - v2;
case '*':
return v1*v2;
case '/':
if (v2 != )
return v1 / v2;
else
abort();
case '^':
return (pow(v1, v2));
default:
abort();
}
}

<3>main.cpp

 #include"标头.h"
int index = ;//记录最大的括号层数
struct op Aop[Maxsize];
int main()
{
btnode * b;
b = new btnode;
char str[Maxsize];
cout << "算式计算器[张安源]" << endl;
while(true)
{
cout << "[Type \"exit\" to exit]" << endl << "请输入你要求的表达式:" << endl;
cin.getline(str, Maxsize);
if (strcmp("exit", str) == ) break;//如果输入的是exit则退出
else
{
int tail = Sortop(str, Aop, index);//整理得到Aop的结构数组
Createbtnode(b, str, , strlen(str) - , tail);
double result = Comp(b);
cout << result << endl;
}
}
}

#04算法测试

当输入的表达式符合规则时,返回表达式的值。

当输入的表达式不符合规则时,则调用abort函数。

#05、总结

好的数据结构能事半功倍,要培养善于发现的思维,当有某个思路然后去实现它,另外要积累经验。好好理解数据结构!

巧妙地用二叉树完成算式计算算法<计算器,二叉树,C++,独辟蹊径>的更多相关文章

  1. 二叉树 ADT接口 遍历算法 常规运算

    BTree.h   (结构定义, 基本操作, 遍历) #define MS 10 typedef struct BTreeNode{ char data; struct BTreeNode * lef ...

  2. Scalaz(31)- Free :自由数据结构-算式和算法的关注分离

    我们可以通过自由数据结构(Free Structure)实现对程序的算式和算法分离关注(separation of concern).算式(Abstract Syntax Tree, AST)即运算表 ...

  3. JavaScript--数据结构与算法之二叉树

    树是一种非线性的数据结构,以分层的方式存储数据. 二叉树:查找非常快,而且二叉树添加或者删除元素也非常快. 形象的可以描述为组织结构图,用来描述一个组织的结构.树是由边连接的点组成.树的一些基本概念: ...

  4. php求二叉树的深度(1、二叉树就可以递归,因为结构和子结构太相似)(2、谋而后动,算法想清楚,很好过的)

    php求二叉树的深度(1.二叉树就可以递归,因为结构和子结构太相似)(2.谋而后动,算法想清楚,很好过的) 一.总结 1.二叉树就可以递归,因为结构和子结构太相似 2.谋而后动,算法想清楚,很好过的 ...

  5. 两通道实信号使用一个FFT同时计算算法

    前言 在工程的实际应用场景中,往往是需要最省资源量.而DSP资源和BRAM资源对FPGA来说弥足珍贵. 对于同时存在多个通道的实信号需要做FFT而言,常规做法是每个通道用一个FFT IP,FFT IP ...

  6. 算法:二叉树的层次遍历(递归实现+非递归实现,lua)

    二叉树知识参考:深入学习二叉树(一) 二叉树基础 递归实现层次遍历算法参考:[面经]用递归方法对二叉树进行层次遍历 && 二叉树深度 上面第一篇基础写得不错,不了解二叉树的值得一看. ...

  7. 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)

    前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...

  8. [PHP] 算法-镜像二叉树的PHP实现

    操作给定的二叉树,将其变换为源二叉树的镜像. 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ 11 9 7 5 思 ...

  9. 编程熊讲解LeetCode算法《二叉树》

    大家好,我是编程熊. 往期我们一起学习了<线性表>相关知识. 本期我们一起学习二叉树,二叉树的问题,大多以递归为基础,根据题目的要求,在递归过程中记录关键信息,进而解决问题. 如果还未学习 ...

随机推荐

  1. MySQL 第九天(核心优化三)

    一.昨天内容回顾 索引设计依据 与数据表有关系的sql语句都统计出来 where order by or等等条件的字段适当做索引 原则: 频率高的sql语句 执行时间长的sql语句 业务逻辑重要的sq ...

  2. <读书笔记>软件调试之道 :问题的核心-修复后的反思

    声明:本文档的内容主要来源于书籍<软件调试修炼之道>作者Paul Butcher,属于读书笔记.欢迎转载! ---------------------------------------- ...

  3. 实时消息平台NSQ的特性

    NSQ是GO语言开发的可用于大规模系统中的实时消息服务,但是和RabbitMQ等相比,它具有什么特色,什么场景下选择NSQ呢? NSQ的自身特色很明显,最主要的优势在如下三个方面: 1,性能.在多个著 ...

  4. 8天掌握EF的Code First开发系列之5 视图、存储过程和异步API

    本文出自8天掌握EF的Code First开发系列,经过自己的实践整理出来. 本篇目录 视图View 存储过程 异步API 本章小结 咱们接着上一篇继续深入学习,这一篇说说Entity Framewo ...

  5. Spring基本框架

    1.Spring基本框架的概念 Spring 框架是一个分层架构,由 7 个定义良好的模块组成.Spring模块构建在核心容器之上,核心容器定义创建.配置和管理bean的方式.组成Spring框架的每 ...

  6. web测试常用的用例及知识

      1.      Web测试中关于登录的测试... 1 2.      搜索功能测试用例设计... 2 3.      翻页功能测试用例... 3 4.      输入框的测试... 5 5.    ...

  7. Handlebars模板库浅析

    Handlebars模板库简单介绍 Handlebars是JavaScript一个语义模板库,通过对view(模板)和data(ajax请求的数据,一般是json)的分离来快速构建Web模板.它采用& ...

  8. nagios二次开发(六)---nagiosql原理及主要文件的介绍

    nagiosql的入口文件:index.php,这也是所有php程序的入口文件.是由apache指定的. index.php 文件的开始引入了 require("functions/prep ...

  9. C# 串口操作系列(3) -- 协议篇,二进制协议数据解析

    原文地址:http://blog.csdn.net/wuyazhe/article/details/5627253 我们的串口程序,除了通用的,进行串口监听收发的简单工具,大多都和下位机有关,这就需要 ...

  10. vim 标记 mark 详解 (转载)

    http://www.cnblogs.com/jianyungsun/archive/2011/02/14/1954057.html Vim 允许你在文本中放置自定义的标记.命令 "ma&q ...