巧妙地用二叉树完成算式计算算法<计算器,二叉树,C++,独辟蹊径>

来源:互联网 发布:java sys refcursor 编辑:程序博客网 时间:2024/05/16 19:42

01、引言

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

我们构造这样一个二叉树

当构造这样一个二叉树之后,解决表达式的值的方法,也就浮出水面了,把2和3相乘,存到*的节点中,然后再和1相加,存到+的节点中.....最后根节点-节点中存放的就是最后的计算结果。就是叶子节点执行其双亲节点的运算,结果存到双亲节点中。

02、选二叉树作为算法的存储结构有什么好处。

       这主要有两个方面的好处,这也是针对于栈算法的两个麻烦的地方。
  1. 免除了算式表达式的检查过程。为什么能免除检查,表达式的规范性呢?并不是不需要检查,而是检查的过程就包含在创建二叉树的过程。认真分析这棵二叉树,我们会发现,所有的叶子节点必须是操作数节点,而所有的非叶子节点必须是运算符节点,否则表达式的结构一点不正确,创建二叉树的过程就可以对表达式经行检查。表达式是否正确也只取决于两个方面,第一、表达式的结构是否正确,比如不能出现2*+6这样的表达式,第二、表达式的数据是否正确,例如不能出现1+2.2.3这样的表达式,2.2.3不是一个符合规则的数据。而数据的检查,也可以在给叶子节点赋值的时候检查。所以避免的单独经行表达式检查的繁琐。
  2. 不需要转化为后缀表达式再经行表达式结果的计算,这也是得益于二叉树这种结构的天然优势,自我感觉就完全是为这种算法题设计的,天造地设嘛!

03、算法实现

       0x001、数据结构的定义:

 1 #define Maxsize 100 2 //定义数据元素类型 3 typedef char  elemtype; 4 //定义二叉树数据变量 5 typedef union  6 { 7     char Operator; 8     double date; 9 }perdate;10 //定义二叉树链式存储结构11 typedef struct node12 {13     perdate DATE;//用union类型存运算符或操作数14     struct node *lchild;15     struct node *rchild;16 }btnode; 
 
