巧妙地用二叉树完成算式计算算法<计算器,二叉树,C++,独辟蹊径>
#01、引言,我们知道算式计算的问题是栈里面一个非常经典的题目。但是用栈来实现是一个非常麻烦的过程,第一要解决算式判断,是否为符合规则的算式,第二要由中最表达式转化为后缀表达式。这两个部分是栈实现计算算式表达式的比较复杂的地方。不仅如此,栈实现里面的各种运算符的优先级,各种条件判断,可以说是麻烦的要命。但是,实际上有一种数据结构比栈更适合解决这类问题。可以说是得天独厚的优势。对,就是二叉树。例如一个表达式:1+2*3-4/5
我们构造这样一个二叉树
#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++,独辟蹊径>的更多相关文章
- 二叉树 ADT接口 遍历算法 常规运算
BTree.h (结构定义, 基本操作, 遍历) #define MS 10 typedef struct BTreeNode{ char data; struct BTreeNode * lef ...
- Scalaz(31)- Free :自由数据结构-算式和算法的关注分离
我们可以通过自由数据结构(Free Structure)实现对程序的算式和算法分离关注(separation of concern).算式(Abstract Syntax Tree, AST)即运算表 ...
- JavaScript--数据结构与算法之二叉树
树是一种非线性的数据结构,以分层的方式存储数据. 二叉树:查找非常快,而且二叉树添加或者删除元素也非常快. 形象的可以描述为组织结构图,用来描述一个组织的结构.树是由边连接的点组成.树的一些基本概念: ...
- php求二叉树的深度(1、二叉树就可以递归,因为结构和子结构太相似)(2、谋而后动,算法想清楚,很好过的)
php求二叉树的深度(1.二叉树就可以递归,因为结构和子结构太相似)(2.谋而后动,算法想清楚,很好过的) 一.总结 1.二叉树就可以递归,因为结构和子结构太相似 2.谋而后动,算法想清楚,很好过的 ...
- 两通道实信号使用一个FFT同时计算算法
前言 在工程的实际应用场景中,往往是需要最省资源量.而DSP资源和BRAM资源对FPGA来说弥足珍贵. 对于同时存在多个通道的实信号需要做FFT而言,常规做法是每个通道用一个FFT IP,FFT IP ...
- 算法:二叉树的层次遍历(递归实现+非递归实现,lua)
二叉树知识参考:深入学习二叉树(一) 二叉树基础 递归实现层次遍历算法参考:[面经]用递归方法对二叉树进行层次遍历 && 二叉树深度 上面第一篇基础写得不错,不了解二叉树的值得一看. ...
- 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)
前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...
- [PHP] 算法-镜像二叉树的PHP实现
操作给定的二叉树,将其变换为源二叉树的镜像. 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ 11 9 7 5 思 ...
- 编程熊讲解LeetCode算法《二叉树》
大家好,我是编程熊. 往期我们一起学习了<线性表>相关知识. 本期我们一起学习二叉树,二叉树的问题,大多以递归为基础,根据题目的要求,在递归过程中记录关键信息,进而解决问题. 如果还未学习 ...
随机推荐
- MySQL5.6安装步骤
MySQL5.6安装步骤(windows7/8_64位) 1. 下载MySQL Community Server 5.6.11 2. 解压MySQL压缩包 将以下载的MySQL压缩包解压到自定义目录下 ...
- over partition by与group by 的区别
(本文摘自scottpei的博客) over partition by与group by 的区别 今天看到一个老兄的问题, 大概如下: 查询出部门的最低工资的userid 号 表结构: D号 ...
- Linux内核分析——汇编代码执行及堆栈变化
张潇月<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验步骤 首先借助实验楼这个平台进入Linux ...
- CocoaPods安装及使用《转》
http://www.cnblogs.com/eagley/p/5407721.html
- Python自动化 【第十七篇】:jQuery介绍
jQuery jQuery是一个兼容多浏览器的javascript库,核心理念是write less,do more(写得更少,做得更多),对javascript进行了封装,是的更加便捷的开发,并且在 ...
- 摘录ECMAScript官方文档中重要的两段话
Every object created by a constructor has an implicit reference (called the object’s prototype) to t ...
- 删除win7远程桌面历史记录
开始-运行-“regedit”注册表中找到HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default 删除不需要的即可
- sqlserver创建,调用 带返回值存取过程
<1>create: ALTER proc [dbo].[common_proc] @sql1 varchar(5000), @sql2 varchar(5000) OUTPUT as ...
- Linux下JDK、Tomcat
1.JDK的安装 1. 下载JDK 先查看Linux系统是多少位(32位/64位):getconf LONG_BIT.再从JDK官网(http://www.oracle.com/technetw ...
- Appium学习路—Android定位元素与操作
一.常用识别元素的工具 uiautomator:Android SDK自带的一个工具,在tools目录下 monitor:Android SDK自带的一个工具,在tools目录下 Appium Ins ...