1 struct op 2 {3     char opration;4     int index;//括号层数//当这个index被标记为-1时,就不会再次被查找到5     int locate;//op的位置6 };

用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 = 0;//记录最大的括号层数struct op Aop[Maxsize];

 1 int main() 2 { 3     btnode * b; 4     b = new btnode; 5     char str[Maxsize]; 6     cout << "算式计算器[张安源]" << endl; 7         while(true)    8         { 9             cout << "[Type \"exit\" to exit]" << endl << "请输入你要求的表达式:" << endl;10            cin.getline(str, Maxsize);11           if (strcmp("exit", str) == 0)  break;//如果输入的是exit则退出12           else13           {14               int tail = Sortop(str, Aop, index);//整理得到Aop的结构数组15               Createbtnode(b, str, 0, strlen(str) - 1, tail);16               double result = Comp(b);17               cout << result << endl;18           }19         }20 }

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

      0x004、整个project。

Header.h

1 #pragma once2 #include<iostream>3 using namespace std;4 #define Maxsize 1005 //定义数据元素类型6 //*********int check = 0;//作为判断表达式是否正确的标记7 typedef char elemtype;8 //定义二叉树数据变量9 typedef union10 {11 char Operator;12 double date;13 }perdate;14 //定义二叉树链式存储结构15 typedef struct node16 {17 perdate DATE;//用union类型存运算符或操作数18 struct node *lchild;19 struct node *rchild;20 }btnode;21 //定义查找运算符的结构数组22 struct op23 {24 char opration;25 int index;//括号层数//当这个index被标记为-1时,就不会再次被查找到26 int locate;//op的位置27 };28 extern int index;29 extern struct op Aop[Maxsize];30 //******************************************************31 //查找op,并填充Aop数组32 int Sortop(char str[], op Aop[], int &index);33 //将字符串转化为浮点数34 double str_to_flaot(char strpoly[], int p,int q);35 //判断数组是不是1.2类型,就是只有数据36 bool isdate(char str[],int p,int q);//p,q指向str的开始和结尾处37 //判断str是否为运算符和括号38 bool isoprater(char str[],int p,int q);//p,q指向str的开始和结尾处39 //用算数表达式创建二叉树40 void Createbtnode(btnode *b, char *str, int p, int q,int tail);//p,q指向str的开始和结尾处;tail是Aop的尾指针41 //计算二叉树算式的结果42 double Comp(btnode *b);


op.cpp

  1 #include"标头.h"  2 //查找op,并填充Aop数组  3 int Sortop(char str[], op Aop[], int &index)  4 {  5     int j = 0;//记录Aop的top  6     int i;  7     int ind = 0;//记录括号层数  8     for (i = 0; str[i] != '\0'; i++)  9     { 10         if (str[i] == '(') 11             ind++; 12         else if (str[i] == ')') 13             ind--; 14         else if (str[i] == '+' || str[i] == '-' || str[i] == '*'||str[i]=='/' || str[i] == '^') 15         { 16             Aop[j].opration = str[i]; 17             Aop[j].index = ind; 18             Aop[j].locate = i; 19             j++; 20         } 21         index = (index > ind) ? index : ind; 22     } 23     return j; 24 } 25 //将字符串转化为浮点数 26 double str_to_flaot(char strpoly[], int p,int q) 27 { 28     if (strpoly[p] == '(') 29         p++; 30     if (strpoly[q] == ')') 31         q--; 32     //判断小数点前有几位数字 33     int index = 0; 34     int temp = p;//保存原来的p值 35     double  n = 0;//最后的浮点数 36     for (;( p <= q)&&(strpoly[p]!='.'); p++) index++; 37     p = temp; 38     for (; p<=q; p++) 39     { 40         if (strpoly[p] == '.')  continue; 41         index--; 42         n = n + ((double)(strpoly[p] - '0'))*(pow(10, index)); 43  44     } 45         return n; 46 } 47 //判断数组是不是1.2类型,就是只有数据//忽略括号 48 bool isdate(char str[],int p,int q) 49 { 50     int i; 51     int index = 0; 52     for (i = p; i<=q; i++) 53     { 54         if (str[i] == '.') 55             index++; 56         if (str[i] == '+' || str[i] == '-' || str[i] == '*' ||str[i]=='/' || str[i] == '^') 57             return false; 58     } 59     if (index== 0 || index == 1) 60     { 61         return true; 62     } 63     else 64         abort(); 65 } 66 //判断str是否为运算符和括号 67 bool isoprater(char str[],int p,int q) 68 { 69     if ((p==q)&&(str[p] == '(' || str[p] == ')' || str[p] == '*'||str[p]=='/' || str[p] == '^' || str[p] == '+' || str[p] == '-')) 70         return true; 71     else 72         return false; 73 } 74 //用算数表达式创建二叉树 75 void Createbtnode(btnode *b, char *str, int p, int q,int tail)        //由str串创建二叉链 76 {                                                            //p,q分别标志Aop的首尾 77     int i = 0; 78     int j = 0;// 79     int find=0; 80     if (isdate(str,p,q))//str为1.3类型 81     { 82         //创建头节点,并将数据位置为str_to_double 83         b->DATE.date = str_to_flaot(str,p,q); 84         b->lchild = NULL; 85         b->rchild = NULL; 86     } 87     else if (isoprater(str,p,q))//str为+、—、^、(、)、* 88     { 89         abort(); 90         b->DATE.Operator = str[i]; 91         b->lchild = NULL; 92         b->rchild = NULL; 93     } 94     ///*************************************************************** 95     else 96         for (int temp = 0; temp <= index; temp++) 97         { 98             for (j = tail; j >=0; j--)//从后往前找,才符合运算的法则,前面先算后面后算 99             {100                 if (Aop[j].index == temp && ((Aop[j].opration == '+')||(Aop[j].opration == '-')) && Aop[j].locate >= p&&Aop[j].locate <= q)101                 {102                     find++;103                     Aop[j].index = -1;//标志这个已经被找过了104                     btnode *lt, *rt;105                     lt = new btnode;106                     rt = new btnode;107                     b->lchild = lt;108                     b->rchild = rt;109                     b->DATE.Operator = Aop[j].opration;110                     Createbtnode(b->lchild, str, p, Aop[j].locate - 1,tail);111                     Createbtnode(b->rchild, str, Aop[j].locate+1, q,tail);112                 }113             }114         if(find==0)115             for (j = tail; j >=0; j--)116             {117                 if (Aop[j].index == temp && ((Aop[j].opration == '*')||(Aop[j].opration=='/')) && Aop[j].locate >= p&&Aop[j].locate <= q)118                 {119                     find++;120                     Aop[j].index = -1;//标志这个已经被找过了121                     btnode *lt, *rt;122                     lt = new btnode;123                     rt = new btnode;124                     b->lchild = lt;125                     b->rchild = rt;126                     b->DATE.Operator = Aop[j].opration;127                     Createbtnode(b->lchild, str, p, Aop[j].locate - 1,tail);128                     Createbtnode(b->rchild, str, Aop[j].locate+1, q,tail);129                 }130             }131         if(find==0)132             for (j = tail; j >=0; j--)133             {134                 if (Aop[j].index == temp && (Aop[j].opration == '^') && Aop[j].locate >= p&&Aop[j].locate <= q)135                 {136                     Aop[j].index = -1;//标志这个已经被找过了137                     btnode *lt, *rt;138                     lt = new btnode;139                     rt = new btnode;140                     b->lchild = lt;141                     b->rchild = rt;142                     b->DATE.Operator = Aop[j].opration;143                     Createbtnode(b->lchild, str, p, Aop[j].locate - 1,tail);144                     Createbtnode(b->rchild, str, Aop[j].locate+1, q,tail);145                 }146             }147         }148 }149 //计算二叉树算式的结果150 double Comp(btnode *b)151 {152     double v1, v2;153     if (b == NULL) return 0;154     if (b->lchild == NULL && b->rchild == NULL)155         return b->DATE.date;    //叶子节点直接返回节点值156     v1 = Comp(b->lchild);157     v2 = Comp(b->rchild);158     switch (b->DATE.Operator)159     {160     case '+':161         return v1 + v2;162     case '-':163         return v1 - v2;164     case '*':165         return v1*v2;166     case '/':167         if (v2 != 0)168             return v1 / v2;169         else170             abort();171     case '^':172         return (pow(v1, v2));173     default:174         abort();175     }176 }

main.cpp

 1 #include"标头.h" 2 int index = 0;//记录最大的括号层数 3 struct op Aop[Maxsize]; 4 int main() 5 { 6     btnode * b; 7     b = new btnode; 8     char str[Maxsize]; 9     cout << "算式计算器[张安源]" << endl;10         while(true)   11         {12             cout << "[Type \"exit\" to exit]" << endl << "请输入你要求的表达式:" << endl;13            cin.getline(str, Maxsize);14           if (strcmp("exit", str) == 0)  break;//如果输入的是exit则退出15           else16           {17               int tail = Sortop(str, Aop, index);//整理得到Aop的结构数组18               Createbtnode(b, str, 0, strlen(str) - 1, tail);19               double result = Comp(b);20               cout << result << endl;21           }22         }23 }


04算法测试

                   

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

                 

                

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

05、总结

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


op.
cp
p

0 0